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         Roo.log('ADDXTYPE');
393         cn = Roo.factory(tree);
394         //Roo.log(['addxtype', cn]);
395            
396         cn.parentType = this.xtype; //??
397         cn.parentId = this.id;
398
399         Roo.log(cn.el);
400         
401         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
402         if (typeof(cn.container_method) == 'string') {
403             cntr = cn.container_method;
404         }
405         
406         
407         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
408         
409         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
410         
411         var build_from_html =  Roo.XComponent.build_from_html;
412           
413         var is_body  = (tree.xtype == 'Body') ;
414           
415         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
416           
417         var self_cntr_el = Roo.get(this[cntr](false));
418         
419         // do not try and build conditional elements 
420         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
421             return false;
422         }
423         
424         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
425             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
426                 return this.addxtypeChild(tree,cntr, is_body);
427             }
428             
429             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
430                 
431             if(echild){
432                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
433             }
434             
435             Roo.log('skipping render');
436             return cn;
437             
438         }
439         
440         var ret = false;
441         if (!build_from_html) {
442             return false;
443         }
444         
445         // this i think handles overlaying multiple children of the same type
446         // with the sam eelement.. - which might be buggy..
447         while (true) {
448             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
449             
450             if (!echild) {
451                 break;
452             }
453             
454             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
455                 break;
456             }
457             
458             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459         }
460        
461         return ret;
462     },
463     
464     
465     addxtypeChild : function (tree, cntr, is_body)
466     {
467         Roo.debug && Roo.log('addxtypeChild:' + cntr);
468         Roo.log('ADDXTYPECHILD');
469         var cn = this;
470         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
471         
472         
473         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
474                     (typeof(tree['flexy:foreach']) != 'undefined');
475           
476     
477         
478         skip_children = false;
479         // render the element if it's not BODY.
480         if (!is_body) {
481             
482             // if parent was disabled, then do not try and create the children..
483             if(!this[cntr](true)){
484                 tree.items = [];
485                 return tree;
486             }
487            
488             cn = Roo.factory(tree);
489             Roo.log(cn.el);
490            
491             cn.parentType = this.xtype; //??
492             cn.parentId = this.id;
493             
494             var build_from_html =  Roo.XComponent.build_from_html;
495             
496             
497             // does the container contain child eleemnts with 'xtype' attributes.
498             // that match this xtype..
499             // note - when we render we create these as well..
500             // so we should check to see if body has xtype set.
501             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
502                
503                 var self_cntr_el = Roo.get(this[cntr](false));
504                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
505                 if (echild) { 
506                     //Roo.log(Roo.XComponent.build_from_html);
507                     //Roo.log("got echild:");
508                     //Roo.log(echild);
509                 }
510                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
511                 // and are not displayed -this causes this to use up the wrong element when matching.
512                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
513                 
514                 
515                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
516                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
517                   
518                   
519                   
520                     cn.el = echild;
521                     Roo.log(cn.el);
522                   //  Roo.log("GOT");
523                     //echild.dom.removeAttribute('xtype');
524                 } else {
525                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
526                     Roo.debug && Roo.log(self_cntr_el);
527                     Roo.debug && Roo.log(echild);
528                     Roo.debug && Roo.log(cn);
529                 }
530             }
531            
532             
533            
534             // if object has flexy:if - then it may or may not be rendered.
535             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
536                 // skip a flexy if element.
537                 Roo.debug && Roo.log('skipping render');
538                 Roo.debug && Roo.log(tree);
539                 if (!cn.el) {
540                     Roo.debug && Roo.log('skipping all children');
541                     skip_children = true;
542                 }
543                 
544              } else {
545                  
546                 // actually if flexy:foreach is found, we really want to create 
547                 // multiple copies here...
548                 //Roo.log('render');
549                 //Roo.log(this[cntr]());
550                 // some elements do not have render methods.. like the layouts...
551                 /*
552                 if(this[cntr](true) === false){
553                     cn.items = [];
554                     return cn;
555                 }
556                 */
557                 cn.render && cn.render(this[cntr](true));
558                 
559              }
560             // then add the element..
561         }
562          
563         // handle the kids..
564         
565         var nitems = [];
566         /*
567         if (typeof (tree.menu) != 'undefined') {
568             tree.menu.parentType = cn.xtype;
569             tree.menu.triggerEl = cn.el;
570             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
571             
572         }
573         */
574         if (!tree.items || !tree.items.length) {
575             cn.items = nitems;
576             //Roo.log(["no children", this]);
577             
578             return cn;
579         }
580          
581         var items = tree.items;
582         delete tree.items;
583         
584         //Roo.log(items.length);
585             // add the items..
586         if (!skip_children) {    
587             for(var i =0;i < items.length;i++) {
588               //  Roo.log(['add child', items[i]]);
589                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
590             }
591         }
592         
593         cn.items = nitems;
594         
595         //Roo.log("fire childrenrendered");
596         
597         cn.fireEvent('childrenrendered', this);
598         
599         return cn;
600     },
601     
602     /**
603      * Set the element that will be used to show or hide
604      */
605     setVisibilityEl : function(el)
606     {
607         this.visibilityEl = el;
608     },
609     
610      /**
611      * Get the element that will be used to show or hide
612      */
613     getVisibilityEl : function()
614     {
615         if (typeof(this.visibilityEl) == 'object') {
616             return this.visibilityEl;
617         }
618         
619         if (typeof(this.visibilityEl) == 'string') {
620             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
621         }
622         
623         return this.getEl();
624     },
625     
626     /**
627      * Show a component - removes 'hidden' class
628      */
629     show : function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634          
635         this.getVisibilityEl().removeClass(['hidden','d-none']);
636         
637         this.fireEvent('show', this);
638         
639         
640     },
641     /**
642      * Hide a component - adds 'hidden' class
643      */
644     hide: function()
645     {
646         if(!this.getVisibilityEl()){
647             return;
648         }
649         
650         this.getVisibilityEl().addClass(['hidden','d-none']);
651         
652         this.fireEvent('hide', this);
653         
654     }
655 });
656
657  /*
658  * - LGPL
659  *
660  * element
661  * 
662  */
663
664 /**
665  * @class Roo.bootstrap.Element
666  * @extends Roo.bootstrap.Component
667  * @children Roo.bootstrap.Component
668  * Bootstrap Element class (basically a DIV used to make random stuff )
669  * 
670  * @cfg {String} html contents of the element
671  * @cfg {String} tag tag of the element
672  * @cfg {String} cls class of the element
673  * @cfg {Boolean} preventDefault (true|false) default false
674  * @cfg {Boolean} clickable (true|false) default false
675  * @cfg {String} role default blank - set to button to force cursor pointer
676  
677  * 
678  * @constructor
679  * Create a new Element
680  * @param {Object} config The config object
681  */
682
683 Roo.bootstrap.Element = function(config){
684     Roo.bootstrap.Element.superclass.constructor.call(this, config);
685     
686     this.addEvents({
687         // raw events
688         /**
689          * @event click
690          * When a element is chick
691          * @param {Roo.bootstrap.Element} this
692          * @param {Roo.EventObject} e
693          */
694         "click" : true 
695         
696       
697     });
698 };
699
700 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
701     
702     tag: 'div',
703     cls: '',
704     html: '',
705     preventDefault: false, 
706     clickable: false,
707     tapedTwice : false,
708     role : false,
709     
710     getAutoCreate : function(){
711         
712         var cfg = {
713             tag: this.tag,
714             // cls: this.cls, double assign in parent class Component.js :: onRender
715             html: this.html
716         };
717         if (this.role !== false) {
718             cfg.role = this.role;
719         }
720         
721         return cfg;
722     },
723     
724     initEvents: function() 
725     {
726         Roo.bootstrap.Element.superclass.initEvents.call(this);
727         
728         if(this.clickable){
729             this.el.on('click', this.onClick, this);
730         }
731         
732         
733     },
734     
735     onClick : function(e)
736     {
737         if(this.preventDefault){
738             e.preventDefault();
739         }
740         
741         this.fireEvent('click', this, e); // why was this double click before?
742     },
743     
744     
745     
746
747     
748     
749     getValue : function()
750     {
751         return this.el.dom.innerHTML;
752     },
753     
754     setValue : function(value)
755     {
756         this.el.dom.innerHTML = value;
757     }
758    
759 });
760
761  
762
763  /*
764  * - LGPL
765  *
766  * dropable area
767  * 
768  */
769
770 /**
771  * @class Roo.bootstrap.DropTarget
772  * @extends Roo.bootstrap.Element
773  * Bootstrap DropTarget class
774  
775  * @cfg {string} name dropable name
776  * 
777  * @constructor
778  * Create a new Dropable Area
779  * @param {Object} config The config object
780  */
781
782 Roo.bootstrap.DropTarget = function(config){
783     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
784     
785     this.addEvents({
786         // raw events
787         /**
788          * @event click
789          * When a element is chick
790          * @param {Roo.bootstrap.Element} this
791          * @param {Roo.EventObject} e
792          */
793         "drop" : true
794     });
795 };
796
797 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
798     
799     
800     getAutoCreate : function(){
801         
802          
803     },
804     
805     initEvents: function() 
806     {
807         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
808         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
809             ddGroup: this.name,
810             listeners : {
811                 drop : this.dragDrop.createDelegate(this),
812                 enter : this.dragEnter.createDelegate(this),
813                 out : this.dragOut.createDelegate(this),
814                 over : this.dragOver.createDelegate(this)
815             }
816             
817         });
818         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
819     },
820     
821     dragDrop : function(source,e,data)
822     {
823         // user has to decide how to impliment this.
824         Roo.log('drop');
825         Roo.log(this);
826         //this.fireEvent('drop', this, source, e ,data);
827         return false;
828     },
829     
830     dragEnter : function(n, dd, e, data)
831     {
832         // probably want to resize the element to match the dropped element..
833         Roo.log("enter");
834         this.originalSize = this.el.getSize();
835         this.el.setSize( n.el.getSize());
836         this.dropZone.DDM.refreshCache(this.name);
837         Roo.log([n, dd, e, data]);
838     },
839     
840     dragOut : function(value)
841     {
842         // resize back to normal
843         Roo.log("out");
844         this.el.setSize(this.originalSize);
845         this.dropZone.resetConstraints();
846     },
847     
848     dragOver : function()
849     {
850         // ??? do nothing?
851     }
852    
853 });
854
855  
856
857  /*
858  * - LGPL
859  *
860  * Body
861  *
862  */
863
864 /**
865  * @class Roo.bootstrap.Body
866  * @extends Roo.bootstrap.Component
867  * @children Roo.bootstrap.Component 
868  * @parent none builder
869  * Bootstrap Body class
870  *
871  * @constructor
872  * Create a new body
873  * @param {Object} config The config object
874  */
875
876 Roo.bootstrap.Body = function(config){
877
878     config = config || {};
879
880     Roo.bootstrap.Body.superclass.constructor.call(this, config);
881     this.el = Roo.get(config.el ? config.el : document.body );
882     if (this.cls && this.cls.length) {
883         Roo.get(document.body).addClass(this.cls);
884     }
885 };
886
887 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
888
889     is_body : true,// just to make sure it's constructed?
890
891         autoCreate : {
892         cls: 'container'
893     },
894     onRender : function(ct, position)
895     {
896        /* Roo.log("Roo.bootstrap.Body - onRender");
897         if (this.cls && this.cls.length) {
898             Roo.get(document.body).addClass(this.cls);
899         }
900         // style??? xttr???
901         */
902     }
903
904
905
906
907 });
908 /*
909  * - LGPL
910  *
911  * button group
912  * 
913  */
914
915
916 /**
917  * @class Roo.bootstrap.ButtonGroup
918  * @extends Roo.bootstrap.Component
919  * Bootstrap ButtonGroup class
920  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
921  * 
922  * @cfg {String} size lg | sm | xs (default empty normal)
923  * @cfg {String} align vertical | justified  (default none)
924  * @cfg {String} direction up | down (default down)
925  * @cfg {Boolean} toolbar false | true
926  * @cfg {Boolean} btn true | false
927  * 
928  * 
929  * @constructor
930  * Create a new Input
931  * @param {Object} config The config object
932  */
933
934 Roo.bootstrap.ButtonGroup = function(config){
935     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
936 };
937
938 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
939     
940     size: '',
941     align: '',
942     direction: '',
943     toolbar: false,
944     btn: true,
945
946     getAutoCreate : function(){
947         var cfg = {
948             cls: 'btn-group',
949             html : null
950         };
951         
952         cfg.html = this.html || cfg.html;
953         
954         if (this.toolbar) {
955             cfg = {
956                 cls: 'btn-toolbar',
957                 html: null
958             };
959             
960             return cfg;
961         }
962         
963         if (['vertical','justified'].indexOf(this.align)!==-1) {
964             cfg.cls = 'btn-group-' + this.align;
965             
966             if (this.align == 'justified') {
967                 console.log(this.items);
968             }
969         }
970         
971         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
972             cfg.cls += ' btn-group-' + this.size;
973         }
974         
975         if (this.direction == 'up') {
976             cfg.cls += ' dropup' ;
977         }
978         
979         return cfg;
980     },
981     /**
982      * Add a button to the group (similar to NavItem API.)
983      */
984     addItem : function(cfg)
985     {
986         var cn = new Roo.bootstrap.Button(cfg);
987         //this.register(cn);
988         cn.parentId = this.id;
989         cn.onRender(this.el, null);
990         return cn;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * button
999  * 
1000  */
1001
1002 /**
1003  * @class Roo.bootstrap.Button
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Button class
1006  * @cfg {String} html The button content
1007  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1008  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1009  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1010  * @cfg {String} size (lg|sm|xs)
1011  * @cfg {String} tag (a|input|submit)
1012  * @cfg {String} href empty or href
1013  * @cfg {Boolean} disabled default false;
1014  * @cfg {Boolean} isClose default false;
1015  * @cfg {String} glyphicon depricated - use fa
1016  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1017  * @cfg {String} badge text for badge
1018  * @cfg {String} theme (default|glow)  
1019  * @cfg {Boolean} inverse dark themed version
1020  * @cfg {Boolean} toggle is it a slidy toggle button
1021  * @cfg {Boolean} pressed   default null - if the button ahs active state
1022  * @cfg {String} ontext text for on slidy toggle state
1023  * @cfg {String} offtext text for off slidy toggle state
1024  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1025  * @cfg {Boolean} removeClass remove the standard class..
1026  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1027  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1028  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1029
1030  * @constructor
1031  * Create a new button
1032  * @param {Object} config The config object
1033  */
1034
1035
1036 Roo.bootstrap.Button = function(config){
1037     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1038     
1039     this.addEvents({
1040         // raw events
1041         /**
1042          * @event click
1043          * When a button is pressed
1044          * @param {Roo.bootstrap.Button} btn
1045          * @param {Roo.EventObject} e
1046          */
1047         "click" : true,
1048         /**
1049          * @event dblclick
1050          * When a button is double clicked
1051          * @param {Roo.bootstrap.Button} btn
1052          * @param {Roo.EventObject} e
1053          */
1054         "dblclick" : true,
1055          /**
1056          * @event toggle
1057          * After the button has been toggles
1058          * @param {Roo.bootstrap.Button} btn
1059          * @param {Roo.EventObject} e
1060          * @param {boolean} pressed (also available as button.pressed)
1061          */
1062         "toggle" : true
1063     });
1064 };
1065
1066 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1067     html: false,
1068     active: false,
1069     weight: '',
1070     badge_weight: '',
1071     outline : false,
1072     size: '',
1073     tag: 'button',
1074     href: '',
1075     disabled: false,
1076     isClose: false,
1077     glyphicon: '',
1078     fa: '',
1079     badge: '',
1080     theme: 'default',
1081     inverse: false,
1082     
1083     toggle: false,
1084     ontext: 'ON',
1085     offtext: 'OFF',
1086     defaulton: true,
1087     preventDefault: true,
1088     removeClass: false,
1089     name: false,
1090     target: false,
1091     group : false,
1092      
1093     pressed : null,
1094      
1095     
1096     getAutoCreate : function(){
1097         
1098         var cfg = {
1099             tag : 'button',
1100             cls : 'roo-button',
1101             html: ''
1102         };
1103         
1104         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1105             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1106             this.tag = 'button';
1107         } else {
1108             cfg.tag = this.tag;
1109         }
1110         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1111         
1112         if (this.toggle == true) {
1113             cfg={
1114                 tag: 'div',
1115                 cls: 'slider-frame roo-button',
1116                 cn: [
1117                     {
1118                         tag: 'span',
1119                         'data-on-text':'ON',
1120                         'data-off-text':'OFF',
1121                         cls: 'slider-button',
1122                         html: this.offtext
1123                     }
1124                 ]
1125             };
1126             // why are we validating the weights?
1127             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1128                 cfg.cls +=  ' ' + this.weight;
1129             }
1130             
1131             return cfg;
1132         }
1133         
1134         if (this.isClose) {
1135             cfg.cls += ' close';
1136             
1137             cfg["aria-hidden"] = true;
1138             
1139             cfg.html = "&times;";
1140             
1141             return cfg;
1142         }
1143              
1144         
1145         if (this.theme==='default') {
1146             cfg.cls = 'btn roo-button';
1147             
1148             //if (this.parentType != 'Navbar') {
1149             this.weight = this.weight.length ?  this.weight : 'default';
1150             //}
1151             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1152                 
1153                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1154                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1155                 cfg.cls += ' btn-' + outline + weight;
1156                 if (this.weight == 'default') {
1157                     // BC
1158                     cfg.cls += ' btn-' + this.weight;
1159                 }
1160             }
1161         } else if (this.theme==='glow') {
1162             
1163             cfg.tag = 'a';
1164             cfg.cls = 'btn-glow roo-button';
1165             
1166             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1167                 
1168                 cfg.cls += ' ' + this.weight;
1169             }
1170         }
1171    
1172         
1173         if (this.inverse) {
1174             this.cls += ' inverse';
1175         }
1176         
1177         
1178         if (this.active || this.pressed === true) {
1179             cfg.cls += ' active';
1180         }
1181         
1182         if (this.disabled) {
1183             cfg.disabled = 'disabled';
1184         }
1185         
1186         if (this.items) {
1187             Roo.log('changing to ul' );
1188             cfg.tag = 'ul';
1189             this.glyphicon = 'caret';
1190             if (Roo.bootstrap.version == 4) {
1191                 this.fa = 'caret-down';
1192             }
1193             
1194         }
1195         
1196         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1197          
1198         //gsRoo.log(this.parentType);
1199         if (this.parentType === 'Navbar' && !this.parent().bar) {
1200             Roo.log('changing to li?');
1201             
1202             cfg.tag = 'li';
1203             
1204             cfg.cls = '';
1205             cfg.cn =  [{
1206                 tag : 'a',
1207                 cls : 'roo-button',
1208                 html : this.html,
1209                 href : this.href || '#'
1210             }];
1211             if (this.menu) {
1212                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1213                 cfg.cls += ' dropdown';
1214             }   
1215             
1216             delete cfg.html;
1217             
1218         }
1219         
1220        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1221         
1222         if (this.glyphicon) {
1223             cfg.html = ' ' + cfg.html;
1224             
1225             cfg.cn = [
1226                 {
1227                     tag: 'span',
1228                     cls: 'glyphicon glyphicon-' + this.glyphicon
1229                 }
1230             ];
1231         }
1232         if (this.fa) {
1233             cfg.html = ' ' + cfg.html;
1234             
1235             cfg.cn = [
1236                 {
1237                     tag: 'i',
1238                     cls: 'fa fas fa-' + this.fa
1239                 }
1240             ];
1241         }
1242         
1243         if (this.badge) {
1244             cfg.html += ' ';
1245             
1246             cfg.tag = 'a';
1247             
1248 //            cfg.cls='btn roo-button';
1249             
1250             cfg.href=this.href;
1251             
1252             var value = cfg.html;
1253             
1254             if(this.glyphicon){
1255                 value = {
1256                     tag: 'span',
1257                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1258                     html: this.html
1259                 };
1260             }
1261             if(this.fa){
1262                 value = {
1263                     tag: 'i',
1264                     cls: 'fa fas fa-' + this.fa,
1265                     html: this.html
1266                 };
1267             }
1268             
1269             var bw = this.badge_weight.length ? this.badge_weight :
1270                 (this.weight.length ? this.weight : 'secondary');
1271             bw = bw == 'default' ? 'secondary' : bw;
1272             
1273             cfg.cn = [
1274                 value,
1275                 {
1276                     tag: 'span',
1277                     cls: 'badge badge-' + bw,
1278                     html: this.badge
1279                 }
1280             ];
1281             
1282             cfg.html='';
1283         }
1284         
1285         if (this.menu) {
1286             cfg.cls += ' dropdown';
1287             cfg.html = typeof(cfg.html) != 'undefined' ?
1288                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1289         }
1290         
1291         if (cfg.tag !== 'a' && this.href !== '') {
1292             throw "Tag must be a to set href.";
1293         } else if (this.href.length > 0) {
1294             cfg.href = this.href;
1295         }
1296         
1297         if(this.removeClass){
1298             cfg.cls = '';
1299         }
1300         
1301         if(this.target){
1302             cfg.target = this.target;
1303         }
1304         
1305         return cfg;
1306     },
1307     initEvents: function() {
1308        // Roo.log('init events?');
1309 //        Roo.log(this.el.dom);
1310         // add the menu...
1311         
1312         if (typeof (this.menu) != 'undefined') {
1313             this.menu.parentType = this.xtype;
1314             this.menu.triggerEl = this.el;
1315             this.addxtype(Roo.apply({}, this.menu));
1316         }
1317
1318
1319         if (this.el.hasClass('roo-button')) {
1320              this.el.on('click', this.onClick, this);
1321              this.el.on('dblclick', this.onDblClick, this);
1322         } else {
1323              this.el.select('.roo-button').on('click', this.onClick, this);
1324              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1325              
1326         }
1327         // why?
1328         if(this.removeClass){
1329             this.el.on('click', this.onClick, this);
1330         }
1331         
1332         if (this.group === true) {
1333              if (this.pressed === false || this.pressed === true) {
1334                 // nothing
1335             } else {
1336                 this.pressed = false;
1337                 this.setActive(this.pressed);
1338             }
1339             
1340         }
1341         
1342         this.el.enableDisplayMode();
1343         
1344     },
1345     onClick : function(e)
1346     {
1347         if (this.disabled) {
1348             return;
1349         }
1350         
1351         Roo.log('button on click ');
1352         if(this.href === '' || this.preventDefault){
1353             e.preventDefault();
1354         }
1355         
1356         if (this.group) {
1357             if (this.pressed) {
1358                 // do nothing -
1359                 return;
1360             }
1361             this.setActive(true);
1362             var pi = this.parent().items;
1363             for (var i = 0;i < pi.length;i++) {
1364                 if (this == pi[i]) {
1365                     continue;
1366                 }
1367                 if (pi[i].el.hasClass('roo-button')) {
1368                     pi[i].setActive(false);
1369                 }
1370             }
1371             this.fireEvent('click', this, e);            
1372             return;
1373         }
1374         
1375         if (this.pressed === true || this.pressed === false) {
1376             this.toggleActive(e);
1377         }
1378         
1379         
1380         this.fireEvent('click', this, e);
1381     },
1382     onDblClick: function(e)
1383     {
1384         if (this.disabled) {
1385             return;
1386         }
1387         if(this.preventDefault){
1388             e.preventDefault();
1389         }
1390         this.fireEvent('dblclick', this, e);
1391     },
1392     /**
1393      * Enables this button
1394      */
1395     enable : function()
1396     {
1397         this.disabled = false;
1398         this.el.removeClass('disabled');
1399         this.el.dom.removeAttribute("disabled");
1400     },
1401     
1402     /**
1403      * Disable this button
1404      */
1405     disable : function()
1406     {
1407         this.disabled = true;
1408         this.el.addClass('disabled');
1409         this.el.attr("disabled", "disabled")
1410     },
1411      /**
1412      * sets the active state on/off, 
1413      * @param {Boolean} state (optional) Force a particular state
1414      */
1415     setActive : function(v) {
1416         
1417         this.el[v ? 'addClass' : 'removeClass']('active');
1418         this.pressed = v;
1419     },
1420      /**
1421      * toggles the current active state 
1422      */
1423     toggleActive : function(e)
1424     {
1425         this.setActive(!this.pressed); // this modifies pressed...
1426         this.fireEvent('toggle', this, e, this.pressed);
1427     },
1428      /**
1429      * get the current active state
1430      * @return {boolean} true if it's active
1431      */
1432     isActive : function()
1433     {
1434         return this.el.hasClass('active');
1435     },
1436     /**
1437      * set the text of the first selected button
1438      */
1439     setText : function(str)
1440     {
1441         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1442     },
1443     /**
1444      * get the text of the first selected button
1445      */
1446     getText : function()
1447     {
1448         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1449     },
1450     
1451     setWeight : function(str)
1452     {
1453         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1454         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1455         this.weight = str;
1456         var outline = this.outline ? 'outline-' : '';
1457         if (str == 'default') {
1458             this.el.addClass('btn-default btn-outline-secondary');        
1459             return;
1460         }
1461         this.el.addClass('btn-' + outline + str);        
1462     }
1463     
1464     
1465 });
1466 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1467
1468 Roo.bootstrap.Button.weights = [
1469     'default',
1470     'secondary' ,
1471     'primary',
1472     'success',
1473     'info',
1474     'warning',
1475     'danger',
1476     'link',
1477     'light',
1478     'dark'              
1479    
1480 ];/*
1481  * - LGPL
1482  *
1483  * column
1484  * 
1485  */
1486
1487 /**
1488  * @class Roo.bootstrap.Column
1489  * @extends Roo.bootstrap.Component
1490  * @children Roo.bootstrap.Component
1491  * Bootstrap Column class
1492  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1493  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1494  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1495  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1496  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1497  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1498  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1499  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1500  *
1501  * 
1502  * @cfg {Boolean} hidden (true|false) hide the element
1503  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1504  * @cfg {String} fa (ban|check|...) font awesome icon
1505  * @cfg {Number} fasize (1|2|....) font awsome size
1506
1507  * @cfg {String} icon (info-sign|check|...) glyphicon name
1508
1509  * @cfg {String} html content of column.
1510  * 
1511  * @constructor
1512  * Create a new Column
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Column = function(config){
1517     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1518 };
1519
1520 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1521     
1522     xs: false,
1523     sm: false,
1524     md: false,
1525     lg: false,
1526     xsoff: false,
1527     smoff: false,
1528     mdoff: false,
1529     lgoff: false,
1530     html: '',
1531     offset: 0,
1532     alert: false,
1533     fa: false,
1534     icon : false,
1535     hidden : false,
1536     fasize : 1,
1537     
1538     getAutoCreate : function(){
1539         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1540         
1541         cfg = {
1542             tag: 'div',
1543             cls: 'column'
1544         };
1545         
1546         var settings=this;
1547         var sizes =   ['xs','sm','md','lg'];
1548         sizes.map(function(size ,ix){
1549             //Roo.log( size + ':' + settings[size]);
1550             
1551             if (settings[size+'off'] !== false) {
1552                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1553             }
1554             
1555             if (settings[size] === false) {
1556                 return;
1557             }
1558             
1559             if (!settings[size]) { // 0 = hidden
1560                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1561                 // bootsrap4
1562                 for (var i = ix; i > -1; i--) {
1563                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1564                 }
1565                 
1566                 
1567                 return;
1568             }
1569             cfg.cls += ' col-' + size + '-' + settings[size] + (
1570                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1571             );
1572             
1573         });
1574         
1575         if (this.hidden) {
1576             cfg.cls += ' hidden';
1577         }
1578         
1579         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1580             cfg.cls +=' alert alert-' + this.alert;
1581         }
1582         
1583         
1584         if (this.html.length) {
1585             cfg.html = this.html;
1586         }
1587         if (this.fa) {
1588             var fasize = '';
1589             if (this.fasize > 1) {
1590                 fasize = ' fa-' + this.fasize + 'x';
1591             }
1592             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1593             
1594             
1595         }
1596         if (this.icon) {
1597             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1598         }
1599         
1600         return cfg;
1601     }
1602    
1603 });
1604
1605  
1606
1607  /*
1608  * - LGPL
1609  *
1610  * page container.
1611  * 
1612  */
1613
1614
1615 /**
1616  * @class Roo.bootstrap.Container
1617  * @extends Roo.bootstrap.Component
1618  * @children Roo.bootstrap.Component
1619  * @parent builder
1620  * Bootstrap Container class
1621  * @cfg {Boolean} jumbotron is it a jumbotron element
1622  * @cfg {String} html content of element
1623  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1624  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1625  * @cfg {String} header content of header (for panel)
1626  * @cfg {String} footer content of footer (for panel)
1627  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1628  * @cfg {String} tag (header|aside|section) type of HTML tag.
1629  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1630  * @cfg {String} fa font awesome icon
1631  * @cfg {String} icon (info-sign|check|...) glyphicon name
1632  * @cfg {Boolean} hidden (true|false) hide the element
1633  * @cfg {Boolean} expandable (true|false) default false
1634  * @cfg {Boolean} expanded (true|false) default true
1635  * @cfg {String} rheader contet on the right of header
1636  * @cfg {Boolean} clickable (true|false) default false
1637
1638  *     
1639  * @constructor
1640  * Create a new Container
1641  * @param {Object} config The config object
1642  */
1643
1644 Roo.bootstrap.Container = function(config){
1645     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1646     
1647     this.addEvents({
1648         // raw events
1649          /**
1650          * @event expand
1651          * After the panel has been expand
1652          * 
1653          * @param {Roo.bootstrap.Container} this
1654          */
1655         "expand" : true,
1656         /**
1657          * @event collapse
1658          * After the panel has been collapsed
1659          * 
1660          * @param {Roo.bootstrap.Container} this
1661          */
1662         "collapse" : true,
1663         /**
1664          * @event click
1665          * When a element is chick
1666          * @param {Roo.bootstrap.Container} this
1667          * @param {Roo.EventObject} e
1668          */
1669         "click" : true
1670     });
1671 };
1672
1673 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1674     
1675     jumbotron : false,
1676     well: '',
1677     panel : '',
1678     header: '',
1679     footer : '',
1680     sticky: '',
1681     tag : false,
1682     alert : false,
1683     fa: false,
1684     icon : false,
1685     expandable : false,
1686     rheader : '',
1687     expanded : true,
1688     clickable: false,
1689   
1690      
1691     getChildContainer : function() {
1692         
1693         if(!this.el){
1694             return false;
1695         }
1696         
1697         if (this.panel.length) {
1698             return this.el.select('.panel-body',true).first();
1699         }
1700         
1701         return this.el;
1702     },
1703     
1704     
1705     getAutoCreate : function(){
1706         
1707         var cfg = {
1708             tag : this.tag || 'div',
1709             html : '',
1710             cls : ''
1711         };
1712         if (this.jumbotron) {
1713             cfg.cls = 'jumbotron';
1714         }
1715         
1716         
1717         
1718         // - this is applied by the parent..
1719         //if (this.cls) {
1720         //    cfg.cls = this.cls + '';
1721         //}
1722         
1723         if (this.sticky.length) {
1724             
1725             var bd = Roo.get(document.body);
1726             if (!bd.hasClass('bootstrap-sticky')) {
1727                 bd.addClass('bootstrap-sticky');
1728                 Roo.select('html',true).setStyle('height', '100%');
1729             }
1730              
1731             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1732         }
1733         
1734         
1735         if (this.well.length) {
1736             switch (this.well) {
1737                 case 'lg':
1738                 case 'sm':
1739                     cfg.cls +=' well well-' +this.well;
1740                     break;
1741                 default:
1742                     cfg.cls +=' well';
1743                     break;
1744             }
1745         }
1746         
1747         if (this.hidden) {
1748             cfg.cls += ' hidden';
1749         }
1750         
1751         
1752         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1753             cfg.cls +=' alert alert-' + this.alert;
1754         }
1755         
1756         var body = cfg;
1757         
1758         if (this.panel.length) {
1759             cfg.cls += ' panel panel-' + this.panel;
1760             cfg.cn = [];
1761             if (this.header.length) {
1762                 
1763                 var h = [];
1764                 
1765                 if(this.expandable){
1766                     
1767                     cfg.cls = cfg.cls + ' expandable';
1768                     
1769                     h.push({
1770                         tag: 'i',
1771                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1772                     });
1773                     
1774                 }
1775                 
1776                 h.push(
1777                     {
1778                         tag: 'span',
1779                         cls : 'panel-title',
1780                         html : (this.expandable ? '&nbsp;' : '') + this.header
1781                     },
1782                     {
1783                         tag: 'span',
1784                         cls: 'panel-header-right',
1785                         html: this.rheader
1786                     }
1787                 );
1788                 
1789                 cfg.cn.push({
1790                     cls : 'panel-heading',
1791                     style : this.expandable ? 'cursor: pointer' : '',
1792                     cn : h
1793                 });
1794                 
1795             }
1796             
1797             body = false;
1798             cfg.cn.push({
1799                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1800                 html : this.html
1801             });
1802             
1803             
1804             if (this.footer.length) {
1805                 cfg.cn.push({
1806                     cls : 'panel-footer',
1807                     html : this.footer
1808                     
1809                 });
1810             }
1811             
1812         }
1813         
1814         if (body) {
1815             body.html = this.html || cfg.html;
1816             // prefix with the icons..
1817             if (this.fa) {
1818                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1819             }
1820             if (this.icon) {
1821                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1822             }
1823             
1824             
1825         }
1826         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1827             cfg.cls =  'container';
1828         }
1829         
1830         return cfg;
1831     },
1832     
1833     initEvents: function() 
1834     {
1835         if(this.expandable){
1836             var headerEl = this.headerEl();
1837         
1838             if(headerEl){
1839                 headerEl.on('click', this.onToggleClick, this);
1840             }
1841         }
1842         
1843         if(this.clickable){
1844             this.el.on('click', this.onClick, this);
1845         }
1846         
1847     },
1848     
1849     onToggleClick : function()
1850     {
1851         var headerEl = this.headerEl();
1852         
1853         if(!headerEl){
1854             return;
1855         }
1856         
1857         if(this.expanded){
1858             this.collapse();
1859             return;
1860         }
1861         
1862         this.expand();
1863     },
1864     
1865     expand : function()
1866     {
1867         if(this.fireEvent('expand', this)) {
1868             
1869             this.expanded = true;
1870             
1871             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1872             
1873             this.el.select('.panel-body',true).first().removeClass('hide');
1874             
1875             var toggleEl = this.toggleEl();
1876
1877             if(!toggleEl){
1878                 return;
1879             }
1880
1881             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1882         }
1883         
1884     },
1885     
1886     collapse : function()
1887     {
1888         if(this.fireEvent('collapse', this)) {
1889             
1890             this.expanded = false;
1891             
1892             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1893             this.el.select('.panel-body',true).first().addClass('hide');
1894         
1895             var toggleEl = this.toggleEl();
1896
1897             if(!toggleEl){
1898                 return;
1899             }
1900
1901             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1902         }
1903     },
1904     
1905     toggleEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading .fa',true).first();
1912     },
1913     
1914     headerEl : function()
1915     {
1916         if(!this.el || !this.panel.length || !this.header.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-heading',true).first()
1921     },
1922     
1923     bodyEl : function()
1924     {
1925         if(!this.el || !this.panel.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-body',true).first()
1930     },
1931     
1932     titleEl : function()
1933     {
1934         if(!this.el || !this.panel.length || !this.header.length){
1935             return;
1936         }
1937         
1938         return this.el.select('.panel-title',true).first();
1939     },
1940     
1941     setTitle : function(v)
1942     {
1943         var titleEl = this.titleEl();
1944         
1945         if(!titleEl){
1946             return;
1947         }
1948         
1949         titleEl.dom.innerHTML = v;
1950     },
1951     
1952     getTitle : function()
1953     {
1954         
1955         var titleEl = this.titleEl();
1956         
1957         if(!titleEl){
1958             return '';
1959         }
1960         
1961         return titleEl.dom.innerHTML;
1962     },
1963     
1964     setRightTitle : function(v)
1965     {
1966         var t = this.el.select('.panel-header-right',true).first();
1967         
1968         if(!t){
1969             return;
1970         }
1971         
1972         t.dom.innerHTML = v;
1973     },
1974     
1975     onClick : function(e)
1976     {
1977         e.preventDefault();
1978         
1979         this.fireEvent('click', this, e);
1980     }
1981 });
1982
1983  /**
1984  * @class Roo.bootstrap.Card
1985  * @extends Roo.bootstrap.Component
1986  * @children Roo.bootstrap.Component
1987  * @licence LGPL
1988  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1989  *
1990  *
1991  * possible... may not be implemented..
1992  * @cfg {String} header_image  src url of image.
1993  * @cfg {String|Object} header
1994  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1995  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1996  * 
1997  * @cfg {String} title
1998  * @cfg {String} subtitle
1999  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
2000  * @cfg {String} footer
2001  
2002  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2003  * 
2004  * @cfg {String} margin (0|1|2|3|4|5|auto)
2005  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2006  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2007  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2008  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2009  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2010  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2011  *
2012  * @cfg {String} padding (0|1|2|3|4|5)
2013  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2014  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2015  * @cfg {String} padding_left (0|1|2|3|4|5)
2016  * @cfg {String} padding_right (0|1|2|3|4|5)
2017  * @cfg {String} padding_x (0|1|2|3|4|5)
2018  * @cfg {String} padding_y (0|1|2|3|4|5)
2019  *
2020  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2021  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2022  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2024  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2025  
2026  * @config {Boolean} dragable  if this card can be dragged.
2027  * @config {String} drag_group  group for drag
2028  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2029  * @config {String} drop_group  group for drag
2030  * 
2031  * @config {Boolean} collapsable can the body be collapsed.
2032  * @config {Boolean} collapsed is the body collapsed when rendered...
2033  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2034  * @config {Boolean} rotated is the body rotated when rendered...
2035  * 
2036  * @constructor
2037  * Create a new Container
2038  * @param {Object} config The config object
2039  */
2040
2041 Roo.bootstrap.Card = function(config){
2042     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2043     
2044     this.addEvents({
2045          // raw events
2046         /**
2047          * @event drop
2048          * When a element a card is dropped
2049          * @param {Roo.bootstrap.Card} this
2050          *
2051          * 
2052          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2053          * @param {String} position 'above' or 'below'
2054          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2055         
2056          */
2057         'drop' : true,
2058          /**
2059          * @event rotate
2060          * When a element a card is rotate
2061          * @param {Roo.bootstrap.Card} this
2062          * @param {Roo.Element} n the node being dropped?
2063          * @param {Boolean} rotate status
2064          */
2065         'rotate' : true,
2066         /**
2067          * @event cardover
2068          * When a card element is dragged over ready to drop (return false to block dropable)
2069          * @param {Roo.bootstrap.Card} this
2070          * @param {Object} data from dragdrop 
2071          */
2072          'cardover' : true
2073          
2074     });
2075 };
2076
2077
2078 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2079     
2080     
2081     weight : '',
2082     
2083     margin: '', /// may be better in component?
2084     margin_top: '', 
2085     margin_bottom: '', 
2086     margin_left: '',
2087     margin_right: '',
2088     margin_x: '',
2089     margin_y: '',
2090     
2091     padding : '',
2092     padding_top: '', 
2093     padding_bottom: '', 
2094     padding_left: '',
2095     padding_right: '',
2096     padding_x: '',
2097     padding_y: '',
2098     
2099     display: '', 
2100     display_xs: '', 
2101     display_sm: '', 
2102     display_lg: '',
2103     display_xl: '',
2104  
2105     header_image  : '',
2106     header : '',
2107     header_size : 0,
2108     title : '',
2109     subtitle : '',
2110     html : '',
2111     footer: '',
2112
2113     collapsable : false,
2114     collapsed : false,
2115     rotateable : false,
2116     rotated : false,
2117     
2118     dragable : false,
2119     drag_group : false,
2120     dropable : false,
2121     drop_group : false,
2122     childContainer : false,
2123     dropEl : false, /// the dom placeholde element that indicates drop location.
2124     containerEl: false, // body container
2125     bodyEl: false, // card-body
2126     headerContainerEl : false, //
2127     headerEl : false,
2128     header_imageEl : false,
2129     
2130     
2131     layoutCls : function()
2132     {
2133         var cls = '';
2134         var t = this;
2135         Roo.log(this.margin_bottom.length);
2136         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2137             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2138             
2139             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2141             }
2142             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2143                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2144             }
2145         });
2146         
2147         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2148             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2149                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2150             }
2151         });
2152         
2153         // more generic support?
2154         if (this.hidden) {
2155             cls += ' d-none';
2156         }
2157         
2158         return cls;
2159     },
2160  
2161        // Roo.log("Call onRender: " + this.xtype);
2162         /*  We are looking at something like this.
2163 <div class="card">
2164     <img src="..." class="card-img-top" alt="...">
2165     <div class="card-body">
2166         <h5 class="card-title">Card title</h5>
2167          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2168
2169         >> this bit is really the body...
2170         <div> << we will ad dthis in hopefully it will not break shit.
2171         
2172         ** card text does not actually have any styling...
2173         
2174             <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>
2175         
2176         </div> <<
2177           <a href="#" class="card-link">Card link</a>
2178           
2179     </div>
2180     <div class="card-footer">
2181         <small class="text-muted">Last updated 3 mins ago</small>
2182     </div>
2183 </div>
2184          */
2185     getAutoCreate : function(){
2186         
2187         var cfg = {
2188             tag : 'div',
2189             cls : 'card',
2190             cn : [ ]
2191         };
2192         
2193         if (this.weight.length && this.weight != 'light') {
2194             cfg.cls += ' text-white';
2195         } else {
2196             cfg.cls += ' text-dark'; // need as it's nested..
2197         }
2198         if (this.weight.length) {
2199             cfg.cls += ' bg-' + this.weight;
2200         }
2201         
2202         cfg.cls += ' ' + this.layoutCls(); 
2203         
2204         var hdr = false;
2205         var hdr_ctr = false;
2206         if (this.header.length) {
2207             hdr = {
2208                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2209                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2210                 cn : []
2211             };
2212             cfg.cn.push(hdr);
2213             hdr_ctr = hdr;
2214         } else {
2215             hdr = {
2216                 tag : 'div',
2217                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2218                 cn : []
2219             };
2220             cfg.cn.push(hdr);
2221             hdr_ctr = hdr;
2222         }
2223         if (this.collapsable) {
2224             hdr_ctr = {
2225             tag : 'a',
2226             cls : 'd-block user-select-none',
2227             cn: [
2228                     {
2229                         tag: 'i',
2230                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2231                     }
2232                    
2233                 ]
2234             };
2235             hdr.cn.push(hdr_ctr);
2236         }
2237         
2238         hdr_ctr.cn.push(        {
2239             tag: 'span',
2240             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2241             html : this.header
2242         });
2243         
2244         
2245         if (this.header_image.length) {
2246             cfg.cn.push({
2247                 tag : 'img',
2248                 cls : 'card-img-top',
2249                 src: this.header_image // escape?
2250             });
2251         } else {
2252             cfg.cn.push({
2253                     tag : 'div',
2254                     cls : 'card-img-top d-none' 
2255                 });
2256         }
2257             
2258         var body = {
2259             tag : 'div',
2260             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2261             cn : []
2262         };
2263         var obody = body;
2264         if (this.collapsable || this.rotateable) {
2265             obody = {
2266                 tag: 'div',
2267                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2268                 cn : [  body ]
2269             };
2270         }
2271         
2272         cfg.cn.push(obody);
2273         
2274         if (this.title.length) {
2275             body.cn.push({
2276                 tag : 'div',
2277                 cls : 'card-title',
2278                 src: this.title // escape?
2279             });
2280         }  
2281         
2282         if (this.subtitle.length) {
2283             body.cn.push({
2284                 tag : 'div',
2285                 cls : 'card-title',
2286                 src: this.subtitle // escape?
2287             });
2288         }
2289         
2290         body.cn.push({
2291             tag : 'div',
2292             cls : 'roo-card-body-ctr'
2293         });
2294         
2295         if (this.html.length) {
2296             body.cn.push({
2297                 tag: 'div',
2298                 html : this.html
2299             });
2300         }
2301         // fixme ? handle objects?
2302         
2303         if (this.footer.length) {
2304            
2305             cfg.cn.push({
2306                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2307                 html : this.footer
2308             });
2309             
2310         } else {
2311             cfg.cn.push({cls : 'card-footer d-none'});
2312         }
2313         
2314         // footer...
2315         
2316         return cfg;
2317     },
2318     
2319     
2320     getCardHeader : function()
2321     {
2322         var  ret = this.el.select('.card-header',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardFooter : function()
2330     {
2331         var  ret = this.el.select('.card-footer',true).first();
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335         
2336         return ret;
2337     },
2338     getCardImageTop : function()
2339     {
2340         var  ret = this.header_imageEl;
2341         if (ret.hasClass('d-none')) {
2342             ret.removeClass('d-none');
2343         }
2344             
2345         return ret;
2346     },
2347     
2348     getChildContainer : function()
2349     {
2350         
2351         if(!this.el){
2352             return false;
2353         }
2354         return this.el.select('.roo-card-body-ctr',true).first();    
2355     },
2356     
2357     initEvents: function() 
2358     {
2359         this.bodyEl = this.el.select('.card-body',true).first(); 
2360         this.containerEl = this.getChildContainer();
2361         if(this.dragable){
2362             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2363                     containerScroll: true,
2364                     ddGroup: this.drag_group || 'default_card_drag_group'
2365             });
2366             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2367         }
2368         if (this.dropable) {
2369             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2370                 containerScroll: true,
2371                 ddGroup: this.drop_group || 'default_card_drag_group'
2372             });
2373             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2374             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2375             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2376             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2377             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2378         }
2379         
2380         if (this.collapsable) {
2381             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2382         }
2383         if (this.rotateable) {
2384             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2385         }
2386         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2387          
2388         this.footerEl = this.el.select('.card-footer',true).first();
2389         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2390         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2391         this.headerEl = this.el.select('.card-header',true).first();
2392         
2393         if (this.rotated) {
2394             this.el.addClass('roo-card-rotated');
2395             this.fireEvent('rotate', this, true);
2396         }
2397         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2398         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2399         
2400     },
2401     getDragData : function(e)
2402     {
2403         var target = this.getEl();
2404         if (target) {
2405             //this.handleSelection(e);
2406             
2407             var dragData = {
2408                 source: this,
2409                 copy: false,
2410                 nodes: this.getEl(),
2411                 records: []
2412             };
2413             
2414             
2415             dragData.ddel = target.dom ;    // the div element
2416             Roo.log(target.getWidth( ));
2417             dragData.ddel.style.width = target.getWidth() + 'px';
2418             
2419             return dragData;
2420         }
2421         return false;
2422     },
2423     /**
2424     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2425     *    whole Element becomes the target, and this causes the drop gesture to append.
2426     *
2427     *    Returns an object:
2428     *     {
2429            
2430            position : 'below' or 'above'
2431            card  : relateive to card OBJECT (or true for no cards listed)
2432            items_n : relative to nth item in list
2433            card_n : relative to  nth card in list
2434     }
2435     *
2436     *    
2437     */
2438     getTargetFromEvent : function(e, dragged_card_el)
2439     {
2440         var target = e.getTarget();
2441         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2442             target = target.parentNode;
2443         }
2444         
2445         var ret = {
2446             position: '',
2447             cards : [],
2448             card_n : -1,
2449             items_n : -1,
2450             card : false 
2451         };
2452         
2453         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2454         // see if target is one of the 'cards'...
2455         
2456         
2457         //Roo.log(this.items.length);
2458         var pos = false;
2459         
2460         var last_card_n = 0;
2461         var cards_len  = 0;
2462         for (var i = 0;i< this.items.length;i++) {
2463             
2464             if (!this.items[i].el.hasClass('card')) {
2465                  continue;
2466             }
2467             pos = this.getDropPoint(e, this.items[i].el.dom);
2468             
2469             cards_len = ret.cards.length;
2470             //Roo.log(this.items[i].el.dom.id);
2471             ret.cards.push(this.items[i]);
2472             last_card_n  = i;
2473             if (ret.card_n < 0 && pos == 'above') {
2474                 ret.position = cards_len > 0 ? 'below' : pos;
2475                 ret.items_n = i > 0 ? i - 1 : 0;
2476                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2477                 ret.card = ret.cards[ret.card_n];
2478             }
2479         }
2480         if (!ret.cards.length) {
2481             ret.card = true;
2482             ret.position = 'below';
2483             ret.items_n;
2484             return ret;
2485         }
2486         // could not find a card.. stick it at the end..
2487         if (ret.card_n < 0) {
2488             ret.card_n = last_card_n;
2489             ret.card = ret.cards[last_card_n];
2490             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2491             ret.position = 'below';
2492         }
2493         
2494         if (this.items[ret.items_n].el == dragged_card_el) {
2495             return false;
2496         }
2497         
2498         if (ret.position == 'below') {
2499             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2500             
2501             if (card_after  && card_after.el == dragged_card_el) {
2502                 return false;
2503             }
2504             return ret;
2505         }
2506         
2507         // its's after ..
2508         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2509         
2510         if (card_before  && card_before.el == dragged_card_el) {
2511             return false;
2512         }
2513         
2514         return ret;
2515     },
2516     
2517     onNodeEnter : function(n, dd, e, data){
2518         return false;
2519     },
2520     onNodeOver : function(n, dd, e, data)
2521     {
2522        
2523         var target_info = this.getTargetFromEvent(e,data.source.el);
2524         if (target_info === false) {
2525             this.dropPlaceHolder('hide');
2526             return false;
2527         }
2528         Roo.log(['getTargetFromEvent', target_info ]);
2529         
2530         
2531         if (this.fireEvent('cardover', this, [ data ]) === false) {
2532             return false;
2533         }
2534         
2535         this.dropPlaceHolder('show', target_info,data);
2536         
2537         return false; 
2538     },
2539     onNodeOut : function(n, dd, e, data){
2540         this.dropPlaceHolder('hide');
2541      
2542     },
2543     onNodeDrop : function(n, dd, e, data)
2544     {
2545         
2546         // call drop - return false if
2547         
2548         // this could actually fail - if the Network drops..
2549         // we will ignore this at present..- client should probably reload
2550         // the whole set of cards if stuff like that fails.
2551         
2552         
2553         var info = this.getTargetFromEvent(e,data.source.el);
2554         if (info === false) {
2555             return false;
2556         }
2557         this.dropPlaceHolder('hide');
2558   
2559           
2560     
2561         this.acceptCard(data.source, info.position, info.card, info.items_n);
2562         return true;
2563          
2564     },
2565     firstChildCard : function()
2566     {
2567         for (var i = 0;i< this.items.length;i++) {
2568             
2569             if (!this.items[i].el.hasClass('card')) {
2570                  continue;
2571             }
2572             return this.items[i];
2573         }
2574         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2575     },
2576     /**
2577      * accept card
2578      *
2579      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2580      */
2581     acceptCard : function(move_card,  position, next_to_card )
2582     {
2583         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2584             return false;
2585         }
2586         
2587         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2588         
2589         move_card.parent().removeCard(move_card);
2590         
2591         
2592         var dom = move_card.el.dom;
2593         dom.style.width = ''; // clear with - which is set by drag.
2594         
2595         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2596             var cardel = next_to_card.el.dom;
2597             
2598             if (position == 'above' ) {
2599                 cardel.parentNode.insertBefore(dom, cardel);
2600             } else if (cardel.nextSibling) {
2601                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2602             } else {
2603                 cardel.parentNode.append(dom);
2604             }
2605         } else {
2606             // card container???
2607             this.containerEl.dom.append(dom);
2608         }
2609         
2610         //FIXME HANDLE card = true 
2611         
2612         // add this to the correct place in items.
2613         
2614         // remove Card from items.
2615         
2616        
2617         if (this.items.length) {
2618             var nitems = [];
2619             //Roo.log([info.items_n, info.position, this.items.length]);
2620             for (var i =0; i < this.items.length; i++) {
2621                 if (i == to_items_n && position == 'above') {
2622                     nitems.push(move_card);
2623                 }
2624                 nitems.push(this.items[i]);
2625                 if (i == to_items_n && position == 'below') {
2626                     nitems.push(move_card);
2627                 }
2628             }
2629             this.items = nitems;
2630             Roo.log(this.items);
2631         } else {
2632             this.items.push(move_card);
2633         }
2634         
2635         move_card.parentId = this.id;
2636         
2637         return true;
2638         
2639         
2640     },
2641     removeCard : function(c)
2642     {
2643         this.items = this.items.filter(function(e) { return e != c });
2644  
2645         var dom = c.el.dom;
2646         dom.parentNode.removeChild(dom);
2647         dom.style.width = ''; // clear with - which is set by drag.
2648         c.parentId = false;
2649         
2650     },
2651     
2652     /**    Decide whether to drop above or below a View node. */
2653     getDropPoint : function(e, n, dd)
2654     {
2655         if (dd) {
2656              return false;
2657         }
2658         if (n == this.containerEl.dom) {
2659             return "above";
2660         }
2661         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2662         var c = t + (b - t) / 2;
2663         var y = Roo.lib.Event.getPageY(e);
2664         if(y <= c) {
2665             return "above";
2666         }else{
2667             return "below";
2668         }
2669     },
2670     onToggleCollapse : function(e)
2671         {
2672         if (this.collapsed) {
2673             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2674             this.collapsableEl.addClass('show');
2675             this.collapsed = false;
2676             return;
2677         }
2678         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2679         this.collapsableEl.removeClass('show');
2680         this.collapsed = true;
2681         
2682     
2683     },
2684     
2685     onToggleRotate : function(e)
2686     {
2687         this.collapsableEl.removeClass('show');
2688         this.footerEl.removeClass('d-none');
2689         this.el.removeClass('roo-card-rotated');
2690         this.el.removeClass('d-none');
2691         if (this.rotated) {
2692             
2693             this.collapsableEl.addClass('show');
2694             this.rotated = false;
2695             this.fireEvent('rotate', this, this.rotated);
2696             return;
2697         }
2698         this.el.addClass('roo-card-rotated');
2699         this.footerEl.addClass('d-none');
2700         this.el.select('.roo-collapsable').removeClass('show');
2701         
2702         this.rotated = true;
2703         this.fireEvent('rotate', this, this.rotated);
2704     
2705     },
2706     
2707     dropPlaceHolder: function (action, info, data)
2708     {
2709         if (this.dropEl === false) {
2710             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2711             cls : 'd-none'
2712             },true);
2713         }
2714         this.dropEl.removeClass(['d-none', 'd-block']);        
2715         if (action == 'hide') {
2716             
2717             this.dropEl.addClass('d-none');
2718             return;
2719         }
2720         // FIXME - info.card == true!!!
2721         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2722         
2723         if (info.card !== true) {
2724             var cardel = info.card.el.dom;
2725             
2726             if (info.position == 'above') {
2727                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2728             } else if (cardel.nextSibling) {
2729                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2730             } else {
2731                 cardel.parentNode.append(this.dropEl.dom);
2732             }
2733         } else {
2734             // card container???
2735             this.containerEl.dom.append(this.dropEl.dom);
2736         }
2737         
2738         this.dropEl.addClass('d-block roo-card-dropzone');
2739         
2740         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2741         
2742         
2743     
2744     
2745     
2746     },
2747     setHeaderText: function(html)
2748     {
2749         this.header = html;
2750         if (this.headerContainerEl) {
2751             this.headerContainerEl.dom.innerHTML = html;
2752         }
2753     },
2754     onHeaderImageLoad : function(ev, he)
2755     {
2756         if (!this.header_image_fit_square) {
2757             return;
2758         }
2759         
2760         var hw = he.naturalHeight / he.naturalWidth;
2761         // wide image = < 0
2762         // tall image = > 1
2763         //var w = he.dom.naturalWidth;
2764         var ww = he.width;
2765         he.style.left =  0;
2766         he.style.position =  'relative';
2767         if (hw > 1) {
2768             var nw = (ww * (1/hw));
2769             Roo.get(he).setSize( ww * (1/hw),  ww);
2770             he.style.left =  ((ww - nw)/ 2) + 'px';
2771             he.style.position =  'relative';
2772         }
2773
2774     }
2775
2776     
2777 });
2778
2779 /*
2780  * - LGPL
2781  *
2782  * Card header - holder for the card header elements.
2783  * 
2784  */
2785
2786 /**
2787  * @class Roo.bootstrap.CardHeader
2788  * @extends Roo.bootstrap.Element
2789  * @parent Roo.bootstrap.Card
2790  * @children Roo.bootstrap.Component
2791  * Bootstrap CardHeader class
2792  * @constructor
2793  * Create a new Card Header - that you can embed children into
2794  * @param {Object} config The config object
2795  */
2796
2797 Roo.bootstrap.CardHeader = function(config){
2798     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2799 };
2800
2801 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2802     
2803     
2804     container_method : 'getCardHeader' 
2805     
2806      
2807     
2808     
2809    
2810 });
2811
2812  
2813
2814  /*
2815  * - LGPL
2816  *
2817  * Card footer - holder for the card footer elements.
2818  * 
2819  */
2820
2821 /**
2822  * @class Roo.bootstrap.CardFooter
2823  * @extends Roo.bootstrap.Element
2824  * @parent Roo.bootstrap.Card
2825  * @children Roo.bootstrap.Component
2826  * Bootstrap CardFooter class
2827  * 
2828  * @constructor
2829  * Create a new Card Footer - that you can embed children into
2830  * @param {Object} config The config object
2831  */
2832
2833 Roo.bootstrap.CardFooter = function(config){
2834     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2835 };
2836
2837 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2838     
2839     
2840     container_method : 'getCardFooter' 
2841     
2842      
2843     
2844     
2845    
2846 });
2847
2848  
2849
2850  /*
2851  * - LGPL
2852  *
2853  * Card header - holder for the card header elements.
2854  * 
2855  */
2856
2857 /**
2858  * @class Roo.bootstrap.CardImageTop
2859  * @extends Roo.bootstrap.Element
2860  * @parent Roo.bootstrap.Card
2861  * @children Roo.bootstrap.Component
2862  * Bootstrap CardImageTop class
2863  * 
2864  * @constructor
2865  * Create a new Card Image Top container
2866  * @param {Object} config The config object
2867  */
2868
2869 Roo.bootstrap.CardImageTop = function(config){
2870     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2871 };
2872
2873 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2874     
2875    
2876     container_method : 'getCardImageTop' 
2877     
2878      
2879     
2880    
2881 });
2882
2883  
2884
2885  
2886 /*
2887 * Licence: LGPL
2888 */
2889
2890 /**
2891  * @class Roo.bootstrap.ButtonUploader
2892  * @extends Roo.bootstrap.Button
2893  * Bootstrap Button Uploader class - it's a button which when you add files to it
2894  *
2895  * 
2896  * @cfg {Number} errorTimeout default 3000
2897  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2898  * @cfg {Array}  html The button text.
2899  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2900  *
2901  * @constructor
2902  * Create a new CardUploader
2903  * @param {Object} config The config object
2904  */
2905
2906 Roo.bootstrap.ButtonUploader = function(config){
2907     
2908  
2909     
2910     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2911     
2912      
2913      this.addEvents({
2914          // raw events
2915         /**
2916          * @event beforeselect
2917          * When button is pressed, before show upload files dialog is shown
2918          * @param {Roo.bootstrap.UploaderButton} this
2919          *
2920          */
2921         'beforeselect' : true,
2922          /**
2923          * @event fired when files have been selected, 
2924          * When a the download link is clicked
2925          * @param {Roo.bootstrap.UploaderButton} this
2926          * @param {Array} Array of files that have been uploaded
2927          */
2928         'uploaded' : true
2929         
2930     });
2931 };
2932  
2933 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2934     
2935      
2936     errorTimeout : 3000,
2937      
2938     images : false,
2939    
2940     fileCollection : false,
2941     allowBlank : true,
2942     
2943     multiple : true,
2944     
2945     getAutoCreate : function()
2946     {
2947        
2948         
2949         return  {
2950             cls :'div' ,
2951             cn : [
2952                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2953             ]
2954         };
2955            
2956          
2957     },
2958      
2959    
2960     initEvents : function()
2961     {
2962         
2963         Roo.bootstrap.Button.prototype.initEvents.call(this);
2964         
2965         
2966         
2967         
2968         
2969         this.urlAPI = (window.createObjectURL && window) || 
2970                                 (window.URL && URL.revokeObjectURL && URL) || 
2971                                 (window.webkitURL && webkitURL);
2972                         
2973         var im = {
2974             tag: 'input',
2975             type : 'file',
2976             cls : 'd-none  roo-card-upload-selector' 
2977           
2978         };
2979         if (this.multiple) {
2980             im.multiple = 'multiple';
2981         }
2982         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2983        
2984         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2985         
2986         this.selectorEl.on('change', this.onFileSelected, this);
2987          
2988          
2989        
2990     },
2991     
2992    
2993     onClick : function(e)
2994     {
2995         e.preventDefault();
2996         
2997         if ( this.fireEvent('beforeselect', this) === false) {
2998             return;
2999         }
3000          
3001         this.selectorEl.dom.click();
3002          
3003     },
3004     
3005     onFileSelected : function(e)
3006     {
3007         e.preventDefault();
3008         
3009         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3010             return;
3011         }
3012         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3013         this.selectorEl.dom.value  = '';// hopefully reset..
3014         
3015         this.fireEvent('uploaded', this,  files );
3016         
3017     },
3018     
3019        
3020    
3021     
3022     /**
3023      * addCard - add an Attachment to the uploader
3024      * @param data - the data about the image to upload
3025      *
3026      * {
3027           id : 123
3028           title : "Title of file",
3029           is_uploaded : false,
3030           src : "http://.....",
3031           srcfile : { the File upload object },
3032           mimetype : file.type,
3033           preview : false,
3034           is_deleted : 0
3035           .. any other data...
3036         }
3037      *
3038      * 
3039     */
3040      
3041     reset: function()
3042     {
3043          
3044          this.selectorEl
3045     } 
3046     
3047     
3048     
3049     
3050 });
3051  /*
3052  * - LGPL
3053  *
3054  * image
3055  * 
3056  */
3057
3058
3059 /**
3060  * @class Roo.bootstrap.Img
3061  * @extends Roo.bootstrap.Component
3062  * Bootstrap Img class
3063  * @cfg {Boolean} imgResponsive false | true
3064  * @cfg {String} border rounded | circle | thumbnail
3065  * @cfg {String} src image source
3066  * @cfg {String} alt image alternative text
3067  * @cfg {String} href a tag href
3068  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3069  * @cfg {String} xsUrl xs image source
3070  * @cfg {String} smUrl sm image source
3071  * @cfg {String} mdUrl md image source
3072  * @cfg {String} lgUrl lg image source
3073  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3074  * 
3075  * @constructor
3076  * Create a new Input
3077  * @param {Object} config The config object
3078  */
3079
3080 Roo.bootstrap.Img = function(config){
3081     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3082     
3083     this.addEvents({
3084         // img events
3085         /**
3086          * @event click
3087          * The img click event for the img.
3088          * @param {Roo.EventObject} e
3089          */
3090         "click" : true,
3091         /**
3092          * @event load
3093          * The when any image loads
3094          * @param {Roo.EventObject} e
3095          */
3096         "load" : true
3097     });
3098 };
3099
3100 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3101     
3102     imgResponsive: true,
3103     border: '',
3104     src: 'about:blank',
3105     href: false,
3106     target: false,
3107     xsUrl: '',
3108     smUrl: '',
3109     mdUrl: '',
3110     lgUrl: '',
3111     backgroundContain : false,
3112
3113     getAutoCreate : function()
3114     {   
3115         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3116             return this.createSingleImg();
3117         }
3118         
3119         var cfg = {
3120             tag: 'div',
3121             cls: 'roo-image-responsive-group',
3122             cn: []
3123         };
3124         var _this = this;
3125         
3126         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3127             
3128             if(!_this[size + 'Url']){
3129                 return;
3130             }
3131             
3132             var img = {
3133                 tag: 'img',
3134                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3135                 html: _this.html || cfg.html,
3136                 src: _this[size + 'Url']
3137             };
3138             
3139             img.cls += ' roo-image-responsive-' + size;
3140             
3141             var s = ['xs', 'sm', 'md', 'lg'];
3142             
3143             s.splice(s.indexOf(size), 1);
3144             
3145             Roo.each(s, function(ss){
3146                 img.cls += ' hidden-' + ss;
3147             });
3148             
3149             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3150                 cfg.cls += ' img-' + _this.border;
3151             }
3152             
3153             if(_this.alt){
3154                 cfg.alt = _this.alt;
3155             }
3156             
3157             if(_this.href){
3158                 var a = {
3159                     tag: 'a',
3160                     href: _this.href,
3161                     cn: [
3162                         img
3163                     ]
3164                 };
3165
3166                 if(this.target){
3167                     a.target = _this.target;
3168                 }
3169             }
3170             
3171             cfg.cn.push((_this.href) ? a : img);
3172             
3173         });
3174         
3175         return cfg;
3176     },
3177     
3178     createSingleImg : function()
3179     {
3180         var cfg = {
3181             tag: 'img',
3182             cls: (this.imgResponsive) ? 'img-responsive' : '',
3183             html : null,
3184             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3185         };
3186         
3187         if (this.backgroundContain) {
3188             cfg.cls += ' background-contain';
3189         }
3190         
3191         cfg.html = this.html || cfg.html;
3192         
3193         if (this.backgroundContain) {
3194             cfg.style="background-image: url(" + this.src + ')';
3195         } else {
3196             cfg.src = this.src || cfg.src;
3197         }
3198         
3199         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3200             cfg.cls += ' img-' + this.border;
3201         }
3202         
3203         if(this.alt){
3204             cfg.alt = this.alt;
3205         }
3206         
3207         if(this.href){
3208             var a = {
3209                 tag: 'a',
3210                 href: this.href,
3211                 cn: [
3212                     cfg
3213                 ]
3214             };
3215             
3216             if(this.target){
3217                 a.target = this.target;
3218             }
3219             
3220         }
3221         
3222         return (this.href) ? a : cfg;
3223     },
3224     
3225     initEvents: function() 
3226     {
3227         if(!this.href){
3228             this.el.on('click', this.onClick, this);
3229         }
3230         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3231             this.el.on('load', this.onImageLoad, this);
3232         } else {
3233             // not sure if this works.. not tested
3234             this.el.select('img', true).on('load', this.onImageLoad, this);
3235         }
3236         
3237     },
3238     
3239     onClick : function(e)
3240     {
3241         Roo.log('img onclick');
3242         this.fireEvent('click', this, e);
3243     },
3244     onImageLoad: function(e)
3245     {
3246         Roo.log('img load');
3247         this.fireEvent('load', this, e);
3248     },
3249     
3250     /**
3251      * Sets the url of the image - used to update it
3252      * @param {String} url the url of the image
3253      */
3254     
3255     setSrc : function(url)
3256     {
3257         this.src =  url;
3258         
3259         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3260             if (this.backgroundContain) {
3261                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3262             } else {
3263                 this.el.dom.src =  url;
3264             }
3265             return;
3266         }
3267         
3268         this.el.select('img', true).first().dom.src =  url;
3269     }
3270     
3271     
3272    
3273 });
3274
3275  /*
3276  * - LGPL
3277  *
3278  * image
3279  * 
3280  */
3281
3282
3283 /**
3284  * @class Roo.bootstrap.Link
3285  * @extends Roo.bootstrap.Component
3286  * @children Roo.bootstrap.Component
3287  * Bootstrap Link Class (eg. '<a href>')
3288  
3289  * @cfg {String} alt image alternative text
3290  * @cfg {String} href a tag href
3291  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3292  * @cfg {String} html the content of the link.
3293  * @cfg {String} anchor name for the anchor link
3294  * @cfg {String} fa - favicon
3295
3296  * @cfg {Boolean} preventDefault (true | false) default false
3297
3298  * 
3299  * @constructor
3300  * Create a new Input
3301  * @param {Object} config The config object
3302  */
3303
3304 Roo.bootstrap.Link = function(config){
3305     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3306     
3307     this.addEvents({
3308         // img events
3309         /**
3310          * @event click
3311          * The img click event for the img.
3312          * @param {Roo.EventObject} e
3313          */
3314         "click" : true
3315     });
3316 };
3317
3318 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3319     
3320     href: false,
3321     target: false,
3322     preventDefault: false,
3323     anchor : false,
3324     alt : false,
3325     fa: false,
3326
3327
3328     getAutoCreate : function()
3329     {
3330         var html = this.html || '';
3331         
3332         if (this.fa !== false) {
3333             html = '<i class="fa fa-' + this.fa + '"></i>';
3334         }
3335         var cfg = {
3336             tag: 'a'
3337         };
3338         // anchor's do not require html/href...
3339         if (this.anchor === false) {
3340             cfg.html = html;
3341             cfg.href = this.href || '#';
3342         } else {
3343             cfg.name = this.anchor;
3344             if (this.html !== false || this.fa !== false) {
3345                 cfg.html = html;
3346             }
3347             if (this.href !== false) {
3348                 cfg.href = this.href;
3349             }
3350         }
3351         
3352         if(this.alt !== false){
3353             cfg.alt = this.alt;
3354         }
3355         
3356         
3357         if(this.target !== false) {
3358             cfg.target = this.target;
3359         }
3360         
3361         return cfg;
3362     },
3363     
3364     initEvents: function() {
3365         
3366         if(!this.href || this.preventDefault){
3367             this.el.on('click', this.onClick, this);
3368         }
3369     },
3370     
3371     onClick : function(e)
3372     {
3373         if(this.preventDefault){
3374             e.preventDefault();
3375         }
3376         //Roo.log('img onclick');
3377         this.fireEvent('click', this, e);
3378     }
3379    
3380 });
3381
3382  /*
3383  * - LGPL
3384  *
3385  * header
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.Header
3391  * @extends Roo.bootstrap.Component
3392  * @children Roo.bootstrap.Component
3393  * Bootstrap Header class
3394  *
3395  * 
3396  * @cfg {String} html content of header
3397  * @cfg {Number} level (1|2|3|4|5|6) default 1
3398  * 
3399  * @constructor
3400  * Create a new Header
3401  * @param {Object} config The config object
3402  */
3403
3404
3405 Roo.bootstrap.Header  = function(config){
3406     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3407 };
3408
3409 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3410     
3411     //href : false,
3412     html : false,
3413     level : 1,
3414     
3415     
3416     
3417     getAutoCreate : function(){
3418         
3419         
3420         
3421         var cfg = {
3422             tag: 'h' + (1 *this.level),
3423             html: this.html || ''
3424         } ;
3425         
3426         return cfg;
3427     }
3428    
3429 });
3430
3431  
3432
3433  /**
3434  * @class Roo.bootstrap.MenuMgr
3435  * @licence LGPL
3436  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3437  * @static
3438  */
3439 Roo.bootstrap.menu.Manager = function(){
3440    var menus, active, groups = {}, attached = false, lastShow = new Date();
3441
3442    // private - called when first menu is created
3443    function init(){
3444        menus = {};
3445        active = new Roo.util.MixedCollection();
3446        Roo.get(document).addKeyListener(27, function(){
3447            if(active.length > 0){
3448                hideAll();
3449            }
3450        });
3451    }
3452
3453    // private
3454    function hideAll(){
3455        if(active && active.length > 0){
3456            var c = active.clone();
3457            c.each(function(m){
3458                m.hide();
3459            });
3460        }
3461    }
3462
3463    // private
3464    function onHide(m){
3465        active.remove(m);
3466        if(active.length < 1){
3467            Roo.get(document).un("mouseup", onMouseDown);
3468             
3469            attached = false;
3470        }
3471    }
3472
3473    // private
3474    function onShow(m){
3475        var last = active.last();
3476        lastShow = new Date();
3477        active.add(m);
3478        if(!attached){
3479           Roo.get(document).on("mouseup", onMouseDown);
3480            
3481            attached = true;
3482        }
3483        if(m.parentMenu){
3484           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3485           m.parentMenu.activeChild = m;
3486        }else if(last && last.isVisible()){
3487           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3488        }
3489    }
3490
3491    // private
3492    function onBeforeHide(m){
3493        if(m.activeChild){
3494            m.activeChild.hide();
3495        }
3496        if(m.autoHideTimer){
3497            clearTimeout(m.autoHideTimer);
3498            delete m.autoHideTimer;
3499        }
3500    }
3501
3502    // private
3503    function onBeforeShow(m){
3504        var pm = m.parentMenu;
3505        if(!pm && !m.allowOtherMenus){
3506            hideAll();
3507        }else if(pm && pm.activeChild && active != m){
3508            pm.activeChild.hide();
3509        }
3510    }
3511
3512    // private this should really trigger on mouseup..
3513    function onMouseDown(e){
3514         Roo.log("on Mouse Up");
3515         
3516         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3517             Roo.log("MenuManager hideAll");
3518             hideAll();
3519             e.stopEvent();
3520         }
3521         
3522         
3523    }
3524
3525    // private
3526    function onBeforeCheck(mi, state){
3527        if(state){
3528            var g = groups[mi.group];
3529            for(var i = 0, l = g.length; i < l; i++){
3530                if(g[i] != mi){
3531                    g[i].setChecked(false);
3532                }
3533            }
3534        }
3535    }
3536
3537    return {
3538
3539        /**
3540         * Hides all menus that are currently visible
3541         */
3542        hideAll : function(){
3543             hideAll();  
3544        },
3545
3546        // private
3547        register : function(menu){
3548            if(!menus){
3549                init();
3550            }
3551            menus[menu.id] = menu;
3552            menu.on("beforehide", onBeforeHide);
3553            menu.on("hide", onHide);
3554            menu.on("beforeshow", onBeforeShow);
3555            menu.on("show", onShow);
3556            var g = menu.group;
3557            if(g && menu.events["checkchange"]){
3558                if(!groups[g]){
3559                    groups[g] = [];
3560                }
3561                groups[g].push(menu);
3562                menu.on("checkchange", onCheck);
3563            }
3564        },
3565
3566         /**
3567          * Returns a {@link Roo.menu.Menu} object
3568          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3569          * be used to generate and return a new Menu instance.
3570          */
3571        get : function(menu){
3572            if(typeof menu == "string"){ // menu id
3573                return menus[menu];
3574            }else if(menu.events){  // menu instance
3575                return menu;
3576            }
3577            /*else if(typeof menu.length == 'number'){ // array of menu items?
3578                return new Roo.bootstrap.Menu({items:menu});
3579            }else{ // otherwise, must be a config
3580                return new Roo.bootstrap.Menu(menu);
3581            }
3582            */
3583            return false;
3584        },
3585
3586        // private
3587        unregister : function(menu){
3588            delete menus[menu.id];
3589            menu.un("beforehide", onBeforeHide);
3590            menu.un("hide", onHide);
3591            menu.un("beforeshow", onBeforeShow);
3592            menu.un("show", onShow);
3593            var g = menu.group;
3594            if(g && menu.events["checkchange"]){
3595                groups[g].remove(menu);
3596                menu.un("checkchange", onCheck);
3597            }
3598        },
3599
3600        // private
3601        registerCheckable : function(menuItem){
3602            var g = menuItem.group;
3603            if(g){
3604                if(!groups[g]){
3605                    groups[g] = [];
3606                }
3607                groups[g].push(menuItem);
3608                menuItem.on("beforecheckchange", onBeforeCheck);
3609            }
3610        },
3611
3612        // private
3613        unregisterCheckable : function(menuItem){
3614            var g = menuItem.group;
3615            if(g){
3616                groups[g].remove(menuItem);
3617                menuItem.un("beforecheckchange", onBeforeCheck);
3618            }
3619        }
3620    };
3621 }(); 
3622 /**
3623  * @class Roo.bootstrap.menu.Menu
3624  * @extends Roo.bootstrap.Component
3625  * @licence LGPL
3626  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3627  * @parent none
3628  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3629  * 
3630  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3631  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3632  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3633  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3634 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3635 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3636  
3637  * @constructor
3638  * Create a new Menu
3639  * @param {Object} config The config objectQ
3640  */
3641
3642
3643 Roo.bootstrap.menu.Menu = function(config){
3644     
3645     if (config.type == 'treeview') {
3646         // normally menu's are drawn attached to the document to handle layering etc..
3647         // however treeview (used by the docs menu is drawn into the parent element)
3648         this.container_method = 'getChildContainer'; 
3649     }
3650     
3651     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3652     if (this.registerMenu && this.type != 'treeview')  {
3653         Roo.bootstrap.menu.Manager.register(this);
3654     }
3655     
3656     
3657     this.addEvents({
3658         /**
3659          * @event beforeshow
3660          * Fires before this menu is displayed (return false to block)
3661          * @param {Roo.menu.Menu} this
3662          */
3663         beforeshow : true,
3664         /**
3665          * @event beforehide
3666          * Fires before this menu is hidden (return false to block)
3667          * @param {Roo.menu.Menu} this
3668          */
3669         beforehide : true,
3670         /**
3671          * @event show
3672          * Fires after this menu is displayed
3673          * @param {Roo.menu.Menu} this
3674          */
3675         show : true,
3676         /**
3677          * @event hide
3678          * Fires after this menu is hidden
3679          * @param {Roo.menu.Menu} this
3680          */
3681         hide : true,
3682         /**
3683          * @event click
3684          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3685          * @param {Roo.menu.Menu} this
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          * @param {Roo.EventObject} e
3688          */
3689         click : true,
3690         /**
3691          * @event mouseover
3692          * Fires when the mouse is hovering over this menu
3693          * @param {Roo.menu.Menu} this
3694          * @param {Roo.EventObject} e
3695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3696          */
3697         mouseover : true,
3698         /**
3699          * @event mouseout
3700          * Fires when the mouse exits this menu
3701          * @param {Roo.menu.Menu} this
3702          * @param {Roo.EventObject} e
3703          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3704          */
3705         mouseout : true,
3706         /**
3707          * @event itemclick
3708          * Fires when a menu item contained in this menu is clicked
3709          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3710          * @param {Roo.EventObject} e
3711          */
3712         itemclick: true
3713     });
3714     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3715 };
3716
3717 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3718     
3719    /// html : false,
3720    
3721     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3722     type: false,
3723     /**
3724      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3725      */
3726     registerMenu : true,
3727     
3728     menuItems :false, // stores the menu items..
3729     
3730     hidden:true,
3731         
3732     parentMenu : false,
3733     
3734     stopEvent : true,
3735     
3736     isLink : false,
3737     
3738     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3739     
3740     hideTrigger : false,
3741     
3742     align : 'tl-bl?',
3743     
3744     
3745     getChildContainer : function() {
3746         return this.el;  
3747     },
3748     
3749     getAutoCreate : function(){
3750          
3751         //if (['right'].indexOf(this.align)!==-1) {
3752         //    cfg.cn[1].cls += ' pull-right'
3753         //}
3754          
3755         var cfg = {
3756             tag : 'ul',
3757             cls : 'dropdown-menu shadow' ,
3758             style : 'z-index:1000'
3759             
3760         };
3761         
3762         if (this.type === 'submenu') {
3763             cfg.cls = 'submenu active';
3764         }
3765         if (this.type === 'treeview') {
3766             cfg.cls = 'treeview-menu';
3767         }
3768         
3769         return cfg;
3770     },
3771     initEvents : function() {
3772         
3773        // Roo.log("ADD event");
3774        // Roo.log(this.triggerEl.dom);
3775         if (this.triggerEl) {
3776             
3777             this.triggerEl.on('click', this.onTriggerClick, this);
3778             
3779             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3780             
3781             if (!this.hideTrigger) {
3782                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3783                     // dropdown toggle on the 'a' in BS4?
3784                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3785                 } else {
3786                     this.triggerEl.addClass('dropdown-toggle');
3787                 }
3788             }
3789         }
3790         
3791         if (Roo.isTouch) {
3792             this.el.on('touchstart'  , this.onTouch, this);
3793         }
3794         this.el.on('click' , this.onClick, this);
3795
3796         this.el.on("mouseover", this.onMouseOver, this);
3797         this.el.on("mouseout", this.onMouseOut, this);
3798         
3799     },
3800     
3801     findTargetItem : function(e)
3802     {
3803         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3804         if(!t){
3805             return false;
3806         }
3807         //Roo.log(t);         Roo.log(t.id);
3808         if(t && t.id){
3809             //Roo.log(this.menuitems);
3810             return this.menuitems.get(t.id);
3811             
3812             //return this.items.get(t.menuItemId);
3813         }
3814         
3815         return false;
3816     },
3817     
3818     onTouch : function(e) 
3819     {
3820         Roo.log("menu.onTouch");
3821         //e.stopEvent(); this make the user popdown broken
3822         this.onClick(e);
3823     },
3824     
3825     onClick : function(e)
3826     {
3827         Roo.log("menu.onClick");
3828         
3829         var t = this.findTargetItem(e);
3830         if(!t || t.isContainer){
3831             return;
3832         }
3833         Roo.log(e);
3834         /*
3835         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3836             if(t == this.activeItem && t.shouldDeactivate(e)){
3837                 this.activeItem.deactivate();
3838                 delete this.activeItem;
3839                 return;
3840             }
3841             if(t.canActivate){
3842                 this.setActiveItem(t, true);
3843             }
3844             return;
3845             
3846             
3847         }
3848         */
3849        
3850         Roo.log('pass click event');
3851         
3852         t.onClick(e);
3853         
3854         this.fireEvent("click", this, t, e);
3855         
3856         var _this = this;
3857         
3858         if(!t.href.length || t.href == '#'){
3859             (function() { _this.hide(); }).defer(100);
3860         }
3861         
3862     },
3863     
3864     onMouseOver : function(e){
3865         var t  = this.findTargetItem(e);
3866         //Roo.log(t);
3867         //if(t){
3868         //    if(t.canActivate && !t.disabled){
3869         //        this.setActiveItem(t, true);
3870         //    }
3871         //}
3872         
3873         this.fireEvent("mouseover", this, e, t);
3874     },
3875     isVisible : function(){
3876         return !this.hidden;
3877     },
3878     onMouseOut : function(e){
3879         var t  = this.findTargetItem(e);
3880         
3881         //if(t ){
3882         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3883         //        this.activeItem.deactivate();
3884         //        delete this.activeItem;
3885         //    }
3886         //}
3887         this.fireEvent("mouseout", this, e, t);
3888     },
3889     
3890     
3891     /**
3892      * Displays this menu relative to another element
3893      * @param {String/HTMLElement/Roo.Element} element The element to align to
3894      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3895      * the element (defaults to this.defaultAlign)
3896      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3897      */
3898     show : function(el, pos, parentMenu)
3899     {
3900         if (false === this.fireEvent("beforeshow", this)) {
3901             Roo.log("show canceled");
3902             return;
3903         }
3904         this.parentMenu = parentMenu;
3905         if(!this.el){
3906             this.render();
3907         }
3908         this.el.addClass('show'); // show otherwise we do not know how big we are..
3909          
3910         var xy = this.el.getAlignToXY(el, pos);
3911         
3912         // bl-tl << left align  below
3913         // tl-bl << left align 
3914         
3915         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3916             // if it goes to far to the right.. -> align left.
3917             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3918         }
3919         if(xy[0] < 0){
3920             // was left align - go right?
3921             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3922         }
3923         
3924         // goes down the bottom
3925         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3926            xy[1]  < 0 ){
3927             var a = this.align.replace('?', '').split('-');
3928             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3929             
3930         }
3931         
3932         this.showAt(  xy , parentMenu, false);
3933     },
3934      /**
3935      * Displays this menu at a specific xy position
3936      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3937      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3938      */
3939     showAt : function(xy, parentMenu, /* private: */_e){
3940         this.parentMenu = parentMenu;
3941         if(!this.el){
3942             this.render();
3943         }
3944         if(_e !== false){
3945             this.fireEvent("beforeshow", this);
3946             //xy = this.el.adjustForConstraints(xy);
3947         }
3948         
3949         //this.el.show();
3950         this.hideMenuItems();
3951         this.hidden = false;
3952         if (this.triggerEl) {
3953             this.triggerEl.addClass('open');
3954         }
3955         
3956         this.el.addClass('show');
3957         
3958         
3959         
3960         // reassign x when hitting right
3961         
3962         // reassign y when hitting bottom
3963         
3964         // but the list may align on trigger left or trigger top... should it be a properity?
3965         
3966         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3967             this.el.setXY(xy);
3968         }
3969         
3970         this.focus();
3971         this.fireEvent("show", this);
3972     },
3973     
3974     focus : function(){
3975         return;
3976         if(!this.hidden){
3977             this.doFocus.defer(50, this);
3978         }
3979     },
3980
3981     doFocus : function(){
3982         if(!this.hidden){
3983             this.focusEl.focus();
3984         }
3985     },
3986
3987     /**
3988      * Hides this menu and optionally all parent menus
3989      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3990      */
3991     hide : function(deep)
3992     {
3993         if (false === this.fireEvent("beforehide", this)) {
3994             Roo.log("hide canceled");
3995             return;
3996         }
3997         this.hideMenuItems();
3998         if(this.el && this.isVisible()){
3999            
4000             if(this.activeItem){
4001                 this.activeItem.deactivate();
4002                 this.activeItem = null;
4003             }
4004             if (this.triggerEl) {
4005                 this.triggerEl.removeClass('open');
4006             }
4007             
4008             this.el.removeClass('show');
4009             this.hidden = true;
4010             this.fireEvent("hide", this);
4011         }
4012         if(deep === true && this.parentMenu){
4013             this.parentMenu.hide(true);
4014         }
4015     },
4016     
4017     onTriggerClick : function(e)
4018     {
4019         Roo.log('trigger click');
4020         
4021         var target = e.getTarget();
4022         
4023         Roo.log(target.nodeName.toLowerCase());
4024         
4025         if(target.nodeName.toLowerCase() === 'i'){
4026             e.preventDefault();
4027         }
4028         
4029     },
4030     
4031     onTriggerPress  : function(e)
4032     {
4033         Roo.log('trigger press');
4034         //Roo.log(e.getTarget());
4035        // Roo.log(this.triggerEl.dom);
4036        
4037         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4038         var pel = Roo.get(e.getTarget());
4039         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4040             Roo.log('is treeview or dropdown?');
4041             return;
4042         }
4043         
4044         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4045             return;
4046         }
4047         
4048         if (this.isVisible()) {
4049             Roo.log('hide');
4050             this.hide();
4051         } else {
4052             Roo.log('show');
4053             
4054             this.show(this.triggerEl, this.align, false);
4055         }
4056         
4057         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4058             e.stopEvent();
4059         }
4060         
4061     },
4062        
4063     
4064     hideMenuItems : function()
4065     {
4066         Roo.log("hide Menu Items");
4067         if (!this.el) { 
4068             return;
4069         }
4070         
4071         this.el.select('.open',true).each(function(aa) {
4072             
4073             aa.removeClass('open');
4074          
4075         });
4076     },
4077     addxtypeChild : function (tree, cntr) {
4078         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4079           
4080         this.menuitems.add(comp);
4081         return comp;
4082
4083     },
4084     getEl : function()
4085     {
4086         Roo.log(this.el);
4087         return this.el;
4088     },
4089     
4090     clear : function()
4091     {
4092         this.getEl().dom.innerHTML = '';
4093         this.menuitems.clear();
4094     }
4095 });
4096
4097  
4098  /**
4099  * @class Roo.bootstrap.menu.Item
4100  * @extends Roo.bootstrap.Component
4101  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4102  * @parent Roo.bootstrap.menu.Menu
4103  * @licence LGPL
4104  * Bootstrap MenuItem class
4105  * 
4106  * @cfg {String} html the menu label
4107  * @cfg {String} href the link
4108  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4109  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4110  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4111  * @cfg {String} fa favicon to show on left of menu item.
4112  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4113  * 
4114  * 
4115  * @constructor
4116  * Create a new MenuItem
4117  * @param {Object} config The config object
4118  */
4119
4120
4121 Roo.bootstrap.menu.Item = function(config){
4122     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4123     this.addEvents({
4124         // raw events
4125         /**
4126          * @event click
4127          * The raw click event for the entire grid.
4128          * @param {Roo.bootstrap.menu.Item} this
4129          * @param {Roo.EventObject} e
4130          */
4131         "click" : true
4132     });
4133 };
4134
4135 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4136     
4137     href : false,
4138     html : false,
4139     preventDefault: false,
4140     isContainer : false,
4141     active : false,
4142     fa: false,
4143     
4144     getAutoCreate : function(){
4145         
4146         if(this.isContainer){
4147             return {
4148                 tag: 'li',
4149                 cls: 'dropdown-menu-item '
4150             };
4151         }
4152         var ctag = {
4153             tag: 'span',
4154             html: 'Link'
4155         };
4156         
4157         var anc = {
4158             tag : 'a',
4159             cls : 'dropdown-item',
4160             href : '#',
4161             cn : [  ]
4162         };
4163         
4164         if (this.fa !== false) {
4165             anc.cn.push({
4166                 tag : 'i',
4167                 cls : 'fa fa-' + this.fa
4168             });
4169         }
4170         
4171         anc.cn.push(ctag);
4172         
4173         
4174         var cfg= {
4175             tag: 'li',
4176             cls: 'dropdown-menu-item',
4177             cn: [ anc ]
4178         };
4179         if (this.parent().type == 'treeview') {
4180             cfg.cls = 'treeview-menu';
4181         }
4182         if (this.active) {
4183             cfg.cls += ' active';
4184         }
4185         
4186         
4187         
4188         anc.href = this.href || cfg.cn[0].href ;
4189         ctag.html = this.html || cfg.cn[0].html ;
4190         return cfg;
4191     },
4192     
4193     initEvents: function()
4194     {
4195         if (this.parent().type == 'treeview') {
4196             this.el.select('a').on('click', this.onClick, this);
4197         }
4198         
4199         if (this.menu) {
4200             this.menu.parentType = this.xtype;
4201             this.menu.triggerEl = this.el;
4202             this.menu = this.addxtype(Roo.apply({}, this.menu));
4203         }
4204         
4205     },
4206     onClick : function(e)
4207     {
4208         //Roo.log('item on click ');
4209         
4210         if(this.href === false || this.preventDefault){
4211             e.preventDefault();
4212         }
4213         //this.parent().hideMenuItems();
4214         
4215         this.fireEvent('click', this, e);
4216     },
4217     getEl : function()
4218     {
4219         return this.el;
4220     } 
4221 });
4222
4223  
4224
4225  
4226
4227   
4228 /**
4229  * @class Roo.bootstrap.menu.Separator
4230  * @extends Roo.bootstrap.Component
4231  * @licence LGPL
4232  * @parent Roo.bootstrap.menu.Menu
4233  * Bootstrap Separator class
4234  * 
4235  * @constructor
4236  * Create a new Separator
4237  * @param {Object} config The config object
4238  */
4239
4240
4241 Roo.bootstrap.menu.Separator = function(config){
4242     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4243 };
4244
4245 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4246     
4247     getAutoCreate : function(){
4248         var cfg = {
4249             tag : 'li',
4250             cls: 'dropdown-divider divider'
4251         };
4252         
4253         return cfg;
4254     }
4255    
4256 });
4257
4258  
4259
4260  
4261 /*
4262 * Licence: LGPL
4263 */
4264
4265 /**
4266  * @class Roo.bootstrap.Modal
4267  * @extends Roo.bootstrap.Component
4268  * @parent none builder
4269  * @children Roo.bootstrap.Component
4270  * Bootstrap Modal class
4271  * @cfg {String} title Title of dialog
4272  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4273  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4274  * @cfg {Boolean} specificTitle default false
4275  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4276  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4277  * @cfg {Boolean} animate default true
4278  * @cfg {Boolean} allow_close default true
4279  * @cfg {Boolean} fitwindow default false
4280  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4281  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4282  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4283  * @cfg {String} size (sm|lg|xl) default empty
4284  * @cfg {Number} max_width set the max width of modal
4285  * @cfg {Boolean} editableTitle can the title be edited
4286
4287  *
4288  *
4289  * @constructor
4290  * Create a new Modal Dialog
4291  * @param {Object} config The config object
4292  */
4293
4294 Roo.bootstrap.Modal = function(config){
4295     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4296     this.addEvents({
4297         // raw events
4298         /**
4299          * @event btnclick
4300          * The raw btnclick event for the button
4301          * @param {Roo.EventObject} e
4302          */
4303         "btnclick" : true,
4304         /**
4305          * @event resize
4306          * Fire when dialog resize
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} e
4309          */
4310         "resize" : true,
4311         /**
4312          * @event titlechanged
4313          * Fire when the editable title has been changed
4314          * @param {Roo.bootstrap.Modal} this
4315          * @param {Roo.EventObject} value
4316          */
4317         "titlechanged" : true 
4318         
4319     });
4320     this.buttons = this.buttons || [];
4321
4322     if (this.tmpl) {
4323         this.tmpl = Roo.factory(this.tmpl);
4324     }
4325
4326 };
4327
4328 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4329
4330     title : 'test dialog',
4331
4332     buttons : false,
4333
4334     // set on load...
4335
4336     html: false,
4337
4338     tmp: false,
4339
4340     specificTitle: false,
4341
4342     buttonPosition: 'right',
4343
4344     allow_close : true,
4345
4346     animate : true,
4347
4348     fitwindow: false,
4349     
4350      // private
4351     dialogEl: false,
4352     bodyEl:  false,
4353     footerEl:  false,
4354     titleEl:  false,
4355     closeEl:  false,
4356
4357     size: '',
4358     
4359     max_width: 0,
4360     
4361     max_height: 0,
4362     
4363     fit_content: false,
4364     editableTitle  : false,
4365
4366     onRender : function(ct, position)
4367     {
4368         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4369
4370         if(!this.el){
4371             var cfg = Roo.apply({},  this.getAutoCreate());
4372             cfg.id = Roo.id();
4373             //if(!cfg.name){
4374             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4375             //}
4376             //if (!cfg.name.length) {
4377             //    delete cfg.name;
4378            // }
4379             if (this.cls) {
4380                 cfg.cls += ' ' + this.cls;
4381             }
4382             if (this.style) {
4383                 cfg.style = this.style;
4384             }
4385             this.el = Roo.get(document.body).createChild(cfg, position);
4386         }
4387         //var type = this.el.dom.type;
4388
4389
4390         if(this.tabIndex !== undefined){
4391             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4392         }
4393
4394         this.dialogEl = this.el.select('.modal-dialog',true).first();
4395         this.bodyEl = this.el.select('.modal-body',true).first();
4396         this.closeEl = this.el.select('.modal-header .close', true).first();
4397         this.headerEl = this.el.select('.modal-header',true).first();
4398         this.titleEl = this.el.select('.modal-title',true).first();
4399         this.footerEl = this.el.select('.modal-footer',true).first();
4400
4401         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4402         
4403         //this.el.addClass("x-dlg-modal");
4404
4405         if (this.buttons.length) {
4406             Roo.each(this.buttons, function(bb) {
4407                 var b = Roo.apply({}, bb);
4408                 b.xns = b.xns || Roo.bootstrap;
4409                 b.xtype = b.xtype || 'Button';
4410                 if (typeof(b.listeners) == 'undefined') {
4411                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4412                 }
4413
4414                 var btn = Roo.factory(b);
4415
4416                 btn.render(this.getButtonContainer());
4417
4418             },this);
4419         }
4420         // render the children.
4421         var nitems = [];
4422
4423         if(typeof(this.items) != 'undefined'){
4424             var items = this.items;
4425             delete this.items;
4426
4427             for(var i =0;i < items.length;i++) {
4428                 // we force children not to montor widnow resize  - as we do that for them.
4429                 items[i].monitorWindowResize = false;
4430                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4431             }
4432         }
4433
4434         this.items = nitems;
4435
4436         // where are these used - they used to be body/close/footer
4437
4438
4439         this.initEvents();
4440         //this.el.addClass([this.fieldClass, this.cls]);
4441
4442     },
4443
4444     getAutoCreate : function()
4445     {
4446         // we will default to modal-body-overflow - might need to remove or make optional later.
4447         var bdy = {
4448                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4449                 html : this.html || ''
4450         };
4451
4452         var title = {
4453             tag: 'h5',
4454             cls : 'modal-title',
4455             html : this.title
4456         };
4457
4458         if(this.specificTitle){ // WTF is this?
4459             title = this.title;
4460         }
4461
4462         var header = [];
4463         if (this.allow_close && Roo.bootstrap.version == 3) {
4464             header.push({
4465                 tag: 'button',
4466                 cls : 'close',
4467                 html : '&times'
4468             });
4469         }
4470
4471         header.push(title);
4472
4473         if (this.editableTitle) {
4474             header.push({
4475                 cls: 'form-control roo-editable-title d-none',
4476                 tag: 'input',
4477                 type: 'text'
4478             });
4479         }
4480         
4481         if (this.allow_close && Roo.bootstrap.version == 4) {
4482             header.push({
4483                 tag: 'button',
4484                 cls : 'close',
4485                 html : '&times'
4486             });
4487         }
4488         
4489         var size = '';
4490
4491         if(this.size.length){
4492             size = 'modal-' + this.size;
4493         }
4494         
4495         var footer = Roo.bootstrap.version == 3 ?
4496             {
4497                 cls : 'modal-footer',
4498                 cn : [
4499                     {
4500                         tag: 'div',
4501                         cls: 'btn-' + this.buttonPosition
4502                     }
4503                 ]
4504
4505             } :
4506             {  // BS4 uses mr-auto on left buttons....
4507                 cls : 'modal-footer'
4508             };
4509
4510             
4511
4512         
4513         
4514         var modal = {
4515             cls: "modal",
4516              cn : [
4517                 {
4518                     cls: "modal-dialog " + size,
4519                     cn : [
4520                         {
4521                             cls : "modal-content",
4522                             cn : [
4523                                 {
4524                                     cls : 'modal-header',
4525                                     cn : header
4526                                 },
4527                                 bdy,
4528                                 footer
4529                             ]
4530
4531                         }
4532                     ]
4533
4534                 }
4535             ]
4536         };
4537
4538         if(this.animate){
4539             modal.cls += ' fade';
4540         }
4541
4542         return modal;
4543
4544     },
4545     getChildContainer : function() {
4546
4547          return this.bodyEl;
4548
4549     },
4550     getButtonContainer : function() {
4551         
4552          return Roo.bootstrap.version == 4 ?
4553             this.el.select('.modal-footer',true).first()
4554             : this.el.select('.modal-footer div',true).first();
4555
4556     },
4557     
4558     closeClick : function()
4559     {
4560         this.hide();
4561     },
4562     
4563     initEvents : function()
4564     {
4565         if (this.allow_close) {
4566             this.closeEl.on('click', this.closeClick, this);
4567         }
4568         Roo.EventManager.onWindowResize(this.resize, this, true);
4569         if (this.editableTitle) {
4570             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4571             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4572             this.headerEditEl.on('keyup', function(e) {
4573                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4574                         this.toggleHeaderInput(false)
4575                     }
4576                 }, this);
4577             this.headerEditEl.on('blur', function(e) {
4578                 this.toggleHeaderInput(false)
4579             },this);
4580         }
4581
4582     },
4583   
4584
4585     resize : function()
4586     {
4587         this.maskEl.setSize(
4588             Roo.lib.Dom.getViewWidth(true),
4589             Roo.lib.Dom.getViewHeight(true)
4590         );
4591         
4592         if (this.fitwindow) {
4593             
4594            this.dialogEl.setStyle( { 'max-width' : '100%' });
4595             this.setSize(
4596                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4597                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4598             );
4599             return;
4600         }
4601         
4602         if(this.max_width !== 0) {
4603             
4604             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4605             
4606             if(this.height) {
4607                 this.setSize(w, this.height);
4608                 return;
4609             }
4610             
4611             if(this.max_height) {
4612                 this.setSize(w,Math.min(
4613                     this.max_height,
4614                     Roo.lib.Dom.getViewportHeight(true) - 60
4615                 ));
4616                 
4617                 return;
4618             }
4619             
4620             if(!this.fit_content) {
4621                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4622                 return;
4623             }
4624             
4625             this.setSize(w, Math.min(
4626                 60 +
4627                 this.headerEl.getHeight() + 
4628                 this.footerEl.getHeight() + 
4629                 this.getChildHeight(this.bodyEl.dom.childNodes),
4630                 Roo.lib.Dom.getViewportHeight(true) - 60)
4631             );
4632         }
4633         
4634     },
4635
4636     setSize : function(w,h)
4637     {
4638         if (!w && !h) {
4639             return;
4640         }
4641         
4642         this.resizeTo(w,h);
4643         // any layout/border etc.. resize..
4644         (function () {
4645             this.items.forEach( function(e) {
4646                 e.layout ? e.layout() : false;
4647
4648             });
4649         }).defer(100,this);
4650         
4651     },
4652
4653     show : function() {
4654
4655         if (!this.rendered) {
4656             this.render();
4657         }
4658         this.toggleHeaderInput(false);
4659         //this.el.setStyle('display', 'block');
4660         this.el.removeClass('hideing');
4661         this.el.dom.style.display='block';
4662         
4663         Roo.get(document.body).addClass('modal-open');
4664  
4665         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4666             
4667             (function(){
4668                 this.el.addClass('show');
4669                 this.el.addClass('in');
4670             }).defer(50, this);
4671         }else{
4672             this.el.addClass('show');
4673             this.el.addClass('in');
4674         }
4675
4676         // not sure how we can show data in here..
4677         //if (this.tmpl) {
4678         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4679         //}
4680
4681         Roo.get(document.body).addClass("x-body-masked");
4682         
4683         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4684         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4685         this.maskEl.dom.style.display = 'block';
4686         this.maskEl.addClass('show');
4687         
4688         
4689         this.resize();
4690         
4691         this.fireEvent('show', this);
4692
4693         // set zindex here - otherwise it appears to be ignored...
4694         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4695         
4696         
4697         // this is for children that are... layout.Border 
4698         (function () {
4699             this.items.forEach( function(e) {
4700                 e.layout ? e.layout() : false;
4701
4702             });
4703         }).defer(100,this);
4704
4705     },
4706     hide : function()
4707     {
4708         if(this.fireEvent("beforehide", this) !== false){
4709             
4710             this.maskEl.removeClass('show');
4711             
4712             this.maskEl.dom.style.display = '';
4713             Roo.get(document.body).removeClass("x-body-masked");
4714             this.el.removeClass('in');
4715             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4716
4717             if(this.animate){ // why
4718                 this.el.addClass('hideing');
4719                 this.el.removeClass('show');
4720                 (function(){
4721                     if (!this.el.hasClass('hideing')) {
4722                         return; // it's been shown again...
4723                     }
4724                     
4725                     this.el.dom.style.display='';
4726
4727                     Roo.get(document.body).removeClass('modal-open');
4728                     this.el.removeClass('hideing');
4729                 }).defer(150,this);
4730                 
4731             }else{
4732                 this.el.removeClass('show');
4733                 this.el.dom.style.display='';
4734                 Roo.get(document.body).removeClass('modal-open');
4735
4736             }
4737             this.fireEvent('hide', this);
4738         }
4739     },
4740     isVisible : function()
4741     {
4742         
4743         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4744         
4745     },
4746
4747     addButton : function(str, cb)
4748     {
4749
4750
4751         var b = Roo.apply({}, { html : str } );
4752         b.xns = b.xns || Roo.bootstrap;
4753         b.xtype = b.xtype || 'Button';
4754         if (typeof(b.listeners) == 'undefined') {
4755             b.listeners = { click : cb.createDelegate(this)  };
4756         }
4757
4758         var btn = Roo.factory(b);
4759
4760         btn.render(this.getButtonContainer());
4761
4762         return btn;
4763
4764     },
4765
4766     setDefaultButton : function(btn)
4767     {
4768         //this.el.select('.modal-footer').()
4769     },
4770
4771     resizeTo: function(w,h)
4772     {
4773         this.dialogEl.setWidth(w);
4774         
4775         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4776
4777         this.bodyEl.setHeight(h - diff);
4778         
4779         this.fireEvent('resize', this);
4780     },
4781     
4782     setContentSize  : function(w, h)
4783     {
4784
4785     },
4786     onButtonClick: function(btn,e)
4787     {
4788         //Roo.log([a,b,c]);
4789         this.fireEvent('btnclick', btn.name, e);
4790     },
4791      /**
4792      * Set the title of the Dialog
4793      * @param {String} str new Title
4794      */
4795     setTitle: function(str) {
4796         this.titleEl.dom.innerHTML = str;
4797         this.title = str;
4798     },
4799     /**
4800      * Set the body of the Dialog
4801      * @param {String} str new Title
4802      */
4803     setBody: function(str) {
4804         this.bodyEl.dom.innerHTML = str;
4805     },
4806     /**
4807      * Set the body of the Dialog using the template
4808      * @param {Obj} data - apply this data to the template and replace the body contents.
4809      */
4810     applyBody: function(obj)
4811     {
4812         if (!this.tmpl) {
4813             Roo.log("Error - using apply Body without a template");
4814             //code
4815         }
4816         this.tmpl.overwrite(this.bodyEl, obj);
4817     },
4818     
4819     getChildHeight : function(child_nodes)
4820     {
4821         if(
4822             !child_nodes ||
4823             child_nodes.length == 0
4824         ) {
4825             return 0;
4826         }
4827         
4828         var child_height = 0;
4829         
4830         for(var i = 0; i < child_nodes.length; i++) {
4831             
4832             /*
4833             * for modal with tabs...
4834             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4835                 
4836                 var layout_childs = child_nodes[i].childNodes;
4837                 
4838                 for(var j = 0; j < layout_childs.length; j++) {
4839                     
4840                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4841                         
4842                         var layout_body_childs = layout_childs[j].childNodes;
4843                         
4844                         for(var k = 0; k < layout_body_childs.length; k++) {
4845                             
4846                             if(layout_body_childs[k].classList.contains('navbar')) {
4847                                 child_height += layout_body_childs[k].offsetHeight;
4848                                 continue;
4849                             }
4850                             
4851                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4852                                 
4853                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4854                                 
4855                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4856                                     
4857                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4858                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4859                                         continue;
4860                                     }
4861                                     
4862                                 }
4863                                 
4864                             }
4865                             
4866                         }
4867                     }
4868                 }
4869                 continue;
4870             }
4871             */
4872             
4873             child_height += child_nodes[i].offsetHeight;
4874             // Roo.log(child_nodes[i].offsetHeight);
4875         }
4876         
4877         return child_height;
4878     },
4879     toggleHeaderInput : function(is_edit)
4880     {
4881         if (!this.editableTitle) {
4882             return; // not editable.
4883         }
4884         if (is_edit && this.is_header_editing) {
4885             return; // already editing..
4886         }
4887         if (is_edit) {
4888     
4889             this.headerEditEl.dom.value = this.title;
4890             this.headerEditEl.removeClass('d-none');
4891             this.headerEditEl.dom.focus();
4892             this.titleEl.addClass('d-none');
4893             
4894             this.is_header_editing = true;
4895             return
4896         }
4897         // flip back to not editing.
4898         this.title = this.headerEditEl.dom.value;
4899         this.headerEditEl.addClass('d-none');
4900         this.titleEl.removeClass('d-none');
4901         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4902         this.is_header_editing = false;
4903         this.fireEvent('titlechanged', this, this.title);
4904     
4905             
4906         
4907     }
4908
4909 });
4910
4911
4912 Roo.apply(Roo.bootstrap.Modal,  {
4913     /**
4914          * Button config that displays a single OK button
4915          * @type Object
4916          */
4917         OK :  [{
4918             name : 'ok',
4919             weight : 'primary',
4920             html : 'OK'
4921         }],
4922         /**
4923          * Button config that displays Yes and No buttons
4924          * @type Object
4925          */
4926         YESNO : [
4927             {
4928                 name  : 'no',
4929                 html : 'No'
4930             },
4931             {
4932                 name  :'yes',
4933                 weight : 'primary',
4934                 html : 'Yes'
4935             }
4936         ],
4937
4938         /**
4939          * Button config that displays OK and Cancel buttons
4940          * @type Object
4941          */
4942         OKCANCEL : [
4943             {
4944                name : 'cancel',
4945                 html : 'Cancel'
4946             },
4947             {
4948                 name : 'ok',
4949                 weight : 'primary',
4950                 html : 'OK'
4951             }
4952         ],
4953         /**
4954          * Button config that displays Yes, No and Cancel buttons
4955          * @type Object
4956          */
4957         YESNOCANCEL : [
4958             {
4959                 name : 'yes',
4960                 weight : 'primary',
4961                 html : 'Yes'
4962             },
4963             {
4964                 name : 'no',
4965                 html : 'No'
4966             },
4967             {
4968                 name : 'cancel',
4969                 html : 'Cancel'
4970             }
4971         ],
4972         
4973         zIndex : 10001
4974 });
4975
4976 /*
4977  * - LGPL
4978  *
4979  * messagebox - can be used as a replace
4980  * 
4981  */
4982 /**
4983  * @class Roo.MessageBox
4984  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4985  * Example usage:
4986  *<pre><code>
4987 // Basic alert:
4988 Roo.Msg.alert('Status', 'Changes saved successfully.');
4989
4990 // Prompt for user data:
4991 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4992     if (btn == 'ok'){
4993         // process text value...
4994     }
4995 });
4996
4997 // Show a dialog using config options:
4998 Roo.Msg.show({
4999    title:'Save Changes?',
5000    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5001    buttons: Roo.Msg.YESNOCANCEL,
5002    fn: processResult,
5003    animEl: 'elId'
5004 });
5005 </code></pre>
5006  * @static
5007  */
5008 Roo.bootstrap.MessageBox = function(){
5009     var dlg, opt, mask, waitTimer;
5010     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5011     var buttons, activeTextEl, bwidth;
5012
5013     
5014     // private
5015     var handleButton = function(button){
5016         dlg.hide();
5017         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5018     };
5019
5020     // private
5021     var handleHide = function(){
5022         if(opt && opt.cls){
5023             dlg.el.removeClass(opt.cls);
5024         }
5025         //if(waitTimer){
5026         //    Roo.TaskMgr.stop(waitTimer);
5027         //    waitTimer = null;
5028         //}
5029     };
5030
5031     // private
5032     var updateButtons = function(b){
5033         var width = 0;
5034         if(!b){
5035             buttons["ok"].hide();
5036             buttons["cancel"].hide();
5037             buttons["yes"].hide();
5038             buttons["no"].hide();
5039             dlg.footerEl.hide();
5040             
5041             return width;
5042         }
5043         dlg.footerEl.show();
5044         for(var k in buttons){
5045             if(typeof buttons[k] != "function"){
5046                 if(b[k]){
5047                     buttons[k].show();
5048                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5049                     width += buttons[k].el.getWidth()+15;
5050                 }else{
5051                     buttons[k].hide();
5052                 }
5053             }
5054         }
5055         return width;
5056     };
5057
5058     // private
5059     var handleEsc = function(d, k, e){
5060         if(opt && opt.closable !== false){
5061             dlg.hide();
5062         }
5063         if(e){
5064             e.stopEvent();
5065         }
5066     };
5067
5068     return {
5069         /**
5070          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5071          * @return {Roo.BasicDialog} The BasicDialog element
5072          */
5073         getDialog : function(){
5074            if(!dlg){
5075                 dlg = new Roo.bootstrap.Modal( {
5076                     //draggable: true,
5077                     //resizable:false,
5078                     //constraintoviewport:false,
5079                     //fixedcenter:true,
5080                     //collapsible : false,
5081                     //shim:true,
5082                     //modal: true,
5083                 //    width: 'auto',
5084                   //  height:100,
5085                     //buttonAlign:"center",
5086                     closeClick : function(){
5087                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5088                             handleButton("no");
5089                         }else{
5090                             handleButton("cancel");
5091                         }
5092                     }
5093                 });
5094                 dlg.render();
5095                 dlg.on("hide", handleHide);
5096                 mask = dlg.mask;
5097                 //dlg.addKeyListener(27, handleEsc);
5098                 buttons = {};
5099                 this.buttons = buttons;
5100                 var bt = this.buttonText;
5101                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5102                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5103                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5104                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5105                 //Roo.log(buttons);
5106                 bodyEl = dlg.bodyEl.createChild({
5107
5108                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5109                         '<textarea class="roo-mb-textarea"></textarea>' +
5110                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5111                 });
5112                 msgEl = bodyEl.dom.firstChild;
5113                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5114                 textboxEl.enableDisplayMode();
5115                 textboxEl.addKeyListener([10,13], function(){
5116                     if(dlg.isVisible() && opt && opt.buttons){
5117                         if(opt.buttons.ok){
5118                             handleButton("ok");
5119                         }else if(opt.buttons.yes){
5120                             handleButton("yes");
5121                         }
5122                     }
5123                 });
5124                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5125                 textareaEl.enableDisplayMode();
5126                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5127                 progressEl.enableDisplayMode();
5128                 
5129                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5130                 var pf = progressEl.dom.firstChild;
5131                 if (pf) {
5132                     pp = Roo.get(pf.firstChild);
5133                     pp.setHeight(pf.offsetHeight);
5134                 }
5135                 
5136             }
5137             return dlg;
5138         },
5139
5140         /**
5141          * Updates the message box body text
5142          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5143          * the XHTML-compliant non-breaking space character '&amp;#160;')
5144          * @return {Roo.MessageBox} This message box
5145          */
5146         updateText : function(text)
5147         {
5148             if(!dlg.isVisible() && !opt.width){
5149                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5150                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5151             }
5152             msgEl.innerHTML = text || '&#160;';
5153       
5154             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5155             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5156             var w = Math.max(
5157                     Math.min(opt.width || cw , this.maxWidth), 
5158                     Math.max(opt.minWidth || this.minWidth, bwidth)
5159             );
5160             if(opt.prompt){
5161                 activeTextEl.setWidth(w);
5162             }
5163             if(dlg.isVisible()){
5164                 dlg.fixedcenter = false;
5165             }
5166             // to big, make it scroll. = But as usual stupid IE does not support
5167             // !important..
5168             
5169             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5170                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5171                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5172             } else {
5173                 bodyEl.dom.style.height = '';
5174                 bodyEl.dom.style.overflowY = '';
5175             }
5176             if (cw > w) {
5177                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5178             } else {
5179                 bodyEl.dom.style.overflowX = '';
5180             }
5181             
5182             dlg.setContentSize(w, bodyEl.getHeight());
5183             if(dlg.isVisible()){
5184                 dlg.fixedcenter = true;
5185             }
5186             return this;
5187         },
5188
5189         /**
5190          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5191          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5192          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5193          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5194          * @return {Roo.MessageBox} This message box
5195          */
5196         updateProgress : function(value, text){
5197             if(text){
5198                 this.updateText(text);
5199             }
5200             
5201             if (pp) { // weird bug on my firefox - for some reason this is not defined
5202                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5203                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5204             }
5205             return this;
5206         },        
5207
5208         /**
5209          * Returns true if the message box is currently displayed
5210          * @return {Boolean} True if the message box is visible, else false
5211          */
5212         isVisible : function(){
5213             return dlg && dlg.isVisible();  
5214         },
5215
5216         /**
5217          * Hides the message box if it is displayed
5218          */
5219         hide : function(){
5220             if(this.isVisible()){
5221                 dlg.hide();
5222             }  
5223         },
5224
5225         /**
5226          * Displays a new message box, or reinitializes an existing message box, based on the config options
5227          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5228          * The following config object properties are supported:
5229          * <pre>
5230 Property    Type             Description
5231 ----------  ---------------  ------------------------------------------------------------------------------------
5232 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5233                                    closes (defaults to undefined)
5234 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5235                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5236 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5237                                    progress and wait dialogs will ignore this property and always hide the
5238                                    close button as they can only be closed programmatically.
5239 cls               String           A custom CSS class to apply to the message box element
5240 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5241                                    displayed (defaults to 75)
5242 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5243                                    function will be btn (the name of the button that was clicked, if applicable,
5244                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5245                                    Progress and wait dialogs will ignore this option since they do not respond to
5246                                    user actions and can only be closed programmatically, so any required function
5247                                    should be called by the same code after it closes the dialog.
5248 icon              String           A CSS class that provides a background image to be used as an icon for
5249                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5250 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5251 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5252 modal             Boolean          False to allow user interaction with the page while the message box is
5253                                    displayed (defaults to true)
5254 msg               String           A string that will replace the existing message box body text (defaults
5255                                    to the XHTML-compliant non-breaking space character '&#160;')
5256 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5257 progress          Boolean          True to display a progress bar (defaults to false)
5258 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5259 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5260 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5261 title             String           The title text
5262 value             String           The string value to set into the active textbox element if displayed
5263 wait              Boolean          True to display a progress bar (defaults to false)
5264 width             Number           The width of the dialog in pixels
5265 </pre>
5266          *
5267          * Example usage:
5268          * <pre><code>
5269 Roo.Msg.show({
5270    title: 'Address',
5271    msg: 'Please enter your address:',
5272    width: 300,
5273    buttons: Roo.MessageBox.OKCANCEL,
5274    multiline: true,
5275    fn: saveAddress,
5276    animEl: 'addAddressBtn'
5277 });
5278 </code></pre>
5279          * @param {Object} config Configuration options
5280          * @return {Roo.MessageBox} This message box
5281          */
5282         show : function(options)
5283         {
5284             
5285             // this causes nightmares if you show one dialog after another
5286             // especially on callbacks..
5287              
5288             if(this.isVisible()){
5289                 
5290                 this.hide();
5291                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5292                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5293                 Roo.log("New Dialog Message:" +  options.msg )
5294                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5295                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5296                 
5297             }
5298             var d = this.getDialog();
5299             opt = options;
5300             d.setTitle(opt.title || "&#160;");
5301             d.closeEl.setDisplayed(opt.closable !== false);
5302             activeTextEl = textboxEl;
5303             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5304             if(opt.prompt){
5305                 if(opt.multiline){
5306                     textboxEl.hide();
5307                     textareaEl.show();
5308                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5309                         opt.multiline : this.defaultTextHeight);
5310                     activeTextEl = textareaEl;
5311                 }else{
5312                     textboxEl.show();
5313                     textareaEl.hide();
5314                 }
5315             }else{
5316                 textboxEl.hide();
5317                 textareaEl.hide();
5318             }
5319             progressEl.setDisplayed(opt.progress === true);
5320             if (opt.progress) {
5321                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5322             }
5323             this.updateProgress(0);
5324             activeTextEl.dom.value = opt.value || "";
5325             if(opt.prompt){
5326                 dlg.setDefaultButton(activeTextEl);
5327             }else{
5328                 var bs = opt.buttons;
5329                 var db = null;
5330                 if(bs && bs.ok){
5331                     db = buttons["ok"];
5332                 }else if(bs && bs.yes){
5333                     db = buttons["yes"];
5334                 }
5335                 dlg.setDefaultButton(db);
5336             }
5337             bwidth = updateButtons(opt.buttons);
5338             this.updateText(opt.msg);
5339             if(opt.cls){
5340                 d.el.addClass(opt.cls);
5341             }
5342             d.proxyDrag = opt.proxyDrag === true;
5343             d.modal = opt.modal !== false;
5344             d.mask = opt.modal !== false ? mask : false;
5345             if(!d.isVisible()){
5346                 // force it to the end of the z-index stack so it gets a cursor in FF
5347                 document.body.appendChild(dlg.el.dom);
5348                 d.animateTarget = null;
5349                 d.show(options.animEl);
5350             }
5351             return this;
5352         },
5353
5354         /**
5355          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5356          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5357          * and closing the message box when the process is complete.
5358          * @param {String} title The title bar text
5359          * @param {String} msg The message box body text
5360          * @return {Roo.MessageBox} This message box
5361          */
5362         progress : function(title, msg){
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: false,
5367                 progress:true,
5368                 closable:false,
5369                 minWidth: this.minProgressWidth,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5377          * If a callback function is passed it will be called after the user clicks the button, and the
5378          * id of the button that was clicked will be passed as the only parameter to the callback
5379          * (could also be the top-right close button).
5380          * @param {String} title The title bar text
5381          * @param {String} msg The message box body text
5382          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5383          * @param {Object} scope (optional) The scope of the callback function
5384          * @return {Roo.MessageBox} This message box
5385          */
5386         alert : function(title, msg, fn, scope)
5387         {
5388             this.show({
5389                 title : title,
5390                 msg : msg,
5391                 buttons: this.OK,
5392                 fn: fn,
5393                 closable : false,
5394                 scope : scope,
5395                 modal : true
5396             });
5397             return this;
5398         },
5399
5400         /**
5401          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5402          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5403          * You are responsible for closing the message box when the process is complete.
5404          * @param {String} msg The message box body text
5405          * @param {String} title (optional) The title bar text
5406          * @return {Roo.MessageBox} This message box
5407          */
5408         wait : function(msg, title){
5409             this.show({
5410                 title : title,
5411                 msg : msg,
5412                 buttons: false,
5413                 closable:false,
5414                 progress:true,
5415                 modal:true,
5416                 width:300,
5417                 wait:true
5418             });
5419             waitTimer = Roo.TaskMgr.start({
5420                 run: function(i){
5421                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5422                 },
5423                 interval: 1000
5424             });
5425             return this;
5426         },
5427
5428         /**
5429          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5430          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5431          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5432          * @param {String} title The title bar text
5433          * @param {String} msg The message box body text
5434          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5435          * @param {Object} scope (optional) The scope of the callback function
5436          * @return {Roo.MessageBox} This message box
5437          */
5438         confirm : function(title, msg, fn, scope){
5439             this.show({
5440                 title : title,
5441                 msg : msg,
5442                 buttons: this.YESNO,
5443                 fn: fn,
5444                 scope : scope,
5445                 modal : true
5446             });
5447             return this;
5448         },
5449
5450         /**
5451          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5452          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5453          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5454          * (could also be the top-right close button) and the text that was entered will be passed as the two
5455          * parameters to the callback.
5456          * @param {String} title The title bar text
5457          * @param {String} msg The message box body text
5458          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5459          * @param {Object} scope (optional) The scope of the callback function
5460          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5461          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5462          * @return {Roo.MessageBox} This message box
5463          */
5464         prompt : function(title, msg, fn, scope, multiline){
5465             this.show({
5466                 title : title,
5467                 msg : msg,
5468                 buttons: this.OKCANCEL,
5469                 fn: fn,
5470                 minWidth:250,
5471                 scope : scope,
5472                 prompt:true,
5473                 multiline: multiline,
5474                 modal : true
5475             });
5476             return this;
5477         },
5478
5479         /**
5480          * Button config that displays a single OK button
5481          * @type Object
5482          */
5483         OK : {ok:true},
5484         /**
5485          * Button config that displays Yes and No buttons
5486          * @type Object
5487          */
5488         YESNO : {yes:true, no:true},
5489         /**
5490          * Button config that displays OK and Cancel buttons
5491          * @type Object
5492          */
5493         OKCANCEL : {ok:true, cancel:true},
5494         /**
5495          * Button config that displays Yes, No and Cancel buttons
5496          * @type Object
5497          */
5498         YESNOCANCEL : {yes:true, no:true, cancel:true},
5499
5500         /**
5501          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5502          * @type Number
5503          */
5504         defaultTextHeight : 75,
5505         /**
5506          * The maximum width in pixels of the message box (defaults to 600)
5507          * @type Number
5508          */
5509         maxWidth : 600,
5510         /**
5511          * The minimum width in pixels of the message box (defaults to 100)
5512          * @type Number
5513          */
5514         minWidth : 100,
5515         /**
5516          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5517          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5518          * @type Number
5519          */
5520         minProgressWidth : 250,
5521         /**
5522          * An object containing the default button text strings that can be overriden for localized language support.
5523          * Supported properties are: ok, cancel, yes and no.
5524          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5525          * @type Object
5526          */
5527         buttonText : {
5528             ok : "OK",
5529             cancel : "Cancel",
5530             yes : "Yes",
5531             no : "No"
5532         }
5533     };
5534 }();
5535
5536 /**
5537  * Shorthand for {@link Roo.MessageBox}
5538  */
5539 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5540 Roo.Msg = Roo.Msg || Roo.MessageBox;
5541 /*
5542  * - LGPL
5543  *
5544  * navbar
5545  * 
5546  */
5547
5548 /**
5549  * @class Roo.bootstrap.nav.Bar
5550  * @extends Roo.bootstrap.Component
5551  * @abstract
5552  * Bootstrap Navbar class
5553
5554  * @constructor
5555  * Create a new Navbar
5556  * @param {Object} config The config object
5557  */
5558
5559
5560 Roo.bootstrap.nav.Bar = function(config){
5561     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5562     this.addEvents({
5563         // raw events
5564         /**
5565          * @event beforetoggle
5566          * Fire before toggle the menu
5567          * @param {Roo.EventObject} e
5568          */
5569         "beforetoggle" : true
5570     });
5571 };
5572
5573 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5574     
5575     
5576    
5577     // private
5578     navItems : false,
5579     loadMask : false,
5580     
5581     
5582     getAutoCreate : function(){
5583         
5584         
5585         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5586         
5587     },
5588     
5589     initEvents :function ()
5590     {
5591         //Roo.log(this.el.select('.navbar-toggle',true));
5592         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5593         
5594         var mark = {
5595             tag: "div",
5596             cls:"x-dlg-mask"
5597         };
5598         
5599         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5600         
5601         var size = this.el.getSize();
5602         this.maskEl.setSize(size.width, size.height);
5603         this.maskEl.enableDisplayMode("block");
5604         this.maskEl.hide();
5605         
5606         if(this.loadMask){
5607             this.maskEl.show();
5608         }
5609     },
5610     
5611     
5612     getChildContainer : function()
5613     {
5614         if (this.el && this.el.select('.collapse').getCount()) {
5615             return this.el.select('.collapse',true).first();
5616         }
5617         
5618         return this.el;
5619     },
5620     
5621     mask : function()
5622     {
5623         this.maskEl.show();
5624     },
5625     
5626     unmask : function()
5627     {
5628         this.maskEl.hide();
5629     },
5630     onToggle : function()
5631     {
5632         
5633         if(this.fireEvent('beforetoggle', this) === false){
5634             return;
5635         }
5636         var ce = this.el.select('.navbar-collapse',true).first();
5637       
5638         if (!ce.hasClass('show')) {
5639            this.expand();
5640         } else {
5641             this.collapse();
5642         }
5643         
5644         
5645     
5646     },
5647     /**
5648      * Expand the navbar pulldown 
5649      */
5650     expand : function ()
5651     {
5652        
5653         var ce = this.el.select('.navbar-collapse',true).first();
5654         if (ce.hasClass('collapsing')) {
5655             return;
5656         }
5657         ce.dom.style.height = '';
5658                // show it...
5659         ce.addClass('in'); // old...
5660         ce.removeClass('collapse');
5661         ce.addClass('show');
5662         var h = ce.getHeight();
5663         Roo.log(h);
5664         ce.removeClass('show');
5665         // at this point we should be able to see it..
5666         ce.addClass('collapsing');
5667         
5668         ce.setHeight(0); // resize it ...
5669         ce.on('transitionend', function() {
5670             //Roo.log('done transition');
5671             ce.removeClass('collapsing');
5672             ce.addClass('show');
5673             ce.removeClass('collapse');
5674
5675             ce.dom.style.height = '';
5676         }, this, { single: true} );
5677         ce.setHeight(h);
5678         ce.dom.scrollTop = 0;
5679     },
5680     /**
5681      * Collapse the navbar pulldown 
5682      */
5683     collapse : function()
5684     {
5685          var ce = this.el.select('.navbar-collapse',true).first();
5686        
5687         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5688             // it's collapsed or collapsing..
5689             return;
5690         }
5691         ce.removeClass('in'); // old...
5692         ce.setHeight(ce.getHeight());
5693         ce.removeClass('show');
5694         ce.addClass('collapsing');
5695         
5696         ce.on('transitionend', function() {
5697             ce.dom.style.height = '';
5698             ce.removeClass('collapsing');
5699             ce.addClass('collapse');
5700         }, this, { single: true} );
5701         ce.setHeight(0);
5702     }
5703     
5704     
5705     
5706 });
5707
5708
5709
5710  
5711
5712  /*
5713  * - LGPL
5714  *
5715  * navbar
5716  * 
5717  */
5718
5719 /**
5720  * @class Roo.bootstrap.nav.Simplebar
5721  * @extends Roo.bootstrap.nav.Bar
5722  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5723  * Bootstrap Sidebar class
5724  *
5725  * @cfg {Boolean} inverse is inverted color
5726  * 
5727  * @cfg {String} type (nav | pills | tabs)
5728  * @cfg {Boolean} arrangement stacked | justified
5729  * @cfg {String} align (left | right) alignment
5730  * 
5731  * @cfg {Boolean} main (true|false) main nav bar? default false
5732  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5733  * 
5734  * @cfg {String} tag (header|footer|nav|div) default is nav 
5735
5736  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5737  * 
5738  * 
5739  * @constructor
5740  * Create a new Sidebar
5741  * @param {Object} config The config object
5742  */
5743
5744
5745 Roo.bootstrap.nav.Simplebar = function(config){
5746     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5747 };
5748
5749 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5750     
5751     inverse: false,
5752     
5753     type: false,
5754     arrangement: '',
5755     align : false,
5756     
5757     weight : 'light',
5758     
5759     main : false,
5760     
5761     
5762     tag : false,
5763     
5764     
5765     getAutoCreate : function(){
5766         
5767         
5768         var cfg = {
5769             tag : this.tag || 'div',
5770             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5771         };
5772         if (['light','white'].indexOf(this.weight) > -1) {
5773             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5774         }
5775         cfg.cls += ' bg-' + this.weight;
5776         
5777         if (this.inverse) {
5778             cfg.cls += ' navbar-inverse';
5779             
5780         }
5781         
5782         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5783         
5784         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5785             return cfg;
5786         }
5787         
5788         
5789     
5790         
5791         cfg.cn = [
5792             {
5793                 cls: 'nav nav-' + this.xtype,
5794                 tag : 'ul'
5795             }
5796         ];
5797         
5798          
5799         this.type = this.type || 'nav';
5800         if (['tabs','pills'].indexOf(this.type) != -1) {
5801             cfg.cn[0].cls += ' nav-' + this.type
5802         
5803         
5804         } else {
5805             if (this.type!=='nav') {
5806                 Roo.log('nav type must be nav/tabs/pills')
5807             }
5808             cfg.cn[0].cls += ' navbar-nav'
5809         }
5810         
5811         
5812         
5813         
5814         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5815             cfg.cn[0].cls += ' nav-' + this.arrangement;
5816         }
5817         
5818         
5819         if (this.align === 'right') {
5820             cfg.cn[0].cls += ' navbar-right';
5821         }
5822         
5823         
5824         
5825         
5826         return cfg;
5827     
5828         
5829     }
5830     
5831     
5832     
5833 });
5834
5835
5836
5837  
5838
5839  
5840        /*
5841  * - LGPL
5842  *
5843  * navbar
5844  * navbar-fixed-top
5845  * navbar-expand-md  fixed-top 
5846  */
5847
5848 /**
5849  * @class Roo.bootstrap.nav.Headerbar
5850  * @extends Roo.bootstrap.nav.Simplebar
5851  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5852  * Bootstrap Sidebar class
5853  *
5854  * @cfg {String} brand what is brand
5855  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5856  * @cfg {String} brand_href href of the brand
5857  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5858  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5859  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5860  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5861  * 
5862  * @constructor
5863  * Create a new Sidebar
5864  * @param {Object} config The config object
5865  */
5866
5867
5868 Roo.bootstrap.nav.Headerbar = function(config){
5869     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5870       
5871 };
5872
5873 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5874     
5875     position: '',
5876     brand: '',
5877     brand_href: false,
5878     srButton : true,
5879     autohide : false,
5880     desktopCenter : false,
5881    
5882     
5883     getAutoCreate : function(){
5884         
5885         var   cfg = {
5886             tag: this.nav || 'nav',
5887             cls: 'navbar navbar-expand-md',
5888             role: 'navigation',
5889             cn: []
5890         };
5891         
5892         var cn = cfg.cn;
5893         if (this.desktopCenter) {
5894             cn.push({cls : 'container', cn : []});
5895             cn = cn[0].cn;
5896         }
5897         
5898         if(this.srButton){
5899             var btn = {
5900                 tag: 'button',
5901                 type: 'button',
5902                 cls: 'navbar-toggle navbar-toggler',
5903                 'data-toggle': 'collapse',
5904                 cn: [
5905                     {
5906                         tag: 'span',
5907                         cls: 'sr-only',
5908                         html: 'Toggle navigation'
5909                     },
5910                     {
5911                         tag: 'span',
5912                         cls: 'icon-bar navbar-toggler-icon'
5913                     },
5914                     {
5915                         tag: 'span',
5916                         cls: 'icon-bar'
5917                     },
5918                     {
5919                         tag: 'span',
5920                         cls: 'icon-bar'
5921                     }
5922                 ]
5923             };
5924             
5925             cn.push( Roo.bootstrap.version == 4 ? btn : {
5926                 tag: 'div',
5927                 cls: 'navbar-header',
5928                 cn: [
5929                     btn
5930                 ]
5931             });
5932         }
5933         
5934         cn.push({
5935             tag: 'div',
5936             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5937             cn : []
5938         });
5939         
5940         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5941         
5942         if (['light','white'].indexOf(this.weight) > -1) {
5943             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5944         }
5945         cfg.cls += ' bg-' + this.weight;
5946         
5947         
5948         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5949             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5950             
5951             // tag can override this..
5952             
5953             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5954         }
5955         
5956         if (this.brand !== '') {
5957             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5958             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5959                 tag: 'a',
5960                 href: this.brand_href ? this.brand_href : '#',
5961                 cls: 'navbar-brand',
5962                 cn: [
5963                 this.brand
5964                 ]
5965             });
5966         }
5967         
5968         if(this.main){
5969             cfg.cls += ' main-nav';
5970         }
5971         
5972         
5973         return cfg;
5974
5975         
5976     },
5977     getHeaderChildContainer : function()
5978     {
5979         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5980             return this.el.select('.navbar-header',true).first();
5981         }
5982         
5983         return this.getChildContainer();
5984     },
5985     
5986     getChildContainer : function()
5987     {
5988          
5989         return this.el.select('.roo-navbar-collapse',true).first();
5990          
5991         
5992     },
5993     
5994     initEvents : function()
5995     {
5996         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5997         
5998         if (this.autohide) {
5999             
6000             var prevScroll = 0;
6001             var ft = this.el;
6002             
6003             Roo.get(document).on('scroll',function(e) {
6004                 var ns = Roo.get(document).getScroll().top;
6005                 var os = prevScroll;
6006                 prevScroll = ns;
6007                 
6008                 if(ns > os){
6009                     ft.removeClass('slideDown');
6010                     ft.addClass('slideUp');
6011                     return;
6012                 }
6013                 ft.removeClass('slideUp');
6014                 ft.addClass('slideDown');
6015                  
6016               
6017           },this);
6018         }
6019     }    
6020     
6021 });
6022
6023
6024
6025  
6026
6027  /*
6028  * - LGPL
6029  *
6030  * navbar
6031  * 
6032  */
6033
6034 /**
6035  * @class Roo.bootstrap.nav.Sidebar
6036  * @extends Roo.bootstrap.nav.Bar
6037  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6038  * Bootstrap Sidebar class
6039  * 
6040  * @constructor
6041  * Create a new Sidebar
6042  * @param {Object} config The config object
6043  */
6044
6045
6046 Roo.bootstrap.nav.Sidebar = function(config){
6047     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6048 };
6049
6050 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6051     
6052     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6053     
6054     getAutoCreate : function(){
6055         
6056         
6057         return  {
6058             tag: 'div',
6059             cls: 'sidebar sidebar-nav'
6060         };
6061     
6062         
6063     }
6064     
6065     
6066     
6067 });
6068
6069
6070
6071  
6072
6073  /*
6074  * - LGPL
6075  *
6076  * nav group
6077  * 
6078  */
6079
6080 /**
6081  * @class Roo.bootstrap.nav.Group
6082  * @extends Roo.bootstrap.Component
6083  * @children Roo.bootstrap.nav.Item
6084  * Bootstrap NavGroup class
6085  * @cfg {String} align (left|right)
6086  * @cfg {Boolean} inverse
6087  * @cfg {String} type (nav|pills|tab) default nav
6088  * @cfg {String} navId - reference Id for navbar.
6089  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6090  * 
6091  * @constructor
6092  * Create a new nav group
6093  * @param {Object} config The config object
6094  */
6095
6096 Roo.bootstrap.nav.Group = function(config){
6097     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6098     this.navItems = [];
6099    
6100     Roo.bootstrap.nav.Group.register(this);
6101      this.addEvents({
6102         /**
6103              * @event changed
6104              * Fires when the active item changes
6105              * @param {Roo.bootstrap.nav.Group} this
6106              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6107              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6108          */
6109         'changed': true
6110      });
6111     
6112 };
6113
6114 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6115     
6116     align: '',
6117     inverse: false,
6118     form: false,
6119     type: 'nav',
6120     navId : '',
6121     // private
6122     pilltype : true,
6123     
6124     navItems : false, 
6125     
6126     getAutoCreate : function()
6127     {
6128         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6129         
6130         cfg = {
6131             tag : 'ul',
6132             cls: 'nav' 
6133         };
6134         if (Roo.bootstrap.version == 4) {
6135             if (['tabs','pills'].indexOf(this.type) != -1) {
6136                 cfg.cls += ' nav-' + this.type; 
6137             } else {
6138                 // trying to remove so header bar can right align top?
6139                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6140                     // do not use on header bar... 
6141                     cfg.cls += ' navbar-nav';
6142                 }
6143             }
6144             
6145         } else {
6146             if (['tabs','pills'].indexOf(this.type) != -1) {
6147                 cfg.cls += ' nav-' + this.type
6148             } else {
6149                 if (this.type !== 'nav') {
6150                     Roo.log('nav type must be nav/tabs/pills')
6151                 }
6152                 cfg.cls += ' navbar-nav'
6153             }
6154         }
6155         
6156         if (this.parent() && this.parent().sidebar) {
6157             cfg = {
6158                 tag: 'ul',
6159                 cls: 'dashboard-menu sidebar-menu'
6160             };
6161             
6162             return cfg;
6163         }
6164         
6165         if (this.form === true) {
6166             cfg = {
6167                 tag: 'form',
6168                 cls: 'navbar-form form-inline'
6169             };
6170             //nav navbar-right ml-md-auto
6171             if (this.align === 'right') {
6172                 cfg.cls += ' navbar-right ml-md-auto';
6173             } else {
6174                 cfg.cls += ' navbar-left';
6175             }
6176         }
6177         
6178         if (this.align === 'right') {
6179             cfg.cls += ' navbar-right ml-md-auto';
6180         } else {
6181             cfg.cls += ' mr-auto';
6182         }
6183         
6184         if (this.inverse) {
6185             cfg.cls += ' navbar-inverse';
6186             
6187         }
6188         
6189         
6190         return cfg;
6191     },
6192     /**
6193     * sets the active Navigation item
6194     * @param {Roo.bootstrap.nav.Item} the new current navitem
6195     */
6196     setActiveItem : function(item)
6197     {
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             if (v == item) {
6201                 return ;
6202             }
6203             if (v.isActive()) {
6204                 v.setActive(false, true);
6205                 prev = v;
6206                 
6207             }
6208             
6209         });
6210
6211         item.setActive(true, true);
6212         this.fireEvent('changed', this, item, prev);
6213         
6214         
6215     },
6216     /**
6217     * gets the active Navigation item
6218     * @return {Roo.bootstrap.nav.Item} the current navitem
6219     */
6220     getActive : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v){
6225             
6226             if (v.isActive()) {
6227                 prev = v;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     
6235     indexOfNav : function()
6236     {
6237         
6238         var prev = false;
6239         Roo.each(this.navItems, function(v,i){
6240             
6241             if (v.isActive()) {
6242                 prev = i;
6243                 
6244             }
6245             
6246         });
6247         return prev;
6248     },
6249     /**
6250     * adds a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     addItem : function(cfg)
6254     {
6255         if (this.form && Roo.bootstrap.version == 4) {
6256             cfg.tag = 'div';
6257         }
6258         var cn = new Roo.bootstrap.nav.Item(cfg);
6259         this.register(cn);
6260         cn.parentId = this.id;
6261         cn.onRender(this.el, null);
6262         return cn;
6263     },
6264     /**
6265     * register a Navigation item
6266     * @param {Roo.bootstrap.nav.Item} the navitem to add
6267     */
6268     register : function(item)
6269     {
6270         this.navItems.push( item);
6271         item.navId = this.navId;
6272     
6273     },
6274     
6275     /**
6276     * clear all the Navigation item
6277     */
6278    
6279     clearAll : function()
6280     {
6281         this.navItems = [];
6282         this.el.dom.innerHTML = '';
6283     },
6284     
6285     getNavItem: function(tabId)
6286     {
6287         var ret = false;
6288         Roo.each(this.navItems, function(e) {
6289             if (e.tabId == tabId) {
6290                ret =  e;
6291                return false;
6292             }
6293             return true;
6294             
6295         });
6296         return ret;
6297     },
6298     
6299     setActiveNext : function()
6300     {
6301         var i = this.indexOfNav(this.getActive());
6302         if (i > this.navItems.length) {
6303             return;
6304         }
6305         this.setActiveItem(this.navItems[i+1]);
6306     },
6307     setActivePrev : function()
6308     {
6309         var i = this.indexOfNav(this.getActive());
6310         if (i  < 1) {
6311             return;
6312         }
6313         this.setActiveItem(this.navItems[i-1]);
6314     },
6315     clearWasActive : function(except) {
6316         Roo.each(this.navItems, function(e) {
6317             if (e.tabId != except.tabId && e.was_active) {
6318                e.was_active = false;
6319                return false;
6320             }
6321             return true;
6322             
6323         });
6324     },
6325     getWasActive : function ()
6326     {
6327         var r = false;
6328         Roo.each(this.navItems, function(e) {
6329             if (e.was_active) {
6330                r = e;
6331                return false;
6332             }
6333             return true;
6334             
6335         });
6336         return r;
6337     }
6338     
6339     
6340 });
6341
6342  
6343 Roo.apply(Roo.bootstrap.nav.Group, {
6344     
6345     groups: {},
6346      /**
6347     * register a Navigation Group
6348     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6349     */
6350     register : function(navgrp)
6351     {
6352         this.groups[navgrp.navId] = navgrp;
6353         
6354     },
6355     /**
6356     * fetch a Navigation Group based on the navigation ID
6357     * @param {string} the navgroup to add
6358     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6359     */
6360     get: function(navId) {
6361         if (typeof(this.groups[navId]) == 'undefined') {
6362             return false;
6363             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6364         }
6365         return this.groups[navId] ;
6366     }
6367     
6368     
6369     
6370 });
6371
6372  /**
6373  * @class Roo.bootstrap.nav.Item
6374  * @extends Roo.bootstrap.Component
6375  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6376  * @parent Roo.bootstrap.nav.Group
6377  * @licence LGPL
6378  * Bootstrap Navbar.NavItem class
6379  * 
6380  * @cfg {String} href  link to
6381  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6382  * @cfg {Boolean} button_outline show and outlined button
6383  * @cfg {String} html content of button
6384  * @cfg {String} badge text inside badge
6385  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6386  * @cfg {String} glyphicon DEPRICATED - use fa
6387  * @cfg {String} icon DEPRICATED - use fa
6388  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6389  * @cfg {Boolean} active Is item active
6390  * @cfg {Boolean} disabled Is item disabled
6391  * @cfg {String} linkcls  Link Class
6392  * @cfg {Boolean} preventDefault (true | false) default false
6393  * @cfg {String} tabId the tab that this item activates.
6394  * @cfg {String} tagtype (a|span) render as a href or span?
6395  * @cfg {Boolean} animateRef (true|false) link to element default false  
6396  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6397   
6398  * @constructor
6399  * Create a new Navbar Item
6400  * @param {Object} config The config object
6401  */
6402 Roo.bootstrap.nav.Item = function(config){
6403     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6404     this.addEvents({
6405         // raw events
6406         /**
6407          * @event click
6408          * The raw click event for the entire grid.
6409          * @param {Roo.EventObject} e
6410          */
6411         "click" : true,
6412          /**
6413             * @event changed
6414             * Fires when the active item active state changes
6415             * @param {Roo.bootstrap.nav.Item} this
6416             * @param {boolean} state the new state
6417              
6418          */
6419         'changed': true,
6420         /**
6421             * @event scrollto
6422             * Fires when scroll to element
6423             * @param {Roo.bootstrap.nav.Item} this
6424             * @param {Object} options
6425             * @param {Roo.EventObject} e
6426              
6427          */
6428         'scrollto': true
6429     });
6430    
6431 };
6432
6433 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6434     
6435     href: false,
6436     html: '',
6437     badge: '',
6438     icon: false,
6439     fa : false,
6440     glyphicon: false,
6441     active: false,
6442     preventDefault : false,
6443     tabId : false,
6444     tagtype : 'a',
6445     tag: 'li',
6446     disabled : false,
6447     animateRef : false,
6448     was_active : false,
6449     button_weight : '',
6450     button_outline : false,
6451     linkcls : '',
6452     navLink: false,
6453     
6454     getAutoCreate : function(){
6455          
6456         var cfg = {
6457             tag: this.tag,
6458             cls: 'nav-item'
6459         };
6460         
6461         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6462         
6463         if (this.active) {
6464             cfg.cls +=  ' active' ;
6465         }
6466         if (this.disabled) {
6467             cfg.cls += ' disabled';
6468         }
6469         
6470         // BS4 only?
6471         if (this.button_weight.length) {
6472             cfg.tag = this.href ? 'a' : 'button';
6473             cfg.html = this.html || '';
6474             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6475             if (this.href) {
6476                 cfg.href = this.href;
6477             }
6478             if (this.fa) {
6479                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6480             } else {
6481                 cfg.cls += " nav-html";
6482             }
6483             
6484             // menu .. should add dropdown-menu class - so no need for carat..
6485             
6486             if (this.badge !== '') {
6487                  
6488                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6489             }
6490             return cfg;
6491         }
6492         
6493         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6494             cfg.cn = [
6495                 {
6496                     tag: this.tagtype,
6497                     href : this.href || "#",
6498                     html: this.html || '',
6499                     cls : ''
6500                 }
6501             ];
6502             if (this.tagtype == 'a') {
6503                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6504         
6505             }
6506             if (this.icon) {
6507                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6508             } else  if (this.fa) {
6509                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6510             } else if(this.glyphicon) {
6511                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6512             } else {
6513                 cfg.cn[0].cls += " nav-html";
6514             }
6515             
6516             if (this.menu) {
6517                 cfg.cn[0].html += " <span class='caret'></span>";
6518              
6519             }
6520             
6521             if (this.badge !== '') {
6522                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6523             }
6524         }
6525         
6526         
6527         
6528         return cfg;
6529     },
6530     onRender : function(ct, position)
6531     {
6532        // Roo.log("Call onRender: " + this.xtype);
6533         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6534             this.tag = 'div';
6535         }
6536         
6537         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6538         this.navLink = this.el.select('.nav-link',true).first();
6539         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6540         return ret;
6541     },
6542       
6543     
6544     initEvents: function() 
6545     {
6546         if (typeof (this.menu) != 'undefined') {
6547             this.menu.parentType = this.xtype;
6548             this.menu.triggerEl = this.el;
6549             this.menu = this.addxtype(Roo.apply({}, this.menu));
6550         }
6551         
6552         this.el.on('click', this.onClick, this);
6553         
6554         //if(this.tagtype == 'span'){
6555         //    this.el.select('span',true).on('click', this.onClick, this);
6556         //}
6557        
6558         // at this point parent should be available..
6559         this.parent().register(this);
6560     },
6561     
6562     onClick : function(e)
6563     {
6564         if (e.getTarget('.dropdown-menu-item')) {
6565             // did you click on a menu itemm.... - then don't trigger onclick..
6566             return;
6567         }
6568         
6569         if(
6570                 this.preventDefault ||
6571                                 this.href === false ||
6572                 this.href === '#' 
6573         ){
6574             //Roo.log("NavItem - prevent Default?");
6575             e.preventDefault();
6576         }
6577         
6578         if (this.disabled) {
6579             return;
6580         }
6581         
6582         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6583         if (tg && tg.transition) {
6584             Roo.log("waiting for the transitionend");
6585             return;
6586         }
6587         
6588         
6589         
6590         //Roo.log("fire event clicked");
6591         if(this.fireEvent('click', this, e) === false){
6592             return;
6593         };
6594         
6595         if(this.tagtype == 'span'){
6596             return;
6597         }
6598         
6599         //Roo.log(this.href);
6600         var ael = this.el.select('a',true).first();
6601         //Roo.log(ael);
6602         
6603         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6604             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6605             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6606                 return; // ignore... - it's a 'hash' to another page.
6607             }
6608             Roo.log("NavItem - prevent Default?");
6609             e.preventDefault();
6610             this.scrollToElement(e);
6611         }
6612         
6613         
6614         var p =  this.parent();
6615    
6616         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6617             if (typeof(p.setActiveItem) !== 'undefined') {
6618                 p.setActiveItem(this);
6619             }
6620         }
6621         
6622         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6623         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6624             // remove the collapsed menu expand...
6625             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6626         }
6627     },
6628     
6629     isActive: function () {
6630         return this.active
6631     },
6632     setActive : function(state, fire, is_was_active)
6633     {
6634         if (this.active && !state && this.navId) {
6635             this.was_active = true;
6636             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6637             if (nv) {
6638                 nv.clearWasActive(this);
6639             }
6640             
6641         }
6642         this.active = state;
6643         
6644         if (!state ) {
6645             this.el.removeClass('active');
6646             this.navLink ? this.navLink.removeClass('active') : false;
6647         } else if (!this.el.hasClass('active')) {
6648             
6649             this.el.addClass('active');
6650             if (Roo.bootstrap.version == 4 && this.navLink ) {
6651                 this.navLink.addClass('active');
6652             }
6653             
6654         }
6655         if (fire) {
6656             this.fireEvent('changed', this, state);
6657         }
6658         
6659         // show a panel if it's registered and related..
6660         
6661         if (!this.navId || !this.tabId || !state || is_was_active) {
6662             return;
6663         }
6664         
6665         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6666         if (!tg) {
6667             return;
6668         }
6669         var pan = tg.getPanelByName(this.tabId);
6670         if (!pan) {
6671             return;
6672         }
6673         // if we can not flip to new panel - go back to old nav highlight..
6674         if (false == tg.showPanel(pan)) {
6675             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6676             if (nv) {
6677                 var onav = nv.getWasActive();
6678                 if (onav) {
6679                     onav.setActive(true, false, true);
6680                 }
6681             }
6682             
6683         }
6684         
6685         
6686         
6687     },
6688      // this should not be here...
6689     setDisabled : function(state)
6690     {
6691         this.disabled = state;
6692         if (!state ) {
6693             this.el.removeClass('disabled');
6694         } else if (!this.el.hasClass('disabled')) {
6695             this.el.addClass('disabled');
6696         }
6697         
6698     },
6699     
6700     /**
6701      * Fetch the element to display the tooltip on.
6702      * @return {Roo.Element} defaults to this.el
6703      */
6704     tooltipEl : function()
6705     {
6706         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6707     },
6708     
6709     scrollToElement : function(e)
6710     {
6711         var c = document.body;
6712         
6713         /*
6714          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6715          */
6716         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6717             c = document.documentElement;
6718         }
6719         
6720         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6721         
6722         if(!target){
6723             return;
6724         }
6725
6726         var o = target.calcOffsetsTo(c);
6727         
6728         var options = {
6729             target : target,
6730             value : o[1]
6731         };
6732         
6733         this.fireEvent('scrollto', this, options, e);
6734         
6735         Roo.get(c).scrollTo('top', options.value, true);
6736         
6737         return;
6738     },
6739     /**
6740      * Set the HTML (text content) of the item
6741      * @param {string} html  content for the nav item
6742      */
6743     setHtml : function(html)
6744     {
6745         this.html = html;
6746         this.htmlEl.dom.innerHTML = html;
6747         
6748     } 
6749 });
6750  
6751
6752  /*
6753  * - LGPL
6754  *
6755  * sidebar item
6756  *
6757  *  li
6758  *    <span> icon </span>
6759  *    <span> text </span>
6760  *    <span>badge </span>
6761  */
6762
6763 /**
6764  * @class Roo.bootstrap.nav.SidebarItem
6765  * @extends Roo.bootstrap.nav.Item
6766  * Bootstrap Navbar.NavSidebarItem class
6767  * 
6768  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6769  * {Boolean} open is the menu open
6770  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6771  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6772  * {String} buttonSize (sm|md|lg)the extra classes for the button
6773  * {Boolean} showArrow show arrow next to the text (default true)
6774  * @constructor
6775  * Create a new Navbar Button
6776  * @param {Object} config The config object
6777  */
6778 Roo.bootstrap.nav.SidebarItem = function(config){
6779     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6780     this.addEvents({
6781         // raw events
6782         /**
6783          * @event click
6784          * The raw click event for the entire grid.
6785          * @param {Roo.EventObject} e
6786          */
6787         "click" : true,
6788          /**
6789             * @event changed
6790             * Fires when the active item active state changes
6791             * @param {Roo.bootstrap.nav.SidebarItem} this
6792             * @param {boolean} state the new state
6793              
6794          */
6795         'changed': true
6796     });
6797    
6798 };
6799
6800 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6801     
6802     badgeWeight : 'default',
6803     
6804     open: false,
6805     
6806     buttonView : false,
6807     
6808     buttonWeight : 'default',
6809     
6810     buttonSize : 'md',
6811     
6812     showArrow : true,
6813     
6814     getAutoCreate : function(){
6815         
6816         
6817         var a = {
6818                 tag: 'a',
6819                 href : this.href || '#',
6820                 cls: '',
6821                 html : '',
6822                 cn : []
6823         };
6824         
6825         if(this.buttonView){
6826             a = {
6827                 tag: 'button',
6828                 href : this.href || '#',
6829                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6830                 html : this.html,
6831                 cn : []
6832             };
6833         }
6834         
6835         var cfg = {
6836             tag: 'li',
6837             cls: '',
6838             cn: [ a ]
6839         };
6840         
6841         if (this.active) {
6842             cfg.cls += ' active';
6843         }
6844         
6845         if (this.disabled) {
6846             cfg.cls += ' disabled';
6847         }
6848         if (this.open) {
6849             cfg.cls += ' open x-open';
6850         }
6851         // left icon..
6852         if (this.glyphicon || this.icon) {
6853             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6854             a.cn.push({ tag : 'i', cls : c }) ;
6855         }
6856         
6857         if(!this.buttonView){
6858             var span = {
6859                 tag: 'span',
6860                 html : this.html || ''
6861             };
6862
6863             a.cn.push(span);
6864             
6865         }
6866         
6867         if (this.badge !== '') {
6868             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6869         }
6870         
6871         if (this.menu) {
6872             
6873             if(this.showArrow){
6874                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6875             }
6876             
6877             a.cls += ' dropdown-toggle treeview' ;
6878         }
6879         
6880         return cfg;
6881     },
6882     
6883     initEvents : function()
6884     { 
6885         if (typeof (this.menu) != 'undefined') {
6886             this.menu.parentType = this.xtype;
6887             this.menu.triggerEl = this.el;
6888             this.menu = this.addxtype(Roo.apply({}, this.menu));
6889         }
6890         
6891         this.el.on('click', this.onClick, this);
6892         
6893         if(this.badge !== ''){
6894             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6895         }
6896         
6897     },
6898     
6899     onClick : function(e)
6900     {
6901         if(this.disabled){
6902             e.preventDefault();
6903             return;
6904         }
6905         
6906         if(this.preventDefault){
6907             e.preventDefault();
6908         }
6909         
6910         this.fireEvent('click', this, e);
6911     },
6912     
6913     disable : function()
6914     {
6915         this.setDisabled(true);
6916     },
6917     
6918     enable : function()
6919     {
6920         this.setDisabled(false);
6921     },
6922     
6923     setDisabled : function(state)
6924     {
6925         if(this.disabled == state){
6926             return;
6927         }
6928         
6929         this.disabled = state;
6930         
6931         if (state) {
6932             this.el.addClass('disabled');
6933             return;
6934         }
6935         
6936         this.el.removeClass('disabled');
6937         
6938         return;
6939     },
6940     
6941     setActive : function(state)
6942     {
6943         if(this.active == state){
6944             return;
6945         }
6946         
6947         this.active = state;
6948         
6949         if (state) {
6950             this.el.addClass('active');
6951             return;
6952         }
6953         
6954         this.el.removeClass('active');
6955         
6956         return;
6957     },
6958     
6959     isActive: function () 
6960     {
6961         return this.active;
6962     },
6963     
6964     setBadge : function(str)
6965     {
6966         if(!this.badgeEl){
6967             return;
6968         }
6969         
6970         this.badgeEl.dom.innerHTML = str;
6971     }
6972     
6973    
6974      
6975  
6976 });
6977  
6978
6979  /*
6980  * - LGPL
6981  *
6982  * nav progress bar
6983  * 
6984  */
6985
6986 /**
6987  * @class Roo.bootstrap.nav.ProgressBar
6988  * @extends Roo.bootstrap.Component
6989  * @children Roo.bootstrap.nav.ProgressBarItem
6990  * Bootstrap NavProgressBar class
6991  * 
6992  * @constructor
6993  * Create a new nav progress bar - a bar indicating step along a process
6994  * @param {Object} config The config object
6995  */
6996
6997 Roo.bootstrap.nav.ProgressBar = function(config){
6998     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6999
7000     this.bullets = this.bullets || [];
7001    
7002 //    Roo.bootstrap.nav.ProgressBar.register(this);
7003      this.addEvents({
7004         /**
7005              * @event changed
7006              * Fires when the active item changes
7007              * @param {Roo.bootstrap.nav.ProgressBar} this
7008              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7009              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7010          */
7011         'changed': true
7012      });
7013     
7014 };
7015
7016 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7017     /**
7018      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7019      * Bullets for the Nav Progress bar for the toolbar
7020      */
7021     bullets : [],
7022     barItems : [],
7023     
7024     getAutoCreate : function()
7025     {
7026         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7027         
7028         cfg = {
7029             tag : 'div',
7030             cls : 'roo-navigation-bar-group',
7031             cn : [
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-top-bar'
7035                 },
7036                 {
7037                     tag : 'div',
7038                     cls : 'roo-navigation-bullets-bar',
7039                     cn : [
7040                         {
7041                             tag : 'ul',
7042                             cls : 'roo-navigation-bar'
7043                         }
7044                     ]
7045                 },
7046                 
7047                 {
7048                     tag : 'div',
7049                     cls : 'roo-navigation-bottom-bar'
7050                 }
7051             ]
7052             
7053         };
7054         
7055         return cfg;
7056         
7057     },
7058     
7059     initEvents: function() 
7060     {
7061         
7062     },
7063     
7064     onRender : function(ct, position) 
7065     {
7066         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7067         
7068         if(this.bullets.length){
7069             Roo.each(this.bullets, function(b){
7070                this.addItem(b);
7071             }, this);
7072         }
7073         
7074         this.format();
7075         
7076     },
7077     
7078     addItem : function(cfg)
7079     {
7080         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7081         
7082         item.parentId = this.id;
7083         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7084         
7085         if(cfg.html){
7086             var top = new Roo.bootstrap.Element({
7087                 tag : 'div',
7088                 cls : 'roo-navigation-bar-text'
7089             });
7090             
7091             var bottom = new Roo.bootstrap.Element({
7092                 tag : 'div',
7093                 cls : 'roo-navigation-bar-text'
7094             });
7095             
7096             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7097             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7098             
7099             var topText = new Roo.bootstrap.Element({
7100                 tag : 'span',
7101                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7102             });
7103             
7104             var bottomText = new Roo.bootstrap.Element({
7105                 tag : 'span',
7106                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7107             });
7108             
7109             topText.onRender(top.el, null);
7110             bottomText.onRender(bottom.el, null);
7111             
7112             item.topEl = top;
7113             item.bottomEl = bottom;
7114         }
7115         
7116         this.barItems.push(item);
7117         
7118         return item;
7119     },
7120     
7121     getActive : function()
7122     {
7123         var active = false;
7124         
7125         Roo.each(this.barItems, function(v){
7126             
7127             if (!v.isActive()) {
7128                 return;
7129             }
7130             
7131             active = v;
7132             return false;
7133             
7134         });
7135         
7136         return active;
7137     },
7138     
7139     setActiveItem : function(item)
7140     {
7141         var prev = false;
7142         
7143         Roo.each(this.barItems, function(v){
7144             if (v.rid == item.rid) {
7145                 return ;
7146             }
7147             
7148             if (v.isActive()) {
7149                 v.setActive(false);
7150                 prev = v;
7151             }
7152         });
7153
7154         item.setActive(true);
7155         
7156         this.fireEvent('changed', this, item, prev);
7157     },
7158     
7159     getBarItem: function(rid)
7160     {
7161         var ret = false;
7162         
7163         Roo.each(this.barItems, function(e) {
7164             if (e.rid != rid) {
7165                 return;
7166             }
7167             
7168             ret =  e;
7169             return false;
7170         });
7171         
7172         return ret;
7173     },
7174     
7175     indexOfItem : function(item)
7176     {
7177         var index = false;
7178         
7179         Roo.each(this.barItems, function(v, i){
7180             
7181             if (v.rid != item.rid) {
7182                 return;
7183             }
7184             
7185             index = i;
7186             return false
7187         });
7188         
7189         return index;
7190     },
7191     
7192     setActiveNext : function()
7193     {
7194         var i = this.indexOfItem(this.getActive());
7195         
7196         if (i > this.barItems.length) {
7197             return;
7198         }
7199         
7200         this.setActiveItem(this.barItems[i+1]);
7201     },
7202     
7203     setActivePrev : function()
7204     {
7205         var i = this.indexOfItem(this.getActive());
7206         
7207         if (i  < 1) {
7208             return;
7209         }
7210         
7211         this.setActiveItem(this.barItems[i-1]);
7212     },
7213     
7214     format : function()
7215     {
7216         if(!this.barItems.length){
7217             return;
7218         }
7219      
7220         var width = 100 / this.barItems.length;
7221         
7222         Roo.each(this.barItems, function(i){
7223             i.el.setStyle('width', width + '%');
7224             i.topEl.el.setStyle('width', width + '%');
7225             i.bottomEl.el.setStyle('width', width + '%');
7226         }, this);
7227         
7228     }
7229     
7230 });
7231 /*
7232  * - LGPL
7233  *
7234  * Nav Progress Item
7235  * 
7236  */
7237
7238 /**
7239  * @class Roo.bootstrap.nav.ProgressBarItem
7240  * @extends Roo.bootstrap.Component
7241  * Bootstrap NavProgressBarItem class
7242  * @cfg {String} rid the reference id
7243  * @cfg {Boolean} active (true|false) Is item active default false
7244  * @cfg {Boolean} disabled (true|false) Is item active default false
7245  * @cfg {String} html
7246  * @cfg {String} position (top|bottom) text position default bottom
7247  * @cfg {String} icon show icon instead of number
7248  * 
7249  * @constructor
7250  * Create a new NavProgressBarItem
7251  * @param {Object} config The config object
7252  */
7253 Roo.bootstrap.nav.ProgressBarItem = function(config){
7254     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7255     this.addEvents({
7256         // raw events
7257         /**
7258          * @event click
7259          * The raw click event for the entire grid.
7260          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7261          * @param {Roo.EventObject} e
7262          */
7263         "click" : true
7264     });
7265    
7266 };
7267
7268 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7269     
7270     rid : '',
7271     active : false,
7272     disabled : false,
7273     html : '',
7274     position : 'bottom',
7275     icon : false,
7276     
7277     getAutoCreate : function()
7278     {
7279         var iconCls = 'roo-navigation-bar-item-icon';
7280         
7281         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7282         
7283         var cfg = {
7284             tag: 'li',
7285             cls: 'roo-navigation-bar-item',
7286             cn : [
7287                 {
7288                     tag : 'i',
7289                     cls : iconCls
7290                 }
7291             ]
7292         };
7293         
7294         if(this.active){
7295             cfg.cls += ' active';
7296         }
7297         if(this.disabled){
7298             cfg.cls += ' disabled';
7299         }
7300         
7301         return cfg;
7302     },
7303     
7304     disable : function()
7305     {
7306         this.setDisabled(true);
7307     },
7308     
7309     enable : function()
7310     {
7311         this.setDisabled(false);
7312     },
7313     
7314     initEvents: function() 
7315     {
7316         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7317         
7318         this.iconEl.on('click', this.onClick, this);
7319     },
7320     
7321     onClick : function(e)
7322     {
7323         e.preventDefault();
7324         
7325         if(this.disabled){
7326             return;
7327         }
7328         
7329         if(this.fireEvent('click', this, e) === false){
7330             return;
7331         };
7332         
7333         this.parent().setActiveItem(this);
7334     },
7335     
7336     isActive: function () 
7337     {
7338         return this.active;
7339     },
7340     
7341     setActive : function(state)
7342     {
7343         if(this.active == state){
7344             return;
7345         }
7346         
7347         this.active = state;
7348         
7349         if (state) {
7350             this.el.addClass('active');
7351             return;
7352         }
7353         
7354         this.el.removeClass('active');
7355         
7356         return;
7357     },
7358     
7359     setDisabled : function(state)
7360     {
7361         if(this.disabled == state){
7362             return;
7363         }
7364         
7365         this.disabled = state;
7366         
7367         if (state) {
7368             this.el.addClass('disabled');
7369             return;
7370         }
7371         
7372         this.el.removeClass('disabled');
7373     },
7374     
7375     tooltipEl : function()
7376     {
7377         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7378     }
7379 });
7380  
7381
7382  /*
7383  * - LGPL
7384  *
7385  *  Breadcrumb Nav
7386  * 
7387  */
7388 Roo.namespace('Roo.bootstrap.breadcrumb');
7389
7390
7391 /**
7392  * @class Roo.bootstrap.breadcrumb.Nav
7393  * @extends Roo.bootstrap.Component
7394  * Bootstrap Breadcrumb Nav Class
7395  *  
7396  * @children Roo.bootstrap.breadcrumb.Item
7397  * 
7398  * @constructor
7399  * Create a new breadcrumb.Nav
7400  * @param {Object} config The config object
7401  */
7402
7403
7404 Roo.bootstrap.breadcrumb.Nav = function(config){
7405     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7406     
7407     
7408 };
7409
7410 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7411     
7412     getAutoCreate : function()
7413     {
7414
7415         var cfg = {
7416             tag: 'nav',
7417             cn : [
7418                 {
7419                     tag : 'ol',
7420                     cls : 'breadcrumb'
7421                 }
7422             ]
7423             
7424         };
7425           
7426         return cfg;
7427     },
7428     
7429     initEvents: function()
7430     {
7431         this.olEl = this.el.select('ol',true).first();    
7432     },
7433     getChildContainer : function()
7434     {
7435         return this.olEl;  
7436     }
7437     
7438 });
7439
7440  /*
7441  * - LGPL
7442  *
7443  *  Breadcrumb Item
7444  * 
7445  */
7446
7447
7448 /**
7449  * @class Roo.bootstrap.breadcrumb.Nav
7450  * @extends Roo.bootstrap.Component
7451  * @children Roo.bootstrap.Component
7452  * @parent Roo.bootstrap.breadcrumb.Nav
7453  * Bootstrap Breadcrumb Nav Class
7454  *  
7455  * 
7456  * @cfg {String} html the content of the link.
7457  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7458  * @cfg {Boolean} active is it active
7459
7460  * 
7461  * @constructor
7462  * Create a new breadcrumb.Nav
7463  * @param {Object} config The config object
7464  */
7465
7466 Roo.bootstrap.breadcrumb.Item = function(config){
7467     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7468     this.addEvents({
7469         // img events
7470         /**
7471          * @event click
7472          * The img click event for the img.
7473          * @param {Roo.EventObject} e
7474          */
7475         "click" : true
7476     });
7477     
7478 };
7479
7480 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7481     
7482     href: false,
7483     html : '',
7484     
7485     getAutoCreate : function()
7486     {
7487
7488         var cfg = {
7489             tag: 'li',
7490             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7491         };
7492         if (this.href !== false) {
7493             cfg.cn = [{
7494                 tag : 'a',
7495                 href : this.href,
7496                 html : this.html
7497             }];
7498         } else {
7499             cfg.html = this.html;
7500         }
7501         
7502         return cfg;
7503     },
7504     
7505     initEvents: function()
7506     {
7507         if (this.href) {
7508             this.el.select('a', true).first().on('click',this.onClick, this)
7509         }
7510         
7511     },
7512     onClick : function(e)
7513     {
7514         e.preventDefault();
7515         this.fireEvent('click',this,  e);
7516     }
7517     
7518 });
7519
7520  /*
7521  * - LGPL
7522  *
7523  * row
7524  * 
7525  */
7526
7527 /**
7528  * @class Roo.bootstrap.Row
7529  * @extends Roo.bootstrap.Component
7530  * @children Roo.bootstrap.Component
7531  * Bootstrap Row class (contains columns...)
7532  * 
7533  * @constructor
7534  * Create a new Row
7535  * @param {Object} config The config object
7536  */
7537
7538 Roo.bootstrap.Row = function(config){
7539     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7540 };
7541
7542 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7543     
7544     getAutoCreate : function(){
7545        return {
7546             cls: 'row clearfix'
7547        };
7548     }
7549     
7550     
7551 });
7552
7553  
7554
7555  /*
7556  * - LGPL
7557  *
7558  * pagination
7559  * 
7560  */
7561
7562 /**
7563  * @class Roo.bootstrap.Pagination
7564  * @extends Roo.bootstrap.Component
7565  * @children Roo.bootstrap.Pagination
7566  * Bootstrap Pagination class
7567  * 
7568  * @cfg {String} size (xs|sm|md|lg|xl)
7569  * @cfg {Boolean} inverse 
7570  * 
7571  * @constructor
7572  * Create a new Pagination
7573  * @param {Object} config The config object
7574  */
7575
7576 Roo.bootstrap.Pagination = function(config){
7577     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7578 };
7579
7580 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7581     
7582     cls: false,
7583     size: false,
7584     inverse: false,
7585     
7586     getAutoCreate : function(){
7587         var cfg = {
7588             tag: 'ul',
7589                 cls: 'pagination'
7590         };
7591         if (this.inverse) {
7592             cfg.cls += ' inverse';
7593         }
7594         if (this.html) {
7595             cfg.html=this.html;
7596         }
7597         if (this.cls) {
7598             cfg.cls += " " + this.cls;
7599         }
7600         return cfg;
7601     }
7602    
7603 });
7604
7605  
7606
7607  /*
7608  * - LGPL
7609  *
7610  * Pagination item
7611  * 
7612  */
7613
7614
7615 /**
7616  * @class Roo.bootstrap.PaginationItem
7617  * @extends Roo.bootstrap.Component
7618  * Bootstrap PaginationItem class
7619  * @cfg {String} html text
7620  * @cfg {String} href the link
7621  * @cfg {Boolean} preventDefault (true | false) default true
7622  * @cfg {Boolean} active (true | false) default false
7623  * @cfg {Boolean} disabled default false
7624  * 
7625  * 
7626  * @constructor
7627  * Create a new PaginationItem
7628  * @param {Object} config The config object
7629  */
7630
7631
7632 Roo.bootstrap.PaginationItem = function(config){
7633     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7634     this.addEvents({
7635         // raw events
7636         /**
7637          * @event click
7638          * The raw click event for the entire grid.
7639          * @param {Roo.EventObject} e
7640          */
7641         "click" : true
7642     });
7643 };
7644
7645 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7646     
7647     href : false,
7648     html : false,
7649     preventDefault: true,
7650     active : false,
7651     cls : false,
7652     disabled: false,
7653     
7654     getAutoCreate : function(){
7655         var cfg= {
7656             tag: 'li',
7657             cn: [
7658                 {
7659                     tag : 'a',
7660                     href : this.href ? this.href : '#',
7661                     html : this.html ? this.html : ''
7662                 }
7663             ]
7664         };
7665         
7666         if(this.cls){
7667             cfg.cls = this.cls;
7668         }
7669         
7670         if(this.disabled){
7671             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7672         }
7673         
7674         if(this.active){
7675             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7676         }
7677         
7678         return cfg;
7679     },
7680     
7681     initEvents: function() {
7682         
7683         this.el.on('click', this.onClick, this);
7684         
7685     },
7686     onClick : function(e)
7687     {
7688         Roo.log('PaginationItem on click ');
7689         if(this.preventDefault){
7690             e.preventDefault();
7691         }
7692         
7693         if(this.disabled){
7694             return;
7695         }
7696         
7697         this.fireEvent('click', this, e);
7698     }
7699    
7700 });
7701
7702  
7703
7704  /*
7705  * - LGPL
7706  *
7707  * slider
7708  * 
7709  */
7710
7711
7712 /**
7713  * @class Roo.bootstrap.Slider
7714  * @extends Roo.bootstrap.Component
7715  * Bootstrap Slider class
7716  *    
7717  * @constructor
7718  * Create a new Slider
7719  * @param {Object} config The config object
7720  */
7721
7722 Roo.bootstrap.Slider = function(config){
7723     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7724 };
7725
7726 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7727     
7728     getAutoCreate : function(){
7729         
7730         var cfg = {
7731             tag: 'div',
7732             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7733             cn: [
7734                 {
7735                     tag: 'a',
7736                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7737                 }
7738             ]
7739         };
7740         
7741         return cfg;
7742     }
7743    
7744 });
7745
7746  /*
7747  * Based on:
7748  * Ext JS Library 1.1.1
7749  * Copyright(c) 2006-2007, Ext JS, LLC.
7750  *
7751  * Originally Released Under LGPL - original licence link has changed is not relivant.
7752  *
7753  * Fork - LGPL
7754  * <script type="text/javascript">
7755  */
7756  /**
7757  * @extends Roo.dd.DDProxy
7758  * @class Roo.grid.SplitDragZone
7759  * Support for Column Header resizing
7760  * @constructor
7761  * @param {Object} config
7762  */
7763 // private
7764 // This is a support class used internally by the Grid components
7765 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7766     this.grid = grid;
7767     this.view = grid.getView();
7768     this.proxy = this.view.resizeProxy;
7769     Roo.grid.SplitDragZone.superclass.constructor.call(
7770         this,
7771         hd, // ID
7772         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7773         {  // CONFIG
7774             dragElId : Roo.id(this.proxy.dom),
7775             resizeFrame:false
7776         }
7777     );
7778     
7779     this.setHandleElId(Roo.id(hd));
7780     if (hd2 !== false) {
7781         this.setOuterHandleElId(Roo.id(hd2));
7782     }
7783     
7784     this.scroll = false;
7785 };
7786 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7787     fly: Roo.Element.fly,
7788
7789     b4StartDrag : function(x, y){
7790         this.view.headersDisabled = true;
7791         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7792                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7793         );
7794         this.proxy.setHeight(h);
7795         
7796         // for old system colWidth really stored the actual width?
7797         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7798         // which in reality did not work.. - it worked only for fixed sizes
7799         // for resizable we need to use actual sizes.
7800         var w = this.cm.getColumnWidth(this.cellIndex);
7801         if (!this.view.mainWrap) {
7802             // bootstrap.
7803             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7804         }
7805         
7806         
7807         
7808         // this was w-this.grid.minColumnWidth;
7809         // doesnt really make sense? - w = thie curren width or the rendered one?
7810         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7811         this.resetConstraints();
7812         this.setXConstraint(minw, 1000);
7813         this.setYConstraint(0, 0);
7814         this.minX = x - minw;
7815         this.maxX = x + 1000;
7816         this.startPos = x;
7817         if (!this.view.mainWrap) { // this is Bootstrap code..
7818             this.getDragEl().style.display='block';
7819         }
7820         
7821         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7822     },
7823
7824
7825     handleMouseDown : function(e){
7826         ev = Roo.EventObject.setEvent(e);
7827         var t = this.fly(ev.getTarget());
7828         if(t.hasClass("x-grid-split")){
7829             this.cellIndex = this.view.getCellIndex(t.dom);
7830             this.split = t.dom;
7831             this.cm = this.grid.colModel;
7832             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7833                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7834             }
7835         }
7836     },
7837
7838     endDrag : function(e){
7839         this.view.headersDisabled = false;
7840         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7841         var diff = endX - this.startPos;
7842         // 
7843         var w = this.cm.getColumnWidth(this.cellIndex);
7844         if (!this.view.mainWrap) {
7845             w = 0;
7846         }
7847         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7848     },
7849
7850     autoOffset : function(){
7851         this.setDelta(0,0);
7852     }
7853 });/*
7854  * Based on:
7855  * Ext JS Library 1.1.1
7856  * Copyright(c) 2006-2007, Ext JS, LLC.
7857  *
7858  * Originally Released Under LGPL - original licence link has changed is not relivant.
7859  *
7860  * Fork - LGPL
7861  * <script type="text/javascript">
7862  */
7863
7864 /**
7865  * @class Roo.grid.AbstractSelectionModel
7866  * @extends Roo.util.Observable
7867  * @abstract
7868  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7869  * implemented by descendant classes.  This class should not be directly instantiated.
7870  * @constructor
7871  */
7872 Roo.grid.AbstractSelectionModel = function(){
7873     this.locked = false;
7874     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7875 };
7876
7877 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7878     /** @ignore Called by the grid automatically. Do not call directly. */
7879     init : function(grid){
7880         this.grid = grid;
7881         this.initEvents();
7882     },
7883
7884     /**
7885      * Locks the selections.
7886      */
7887     lock : function(){
7888         this.locked = true;
7889     },
7890
7891     /**
7892      * Unlocks the selections.
7893      */
7894     unlock : function(){
7895         this.locked = false;
7896     },
7897
7898     /**
7899      * Returns true if the selections are locked.
7900      * @return {Boolean}
7901      */
7902     isLocked : function(){
7903         return this.locked;
7904     }
7905 });/*
7906  * Based on:
7907  * Ext JS Library 1.1.1
7908  * Copyright(c) 2006-2007, Ext JS, LLC.
7909  *
7910  * Originally Released Under LGPL - original licence link has changed is not relivant.
7911  *
7912  * Fork - LGPL
7913  * <script type="text/javascript">
7914  */
7915 /**
7916  * @extends Roo.grid.AbstractSelectionModel
7917  * @class Roo.grid.RowSelectionModel
7918  * The default SelectionModel used by {@link Roo.grid.Grid}.
7919  * It supports multiple selections and keyboard selection/navigation. 
7920  * @constructor
7921  * @param {Object} config
7922  */
7923 Roo.grid.RowSelectionModel = function(config){
7924     Roo.apply(this, config);
7925     this.selections = new Roo.util.MixedCollection(false, function(o){
7926         return o.id;
7927     });
7928
7929     this.last = false;
7930     this.lastActive = false;
7931
7932     this.addEvents({
7933         /**
7934         * @event selectionchange
7935         * Fires when the selection changes
7936         * @param {SelectionModel} this
7937         */
7938        "selectionchange" : true,
7939        /**
7940         * @event afterselectionchange
7941         * Fires after the selection changes (eg. by key press or clicking)
7942         * @param {SelectionModel} this
7943         */
7944        "afterselectionchange" : true,
7945        /**
7946         * @event beforerowselect
7947         * Fires when a row is selected being selected, return false to cancel.
7948         * @param {SelectionModel} this
7949         * @param {Number} rowIndex The selected index
7950         * @param {Boolean} keepExisting False if other selections will be cleared
7951         */
7952        "beforerowselect" : true,
7953        /**
7954         * @event rowselect
7955         * Fires when a row is selected.
7956         * @param {SelectionModel} this
7957         * @param {Number} rowIndex The selected index
7958         * @param {Roo.data.Record} r The record
7959         */
7960        "rowselect" : true,
7961        /**
7962         * @event rowdeselect
7963         * Fires when a row is deselected.
7964         * @param {SelectionModel} this
7965         * @param {Number} rowIndex The selected index
7966         */
7967         "rowdeselect" : true
7968     });
7969     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7970     this.locked = false;
7971 };
7972
7973 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7974     /**
7975      * @cfg {Boolean} singleSelect
7976      * True to allow selection of only one row at a time (defaults to false)
7977      */
7978     singleSelect : false,
7979
7980     // private
7981     initEvents : function(){
7982
7983         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7984             this.grid.on("mousedown", this.handleMouseDown, this);
7985         }else{ // allow click to work like normal
7986             this.grid.on("rowclick", this.handleDragableRowClick, this);
7987         }
7988         // bootstrap does not have a view..
7989         var view = this.grid.view ? this.grid.view : this.grid;
7990         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7991             "up" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectPrevious(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive-1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             "down" : function(e){
8007                 if(!e.shiftKey){
8008                     this.selectNext(e.shiftKey);
8009                 }else if(this.last !== false && this.lastActive !== false){
8010                     var last = this.last;
8011                     this.selectRange(this.last,  this.lastActive+1);
8012                     view.focusRow(this.lastActive);
8013                     if(last !== false){
8014                         this.last = last;
8015                     }
8016                 }else{
8017                     this.selectFirstRow();
8018                 }
8019                 this.fireEvent("afterselectionchange", this);
8020             },
8021             scope: this
8022         });
8023
8024          
8025         view.on("refresh", this.onRefresh, this);
8026         view.on("rowupdated", this.onRowUpdated, this);
8027         view.on("rowremoved", this.onRemove, this);
8028     },
8029
8030     // private
8031     onRefresh : function(){
8032         var ds = this.grid.ds, i, v = this.grid.view;
8033         var s = this.selections;
8034         s.each(function(r){
8035             if((i = ds.indexOfId(r.id)) != -1){
8036                 v.onRowSelect(i);
8037                 s.add(ds.getAt(i)); // updating the selection relate data
8038             }else{
8039                 s.remove(r);
8040             }
8041         });
8042     },
8043
8044     // private
8045     onRemove : function(v, index, r){
8046         this.selections.remove(r);
8047     },
8048
8049     // private
8050     onRowUpdated : function(v, index, r){
8051         if(this.isSelected(r)){
8052             v.onRowSelect(index);
8053         }
8054     },
8055
8056     /**
8057      * Select records.
8058      * @param {Array} records The records to select
8059      * @param {Boolean} keepExisting (optional) True to keep existing selections
8060      */
8061     selectRecords : function(records, keepExisting){
8062         if(!keepExisting){
8063             this.clearSelections();
8064         }
8065         var ds = this.grid.ds;
8066         for(var i = 0, len = records.length; i < len; i++){
8067             this.selectRow(ds.indexOf(records[i]), true);
8068         }
8069     },
8070
8071     /**
8072      * Gets the number of selected rows.
8073      * @return {Number}
8074      */
8075     getCount : function(){
8076         return this.selections.length;
8077     },
8078
8079     /**
8080      * Selects the first row in the grid.
8081      */
8082     selectFirstRow : function(){
8083         this.selectRow(0);
8084     },
8085
8086     /**
8087      * Select the last row.
8088      * @param {Boolean} keepExisting (optional) True to keep existing selections
8089      */
8090     selectLastRow : function(keepExisting){
8091         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8092     },
8093
8094     /**
8095      * Selects the row immediately following the last selected row.
8096      * @param {Boolean} keepExisting (optional) True to keep existing selections
8097      */
8098     selectNext : function(keepExisting){
8099         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8100             this.selectRow(this.last+1, keepExisting);
8101             var view = this.grid.view ? this.grid.view : this.grid;
8102             view.focusRow(this.last);
8103         }
8104     },
8105
8106     /**
8107      * Selects the row that precedes the last selected row.
8108      * @param {Boolean} keepExisting (optional) True to keep existing selections
8109      */
8110     selectPrevious : function(keepExisting){
8111         if(this.last){
8112             this.selectRow(this.last-1, keepExisting);
8113             var view = this.grid.view ? this.grid.view : this.grid;
8114             view.focusRow(this.last);
8115         }
8116     },
8117
8118     /**
8119      * Returns the selected records
8120      * @return {Array} Array of selected records
8121      */
8122     getSelections : function(){
8123         return [].concat(this.selections.items);
8124     },
8125
8126     /**
8127      * Returns the first selected record.
8128      * @return {Record}
8129      */
8130     getSelected : function(){
8131         return this.selections.itemAt(0);
8132     },
8133
8134
8135     /**
8136      * Clears all selections.
8137      */
8138     clearSelections : function(fast){
8139         if(this.locked) {
8140             return;
8141         }
8142         if(fast !== true){
8143             var ds = this.grid.ds;
8144             var s = this.selections;
8145             s.each(function(r){
8146                 this.deselectRow(ds.indexOfId(r.id));
8147             }, this);
8148             s.clear();
8149         }else{
8150             this.selections.clear();
8151         }
8152         this.last = false;
8153     },
8154
8155
8156     /**
8157      * Selects all rows.
8158      */
8159     selectAll : function(){
8160         if(this.locked) {
8161             return;
8162         }
8163         this.selections.clear();
8164         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8165             this.selectRow(i, true);
8166         }
8167     },
8168
8169     /**
8170      * Returns True if there is a selection.
8171      * @return {Boolean}
8172      */
8173     hasSelection : function(){
8174         return this.selections.length > 0;
8175     },
8176
8177     /**
8178      * Returns True if the specified row is selected.
8179      * @param {Number/Record} record The record or index of the record to check
8180      * @return {Boolean}
8181      */
8182     isSelected : function(index){
8183         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8184         return (r && this.selections.key(r.id) ? true : false);
8185     },
8186
8187     /**
8188      * Returns True if the specified record id is selected.
8189      * @param {String} id The id of record to check
8190      * @return {Boolean}
8191      */
8192     isIdSelected : function(id){
8193         return (this.selections.key(id) ? true : false);
8194     },
8195
8196     // private
8197     handleMouseDown : function(e, t)
8198     {
8199         var view = this.grid.view ? this.grid.view : this.grid;
8200         var rowIndex;
8201         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8202             return;
8203         };
8204         if(e.shiftKey && this.last !== false){
8205             var last = this.last;
8206             this.selectRange(last, rowIndex, e.ctrlKey);
8207             this.last = last; // reset the last
8208             view.focusRow(rowIndex);
8209         }else{
8210             var isSelected = this.isSelected(rowIndex);
8211             if(e.button !== 0 && isSelected){
8212                 view.focusRow(rowIndex);
8213             }else if(e.ctrlKey && isSelected){
8214                 this.deselectRow(rowIndex);
8215             }else if(!isSelected){
8216                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8217                 view.focusRow(rowIndex);
8218             }
8219         }
8220         this.fireEvent("afterselectionchange", this);
8221     },
8222     // private
8223     handleDragableRowClick :  function(grid, rowIndex, e) 
8224     {
8225         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8226             this.selectRow(rowIndex, false);
8227             var view = this.grid.view ? this.grid.view : this.grid;
8228             view.focusRow(rowIndex);
8229              this.fireEvent("afterselectionchange", this);
8230         }
8231     },
8232     
8233     /**
8234      * Selects multiple rows.
8235      * @param {Array} rows Array of the indexes of the row to select
8236      * @param {Boolean} keepExisting (optional) True to keep existing selections
8237      */
8238     selectRows : function(rows, keepExisting){
8239         if(!keepExisting){
8240             this.clearSelections();
8241         }
8242         for(var i = 0, len = rows.length; i < len; i++){
8243             this.selectRow(rows[i], true);
8244         }
8245     },
8246
8247     /**
8248      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8249      * @param {Number} startRow The index of the first row in the range
8250      * @param {Number} endRow The index of the last row in the range
8251      * @param {Boolean} keepExisting (optional) True to retain existing selections
8252      */
8253     selectRange : function(startRow, endRow, keepExisting){
8254         if(this.locked) {
8255             return;
8256         }
8257         if(!keepExisting){
8258             this.clearSelections();
8259         }
8260         if(startRow <= endRow){
8261             for(var i = startRow; i <= endRow; i++){
8262                 this.selectRow(i, true);
8263             }
8264         }else{
8265             for(var i = startRow; i >= endRow; i--){
8266                 this.selectRow(i, true);
8267             }
8268         }
8269     },
8270
8271     /**
8272      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8273      * @param {Number} startRow The index of the first row in the range
8274      * @param {Number} endRow The index of the last row in the range
8275      */
8276     deselectRange : function(startRow, endRow, preventViewNotify){
8277         if(this.locked) {
8278             return;
8279         }
8280         for(var i = startRow; i <= endRow; i++){
8281             this.deselectRow(i, preventViewNotify);
8282         }
8283     },
8284
8285     /**
8286      * Selects a row.
8287      * @param {Number} row The index of the row to select
8288      * @param {Boolean} keepExisting (optional) True to keep existing selections
8289      */
8290     selectRow : function(index, keepExisting, preventViewNotify){
8291         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8292             return;
8293         }
8294         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8295             if(!keepExisting || this.singleSelect){
8296                 this.clearSelections();
8297             }
8298             var r = this.grid.ds.getAt(index);
8299             this.selections.add(r);
8300             this.last = this.lastActive = index;
8301             if(!preventViewNotify){
8302                 var view = this.grid.view ? this.grid.view : this.grid;
8303                 view.onRowSelect(index);
8304             }
8305             this.fireEvent("rowselect", this, index, r);
8306             this.fireEvent("selectionchange", this);
8307         }
8308     },
8309
8310     /**
8311      * Deselects a row.
8312      * @param {Number} row The index of the row to deselect
8313      */
8314     deselectRow : function(index, preventViewNotify){
8315         if(this.locked) {
8316             return;
8317         }
8318         if(this.last == index){
8319             this.last = false;
8320         }
8321         if(this.lastActive == index){
8322             this.lastActive = false;
8323         }
8324         var r = this.grid.ds.getAt(index);
8325         this.selections.remove(r);
8326         if(!preventViewNotify){
8327             var view = this.grid.view ? this.grid.view : this.grid;
8328             view.onRowDeselect(index);
8329         }
8330         this.fireEvent("rowdeselect", this, index);
8331         this.fireEvent("selectionchange", this);
8332     },
8333
8334     // private
8335     restoreLast : function(){
8336         if(this._last){
8337             this.last = this._last;
8338         }
8339     },
8340
8341     // private
8342     acceptsNav : function(row, col, cm){
8343         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8344     },
8345
8346     // private
8347     onEditorKey : function(field, e){
8348         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8349         if(k == e.TAB){
8350             e.stopEvent();
8351             ed.completeEdit();
8352             if(e.shiftKey){
8353                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8354             }else{
8355                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8356             }
8357         }else if(k == e.ENTER && !e.ctrlKey){
8358             e.stopEvent();
8359             ed.completeEdit();
8360             if(e.shiftKey){
8361                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8362             }else{
8363                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8364             }
8365         }else if(k == e.ESC){
8366             ed.cancelEdit();
8367         }
8368         if(newCell){
8369             g.startEditing(newCell[0], newCell[1]);
8370         }
8371     }
8372 });/*
8373  * Based on:
8374  * Ext JS Library 1.1.1
8375  * Copyright(c) 2006-2007, Ext JS, LLC.
8376  *
8377  * Originally Released Under LGPL - original licence link has changed is not relivant.
8378  *
8379  * Fork - LGPL
8380  * <script type="text/javascript">
8381  */
8382  
8383
8384 /**
8385  * @class Roo.grid.ColumnModel
8386  * @extends Roo.util.Observable
8387  * This is the default implementation of a ColumnModel used by the Grid. It defines
8388  * the columns in the grid.
8389  * <br>Usage:<br>
8390  <pre><code>
8391  var colModel = new Roo.grid.ColumnModel([
8392         {header: "Ticker", width: 60, sortable: true, locked: true},
8393         {header: "Company Name", width: 150, sortable: true},
8394         {header: "Market Cap.", width: 100, sortable: true},
8395         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8396         {header: "Employees", width: 100, sortable: true, resizable: false}
8397  ]);
8398  </code></pre>
8399  * <p>
8400  
8401  * The config options listed for this class are options which may appear in each
8402  * individual column definition.
8403  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8404  * @constructor
8405  * @param {Object} config An Array of column config objects. See this class's
8406  * config objects for details.
8407 */
8408 Roo.grid.ColumnModel = function(config){
8409         /**
8410      * The config passed into the constructor
8411      */
8412     this.config = []; //config;
8413     this.lookup = {};
8414
8415     // if no id, create one
8416     // if the column does not have a dataIndex mapping,
8417     // map it to the order it is in the config
8418     for(var i = 0, len = config.length; i < len; i++){
8419         this.addColumn(config[i]);
8420         
8421     }
8422
8423     /**
8424      * The width of columns which have no width specified (defaults to 100)
8425      * @type Number
8426      */
8427     this.defaultWidth = 100;
8428
8429     /**
8430      * Default sortable of columns which have no sortable specified (defaults to false)
8431      * @type Boolean
8432      */
8433     this.defaultSortable = false;
8434
8435     this.addEvents({
8436         /**
8437              * @event widthchange
8438              * Fires when the width of a column changes.
8439              * @param {ColumnModel} this
8440              * @param {Number} columnIndex The column index
8441              * @param {Number} newWidth The new width
8442              */
8443             "widthchange": true,
8444         /**
8445              * @event headerchange
8446              * Fires when the text of a header changes.
8447              * @param {ColumnModel} this
8448              * @param {Number} columnIndex The column index
8449              * @param {Number} newText The new header text
8450              */
8451             "headerchange": true,
8452         /**
8453              * @event hiddenchange
8454              * Fires when a column is hidden or "unhidden".
8455              * @param {ColumnModel} this
8456              * @param {Number} columnIndex The column index
8457              * @param {Boolean} hidden true if hidden, false otherwise
8458              */
8459             "hiddenchange": true,
8460             /**
8461          * @event columnmoved
8462          * Fires when a column is moved.
8463          * @param {ColumnModel} this
8464          * @param {Number} oldIndex
8465          * @param {Number} newIndex
8466          */
8467         "columnmoved" : true,
8468         /**
8469          * @event columlockchange
8470          * Fires when a column's locked state is changed
8471          * @param {ColumnModel} this
8472          * @param {Number} colIndex
8473          * @param {Boolean} locked true if locked
8474          */
8475         "columnlockchange" : true
8476     });
8477     Roo.grid.ColumnModel.superclass.constructor.call(this);
8478 };
8479 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8480     /**
8481      * @cfg {String} header [required] The header text to display in the Grid view.
8482      */
8483         /**
8484      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8485      */
8486         /**
8487      * @cfg {String} smHeader Header at Bootsrap Small width
8488      */
8489         /**
8490      * @cfg {String} mdHeader Header at Bootsrap Medium width
8491      */
8492         /**
8493      * @cfg {String} lgHeader Header at Bootsrap Large width
8494      */
8495         /**
8496      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8497      */
8498     /**
8499      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8500      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8501      * specified, the column's index is used as an index into the Record's data Array.
8502      */
8503     /**
8504      * @cfg {Number} width  The initial width in pixels of the column. Using this
8505      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8506      */
8507     /**
8508      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8509      * Defaults to the value of the {@link #defaultSortable} property.
8510      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8511      */
8512     /**
8513      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8514      */
8515     /**
8516      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8517      */
8518     /**
8519      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8520      */
8521     /**
8522      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8523      */
8524     /**
8525      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8526      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8527      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8528      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8529      */
8530        /**
8531      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8532      */
8533     /**
8534      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8535      */
8536     /**
8537      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8538      */
8539     /**
8540      * @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)
8541      */
8542     /**
8543      * @cfg {String} tooltip mouse over tooltip text
8544      */
8545     /**
8546      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8547      */
8548     /**
8549      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8550      */
8551     /**
8552      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8553      */
8554     /**
8555      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8556      */
8557         /**
8558      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8559      */
8560     /**
8561      * Returns the id of the column at the specified index.
8562      * @param {Number} index The column index
8563      * @return {String} the id
8564      */
8565     getColumnId : function(index){
8566         return this.config[index].id;
8567     },
8568
8569     /**
8570      * Returns the column for a specified id.
8571      * @param {String} id The column id
8572      * @return {Object} the column
8573      */
8574     getColumnById : function(id){
8575         return this.lookup[id];
8576     },
8577
8578     
8579     /**
8580      * Returns the column Object for a specified dataIndex.
8581      * @param {String} dataIndex The column dataIndex
8582      * @return {Object|Boolean} the column or false if not found
8583      */
8584     getColumnByDataIndex: function(dataIndex){
8585         var index = this.findColumnIndex(dataIndex);
8586         return index > -1 ? this.config[index] : false;
8587     },
8588     
8589     /**
8590      * Returns the index for a specified column id.
8591      * @param {String} id The column id
8592      * @return {Number} the index, or -1 if not found
8593      */
8594     getIndexById : function(id){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].id == id){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     /**
8604      * Returns the index for a specified column dataIndex.
8605      * @param {String} dataIndex The column dataIndex
8606      * @return {Number} the index, or -1 if not found
8607      */
8608     
8609     findColumnIndex : function(dataIndex){
8610         for(var i = 0, len = this.config.length; i < len; i++){
8611             if(this.config[i].dataIndex == dataIndex){
8612                 return i;
8613             }
8614         }
8615         return -1;
8616     },
8617     
8618     
8619     moveColumn : function(oldIndex, newIndex){
8620         var c = this.config[oldIndex];
8621         this.config.splice(oldIndex, 1);
8622         this.config.splice(newIndex, 0, c);
8623         this.dataMap = null;
8624         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8625     },
8626
8627     isLocked : function(colIndex){
8628         return this.config[colIndex].locked === true;
8629     },
8630
8631     setLocked : function(colIndex, value, suppressEvent){
8632         if(this.isLocked(colIndex) == value){
8633             return;
8634         }
8635         this.config[colIndex].locked = value;
8636         if(!suppressEvent){
8637             this.fireEvent("columnlockchange", this, colIndex, value);
8638         }
8639     },
8640
8641     getTotalLockedWidth : function(){
8642         var totalWidth = 0;
8643         for(var i = 0; i < this.config.length; i++){
8644             if(this.isLocked(i) && !this.isHidden(i)){
8645                 this.totalWidth += this.getColumnWidth(i);
8646             }
8647         }
8648         return totalWidth;
8649     },
8650
8651     getLockedCount : function(){
8652         for(var i = 0, len = this.config.length; i < len; i++){
8653             if(!this.isLocked(i)){
8654                 return i;
8655             }
8656         }
8657         
8658         return this.config.length;
8659     },
8660
8661     /**
8662      * Returns the number of columns.
8663      * @return {Number}
8664      */
8665     getColumnCount : function(visibleOnly){
8666         if(visibleOnly === true){
8667             var c = 0;
8668             for(var i = 0, len = this.config.length; i < len; i++){
8669                 if(!this.isHidden(i)){
8670                     c++;
8671                 }
8672             }
8673             return c;
8674         }
8675         return this.config.length;
8676     },
8677
8678     /**
8679      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8680      * @param {Function} fn
8681      * @param {Object} scope (optional)
8682      * @return {Array} result
8683      */
8684     getColumnsBy : function(fn, scope){
8685         var r = [];
8686         for(var i = 0, len = this.config.length; i < len; i++){
8687             var c = this.config[i];
8688             if(fn.call(scope||this, c, i) === true){
8689                 r[r.length] = c;
8690             }
8691         }
8692         return r;
8693     },
8694
8695     /**
8696      * Returns true if the specified column is sortable.
8697      * @param {Number} col The column index
8698      * @return {Boolean}
8699      */
8700     isSortable : function(col){
8701         if(typeof this.config[col].sortable == "undefined"){
8702             return this.defaultSortable;
8703         }
8704         return this.config[col].sortable;
8705     },
8706
8707     /**
8708      * Returns the rendering (formatting) function defined for the column.
8709      * @param {Number} col The column index.
8710      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8711      */
8712     getRenderer : function(col){
8713         if(!this.config[col].renderer){
8714             return Roo.grid.ColumnModel.defaultRenderer;
8715         }
8716         return this.config[col].renderer;
8717     },
8718
8719     /**
8720      * Sets the rendering (formatting) function for a column.
8721      * @param {Number} col The column index
8722      * @param {Function} fn The function to use to process the cell's raw data
8723      * to return HTML markup for the grid view. The render function is called with
8724      * the following parameters:<ul>
8725      * <li>Data value.</li>
8726      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8727      * <li>css A CSS style string to apply to the table cell.</li>
8728      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8729      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8730      * <li>Row index</li>
8731      * <li>Column index</li>
8732      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8733      */
8734     setRenderer : function(col, fn){
8735         this.config[col].renderer = fn;
8736     },
8737
8738     /**
8739      * Returns the width for the specified column.
8740      * @param {Number} col The column index
8741      * @param (optional) {String} gridSize bootstrap width size.
8742      * @return {Number}
8743      */
8744     getColumnWidth : function(col, gridSize)
8745         {
8746                 var cfg = this.config[col];
8747                 
8748                 if (typeof(gridSize) == 'undefined') {
8749                         return cfg.width * 1 || this.defaultWidth;
8750                 }
8751                 if (gridSize === false) { // if we set it..
8752                         return cfg.width || false;
8753                 }
8754                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8755                 
8756                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8757                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8758                                 continue;
8759                         }
8760                         return cfg[ sizes[i] ];
8761                 }
8762                 return 1;
8763                 
8764     },
8765
8766     /**
8767      * Sets the width for a column.
8768      * @param {Number} col The column index
8769      * @param {Number} width The new width
8770      */
8771     setColumnWidth : function(col, width, suppressEvent){
8772         this.config[col].width = width;
8773         this.totalWidth = null;
8774         if(!suppressEvent){
8775              this.fireEvent("widthchange", this, col, width);
8776         }
8777     },
8778
8779     /**
8780      * Returns the total width of all columns.
8781      * @param {Boolean} includeHidden True to include hidden column widths
8782      * @return {Number}
8783      */
8784     getTotalWidth : function(includeHidden){
8785         if(!this.totalWidth){
8786             this.totalWidth = 0;
8787             for(var i = 0, len = this.config.length; i < len; i++){
8788                 if(includeHidden || !this.isHidden(i)){
8789                     this.totalWidth += this.getColumnWidth(i);
8790                 }
8791             }
8792         }
8793         return this.totalWidth;
8794     },
8795
8796     /**
8797      * Returns the header for the specified column.
8798      * @param {Number} col The column index
8799      * @return {String}
8800      */
8801     getColumnHeader : function(col){
8802         return this.config[col].header;
8803     },
8804
8805     /**
8806      * Sets the header for a column.
8807      * @param {Number} col The column index
8808      * @param {String} header The new header
8809      */
8810     setColumnHeader : function(col, header){
8811         this.config[col].header = header;
8812         this.fireEvent("headerchange", this, col, header);
8813     },
8814
8815     /**
8816      * Returns the tooltip for the specified column.
8817      * @param {Number} col The column index
8818      * @return {String}
8819      */
8820     getColumnTooltip : function(col){
8821             return this.config[col].tooltip;
8822     },
8823     /**
8824      * Sets the tooltip for a column.
8825      * @param {Number} col The column index
8826      * @param {String} tooltip The new tooltip
8827      */
8828     setColumnTooltip : function(col, tooltip){
8829             this.config[col].tooltip = tooltip;
8830     },
8831
8832     /**
8833      * Returns the dataIndex for the specified column.
8834      * @param {Number} col The column index
8835      * @return {Number}
8836      */
8837     getDataIndex : function(col){
8838         return this.config[col].dataIndex;
8839     },
8840
8841     /**
8842      * Sets the dataIndex for a column.
8843      * @param {Number} col The column index
8844      * @param {Number} dataIndex The new dataIndex
8845      */
8846     setDataIndex : function(col, dataIndex){
8847         this.config[col].dataIndex = dataIndex;
8848     },
8849
8850     
8851     
8852     /**
8853      * Returns true if the cell is editable.
8854      * @param {Number} colIndex The column index
8855      * @param {Number} rowIndex The row index - this is nto actually used..?
8856      * @return {Boolean}
8857      */
8858     isCellEditable : function(colIndex, rowIndex){
8859         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8860     },
8861
8862     /**
8863      * Returns the editor defined for the cell/column.
8864      * return false or null to disable editing.
8865      * @param {Number} colIndex The column index
8866      * @param {Number} rowIndex The row index
8867      * @return {Object}
8868      */
8869     getCellEditor : function(colIndex, rowIndex){
8870         return this.config[colIndex].editor;
8871     },
8872
8873     /**
8874      * Sets if a column is editable.
8875      * @param {Number} col The column index
8876      * @param {Boolean} editable True if the column is editable
8877      */
8878     setEditable : function(col, editable){
8879         this.config[col].editable = editable;
8880     },
8881
8882
8883     /**
8884      * Returns true if the column is hidden.
8885      * @param {Number} colIndex The column index
8886      * @return {Boolean}
8887      */
8888     isHidden : function(colIndex){
8889         return this.config[colIndex].hidden;
8890     },
8891
8892
8893     /**
8894      * Returns true if the column width cannot be changed
8895      */
8896     isFixed : function(colIndex){
8897         return this.config[colIndex].fixed;
8898     },
8899
8900     /**
8901      * Returns true if the column can be resized
8902      * @return {Boolean}
8903      */
8904     isResizable : function(colIndex){
8905         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8906     },
8907     /**
8908      * Sets if a column is hidden.
8909      * @param {Number} colIndex The column index
8910      * @param {Boolean} hidden True if the column is hidden
8911      */
8912     setHidden : function(colIndex, hidden){
8913         this.config[colIndex].hidden = hidden;
8914         this.totalWidth = null;
8915         this.fireEvent("hiddenchange", this, colIndex, hidden);
8916     },
8917
8918     /**
8919      * Sets the editor for a column.
8920      * @param {Number} col The column index
8921      * @param {Object} editor The editor object
8922      */
8923     setEditor : function(col, editor){
8924         this.config[col].editor = editor;
8925     },
8926     /**
8927      * Add a column (experimental...) - defaults to adding to the end..
8928      * @param {Object} config 
8929     */
8930     addColumn : function(c)
8931     {
8932     
8933         var i = this.config.length;
8934         this.config[i] = c;
8935         
8936         if(typeof c.dataIndex == "undefined"){
8937             c.dataIndex = i;
8938         }
8939         if(typeof c.renderer == "string"){
8940             c.renderer = Roo.util.Format[c.renderer];
8941         }
8942         if(typeof c.id == "undefined"){
8943             c.id = Roo.id();
8944         }
8945         if(c.editor && c.editor.xtype){
8946             c.editor  = Roo.factory(c.editor, Roo.grid);
8947         }
8948         if(c.editor && c.editor.isFormField){
8949             c.editor = new Roo.grid.GridEditor(c.editor);
8950         }
8951         this.lookup[c.id] = c;
8952     }
8953     
8954 });
8955
8956 Roo.grid.ColumnModel.defaultRenderer = function(value)
8957 {
8958     if(typeof value == "object") {
8959         return value;
8960     }
8961         if(typeof value == "string" && value.length < 1){
8962             return "&#160;";
8963         }
8964     
8965         return String.format("{0}", value);
8966 };
8967
8968 // Alias for backwards compatibility
8969 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8970 /*
8971  * Based on:
8972  * Ext JS Library 1.1.1
8973  * Copyright(c) 2006-2007, Ext JS, LLC.
8974  *
8975  * Originally Released Under LGPL - original licence link has changed is not relivant.
8976  *
8977  * Fork - LGPL
8978  * <script type="text/javascript">
8979  */
8980  
8981 /**
8982  * @class Roo.LoadMask
8983  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8984  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8985  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8986  * element's UpdateManager load indicator and will be destroyed after the initial load.
8987  * @constructor
8988  * Create a new LoadMask
8989  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8990  * @param {Object} config The config object
8991  */
8992 Roo.LoadMask = function(el, config){
8993     this.el = Roo.get(el);
8994     Roo.apply(this, config);
8995     if(this.store){
8996         this.store.on('beforeload', this.onBeforeLoad, this);
8997         this.store.on('load', this.onLoad, this);
8998         this.store.on('loadexception', this.onLoadException, this);
8999         this.removeMask = false;
9000     }else{
9001         var um = this.el.getUpdateManager();
9002         um.showLoadIndicator = false; // disable the default indicator
9003         um.on('beforeupdate', this.onBeforeLoad, this);
9004         um.on('update', this.onLoad, this);
9005         um.on('failure', this.onLoad, this);
9006         this.removeMask = true;
9007     }
9008 };
9009
9010 Roo.LoadMask.prototype = {
9011     /**
9012      * @cfg {Boolean} removeMask
9013      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9014      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9015      */
9016     removeMask : false,
9017     /**
9018      * @cfg {String} msg
9019      * The text to display in a centered loading message box (defaults to 'Loading...')
9020      */
9021     msg : 'Loading...',
9022     /**
9023      * @cfg {String} msgCls
9024      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9025      */
9026     msgCls : 'x-mask-loading',
9027
9028     /**
9029      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9030      * @type Boolean
9031      */
9032     disabled: false,
9033
9034     /**
9035      * Disables the mask to prevent it from being displayed
9036      */
9037     disable : function(){
9038        this.disabled = true;
9039     },
9040
9041     /**
9042      * Enables the mask so that it can be displayed
9043      */
9044     enable : function(){
9045         this.disabled = false;
9046     },
9047     
9048     onLoadException : function()
9049     {
9050         Roo.log(arguments);
9051         
9052         if (typeof(arguments[3]) != 'undefined') {
9053             Roo.MessageBox.alert("Error loading",arguments[3]);
9054         } 
9055         /*
9056         try {
9057             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9058                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9059             }   
9060         } catch(e) {
9061             
9062         }
9063         */
9064     
9065         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9066     },
9067     // private
9068     onLoad : function()
9069     {
9070         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9071     },
9072
9073     // private
9074     onBeforeLoad : function(){
9075         if(!this.disabled){
9076             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9077         }
9078     },
9079
9080     // private
9081     destroy : function(){
9082         if(this.store){
9083             this.store.un('beforeload', this.onBeforeLoad, this);
9084             this.store.un('load', this.onLoad, this);
9085             this.store.un('loadexception', this.onLoadException, this);
9086         }else{
9087             var um = this.el.getUpdateManager();
9088             um.un('beforeupdate', this.onBeforeLoad, this);
9089             um.un('update', this.onLoad, this);
9090             um.un('failure', this.onLoad, this);
9091         }
9092     }
9093 };/**
9094  * @class Roo.bootstrap.Table
9095  * @licence LGBL
9096  * @extends Roo.bootstrap.Component
9097  * @children Roo.bootstrap.TableBody
9098  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9099  * Similar to Roo.grid.Grid
9100  * <pre><code>
9101  var table = Roo.factory({
9102     xtype : 'Table',
9103     xns : Roo.bootstrap,
9104     autoSizeColumns: true,
9105     
9106     
9107     store : {
9108         xtype : 'Store',
9109         xns : Roo.data,
9110         remoteSort : true,
9111         sortInfo : { direction : 'ASC', field: 'name' },
9112         proxy : {
9113            xtype : 'HttpProxy',
9114            xns : Roo.data,
9115            method : 'GET',
9116            url : 'https://example.com/some.data.url.json'
9117         },
9118         reader : {
9119            xtype : 'JsonReader',
9120            xns : Roo.data,
9121            fields : [ 'id', 'name', whatever' ],
9122            id : 'id',
9123            root : 'data'
9124         }
9125     },
9126     cm : [
9127         {
9128             xtype : 'ColumnModel',
9129             xns : Roo.grid,
9130             align : 'center',
9131             cursor : 'pointer',
9132             dataIndex : 'is_in_group',
9133             header : "Name",
9134             sortable : true,
9135             renderer : function(v, x , r) {  
9136             
9137                 return String.format("{0}", v)
9138             }
9139             width : 3
9140         } // more columns..
9141     ],
9142     selModel : {
9143         xtype : 'RowSelectionModel',
9144         xns : Roo.bootstrap.Table
9145         // you can add listeners to catch selection change here....
9146     }
9147      
9148
9149  });
9150  // set any options
9151  grid.render(Roo.get("some-div"));
9152 </code></pre>
9153
9154 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9155
9156
9157
9158  *
9159  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9160  * @cfg {Roo.data.Store} store The data store to use
9161  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9162  * 
9163  * @cfg {String} cls table class
9164  *
9165  *
9166  * @cfg {string} empty_results  Text to display for no results 
9167  * @cfg {boolean} striped Should the rows be alternative striped
9168  * @cfg {boolean} bordered Add borders to the table
9169  * @cfg {boolean} hover Add hover highlighting
9170  * @cfg {boolean} condensed Format condensed
9171  * @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,
9172  *                also adds table-responsive (see bootstrap docs for details)
9173  * @cfg {Boolean} loadMask (true|false) default false
9174  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9175  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9176  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9177  * @cfg {Boolean} rowSelection (true|false) default false
9178  * @cfg {Boolean} cellSelection (true|false) default false
9179  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9180  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9181  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9182  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9183  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9184  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9185  *
9186  * 
9187  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9188  * 
9189  * @constructor
9190  * Create a new Table
9191  * @param {Object} config The config object
9192  */
9193
9194 Roo.bootstrap.Table = function(config)
9195 {
9196     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9197      
9198     // BC...
9199     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9200     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9201     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9202     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9203     
9204     this.view = this; // compat with grid.
9205     
9206     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9207     if (this.sm) {
9208         this.sm.grid = this;
9209         this.selModel = Roo.factory(this.sm, Roo.grid);
9210         this.sm = this.selModel;
9211         this.sm.xmodule = this.xmodule || false;
9212     }
9213     
9214     if (this.cm && typeof(this.cm.config) == 'undefined') {
9215         this.colModel = new Roo.grid.ColumnModel(this.cm);
9216         this.cm = this.colModel;
9217         this.cm.xmodule = this.xmodule || false;
9218     }
9219     if (this.store) {
9220         this.store= Roo.factory(this.store, Roo.data);
9221         this.ds = this.store;
9222         this.ds.xmodule = this.xmodule || false;
9223          
9224     }
9225     if (this.footer && this.store) {
9226         this.footer.dataSource = this.ds;
9227         this.footer = Roo.factory(this.footer);
9228     }
9229     
9230     /** @private */
9231     this.addEvents({
9232         /**
9233          * @event cellclick
9234          * Fires when a cell is clicked
9235          * @param {Roo.bootstrap.Table} this
9236          * @param {Roo.Element} el
9237          * @param {Number} rowIndex
9238          * @param {Number} columnIndex
9239          * @param {Roo.EventObject} e
9240          */
9241         "cellclick" : true,
9242         /**
9243          * @event celldblclick
9244          * Fires when a cell is double clicked
9245          * @param {Roo.bootstrap.Table} this
9246          * @param {Roo.Element} el
9247          * @param {Number} rowIndex
9248          * @param {Number} columnIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "celldblclick" : true,
9252         /**
9253          * @event rowclick
9254          * Fires when a row is clicked
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Roo.EventObject} e
9259          */
9260         "rowclick" : true,
9261         /**
9262          * @event rowdblclick
9263          * Fires when a row is double clicked
9264          * @param {Roo.bootstrap.Table} this
9265          * @param {Roo.Element} el
9266          * @param {Number} rowIndex
9267          * @param {Roo.EventObject} e
9268          */
9269         "rowdblclick" : true,
9270         /**
9271          * @event mouseover
9272          * Fires when a mouseover occur
9273          * @param {Roo.bootstrap.Table} this
9274          * @param {Roo.Element} el
9275          * @param {Number} rowIndex
9276          * @param {Number} columnIndex
9277          * @param {Roo.EventObject} e
9278          */
9279         "mouseover" : true,
9280         /**
9281          * @event mouseout
9282          * Fires when a mouseout occur
9283          * @param {Roo.bootstrap.Table} this
9284          * @param {Roo.Element} el
9285          * @param {Number} rowIndex
9286          * @param {Number} columnIndex
9287          * @param {Roo.EventObject} e
9288          */
9289         "mouseout" : true,
9290         /**
9291          * @event rowclass
9292          * Fires when a row is rendered, so you can change add a style to it.
9293          * @param {Roo.bootstrap.Table} this
9294          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9295          */
9296         'rowclass' : true,
9297           /**
9298          * @event rowsrendered
9299          * Fires when all the  rows have been rendered
9300          * @param {Roo.bootstrap.Table} this
9301          */
9302         'rowsrendered' : true,
9303         /**
9304          * @event contextmenu
9305          * The raw contextmenu event for the entire grid.
9306          * @param {Roo.EventObject} e
9307          */
9308         "contextmenu" : true,
9309         /**
9310          * @event rowcontextmenu
9311          * Fires when a row is right clicked
9312          * @param {Roo.bootstrap.Table} this
9313          * @param {Number} rowIndex
9314          * @param {Roo.EventObject} e
9315          */
9316         "rowcontextmenu" : true,
9317         /**
9318          * @event cellcontextmenu
9319          * Fires when a cell is right clicked
9320          * @param {Roo.bootstrap.Table} this
9321          * @param {Number} rowIndex
9322          * @param {Number} cellIndex
9323          * @param {Roo.EventObject} e
9324          */
9325          "cellcontextmenu" : true,
9326          /**
9327          * @event headercontextmenu
9328          * Fires when a header is right clicked
9329          * @param {Roo.bootstrap.Table} this
9330          * @param {Number} columnIndex
9331          * @param {Roo.EventObject} e
9332          */
9333         "headercontextmenu" : true,
9334         /**
9335          * @event mousedown
9336          * The raw mousedown event for the entire grid.
9337          * @param {Roo.EventObject} e
9338          */
9339         "mousedown" : true
9340         
9341     });
9342 };
9343
9344 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9345     
9346     cls: false,
9347     
9348     empty_results : '',
9349     striped : false,
9350     scrollBody : false,
9351     bordered: false,
9352     hover:  false,
9353     condensed : false,
9354     responsive : false,
9355     sm : false,
9356     cm : false,
9357     store : false,
9358     loadMask : false,
9359     footerShow : true,
9360     footerRow : false,
9361     headerShow : true,
9362     enableColumnResize: true,
9363     disableAutoSize: false,
9364   
9365     rowSelection : false,
9366     cellSelection : false,
9367     layout : false,
9368
9369     minColumnWidth : 50,
9370     
9371     // Roo.Element - the tbody
9372     bodyEl: false,  // <tbody> Roo.Element - thead element    
9373     headEl: false,  // <thead> Roo.Element - thead element
9374     resizeProxy : false, // proxy element for dragging?
9375
9376
9377     
9378     container: false, // used by gridpanel...
9379     
9380     lazyLoad : false,
9381     
9382     CSS : Roo.util.CSS,
9383     
9384     auto_hide_footer : false,
9385     
9386     view: false, // actually points to this..
9387     
9388     getAutoCreate : function()
9389     {
9390         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9391         
9392         cfg = {
9393             tag: 'table',
9394             cls : 'table', 
9395             cn : []
9396         };
9397         // this get's auto added by panel.Grid
9398         if (this.scrollBody) {
9399             cfg.cls += ' table-body-fixed';
9400         }    
9401         if (this.striped) {
9402             cfg.cls += ' table-striped';
9403         }
9404         
9405         if (this.hover) {
9406             cfg.cls += ' table-hover';
9407         }
9408         if (this.bordered) {
9409             cfg.cls += ' table-bordered';
9410         }
9411         if (this.condensed) {
9412             cfg.cls += ' table-condensed';
9413         }
9414         
9415         if (this.responsive) {
9416             cfg.cls += ' table-responsive';
9417         }
9418         
9419         if (this.cls) {
9420             cfg.cls+=  ' ' +this.cls;
9421         }
9422         
9423         
9424         
9425         if (this.layout) {
9426             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9427         }
9428         
9429         if(this.store || this.cm){
9430             if(this.headerShow){
9431                 cfg.cn.push(this.renderHeader());
9432             }
9433             
9434             cfg.cn.push(this.renderBody());
9435             
9436             if(this.footerShow || this.footerRow){
9437                 cfg.cn.push(this.renderFooter());
9438             }
9439
9440             // where does this come from?
9441             //cfg.cls+=  ' TableGrid';
9442         }
9443         
9444         return { cn : [ cfg ] };
9445     },
9446     
9447     initEvents : function()
9448     {   
9449         if(!this.store || !this.cm){
9450             return;
9451         }
9452         if (this.selModel) {
9453             this.selModel.initEvents();
9454         }
9455         
9456         
9457         //Roo.log('initEvents with ds!!!!');
9458         
9459         this.bodyEl = this.el.select('tbody', true).first();
9460         this.headEl = this.el.select('thead', true).first();
9461         this.mainFoot = this.el.select('tfoot', true).first();
9462         
9463         
9464         
9465         
9466         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9467             e.on('click', this.sort, this);
9468         }, this);
9469         
9470         
9471         // why is this done????? = it breaks dialogs??
9472         //this.parent().el.setStyle('position', 'relative');
9473         
9474         
9475         if (this.footer) {
9476             this.footer.parentId = this.id;
9477             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9478             
9479             if(this.lazyLoad){
9480                 this.el.select('tfoot tr td').first().addClass('hide');
9481             }
9482         } 
9483         
9484         if(this.loadMask) {
9485             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9486         }
9487         
9488         this.store.on('load', this.onLoad, this);
9489         this.store.on('beforeload', this.onBeforeLoad, this);
9490         this.store.on('update', this.onUpdate, this);
9491         this.store.on('add', this.onAdd, this);
9492         this.store.on("clear", this.clear, this);
9493         
9494         this.el.on("contextmenu", this.onContextMenu, this);
9495         
9496         
9497         this.cm.on("headerchange", this.onHeaderChange, this);
9498         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9499
9500  //?? does bodyEl get replaced on render?
9501         this.bodyEl.on("click", this.onClick, this);
9502         this.bodyEl.on("dblclick", this.onDblClick, this);        
9503         this.bodyEl.on('scroll', this.onBodyScroll, this);
9504
9505         // guessing mainbody will work - this relays usually caught by selmodel at present.
9506         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9507   
9508   
9509         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9510         
9511   
9512         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9513             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9514         }
9515         
9516         this.initCSS();
9517     },
9518     // Compatibility with grid - we implement all the view features at present.
9519     getView : function()
9520     {
9521         return this;
9522     },
9523     
9524     initCSS : function()
9525     {
9526         if(this.disableAutoSize) {
9527             return;
9528         }
9529         
9530         var cm = this.cm, styles = [];
9531         this.CSS.removeStyleSheet(this.id + '-cssrules');
9532         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9533         // we can honour xs/sm/md/xl  as widths...
9534         // we first have to decide what widht we are currently at...
9535         var sz = Roo.getGridSize();
9536         
9537         var total = 0;
9538         var last = -1;
9539         var cols = []; // visable cols.
9540         var total_abs = 0;
9541         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9542             var w = cm.getColumnWidth(i, false);
9543             if(cm.isHidden(i)){
9544                 cols.push( { rel : false, abs : 0 });
9545                 continue;
9546             }
9547             if (w !== false) {
9548                 cols.push( { rel : false, abs : w });
9549                 total_abs += w;
9550                 last = i; // not really..
9551                 continue;
9552             }
9553             var w = cm.getColumnWidth(i, sz);
9554             if (w > 0) {
9555                 last = i
9556             }
9557             total += w;
9558             cols.push( { rel : w, abs : false });
9559         }
9560         
9561         var avail = this.bodyEl.dom.clientWidth - total_abs;
9562         
9563         var unitWidth = Math.floor(avail / total);
9564         var rem = avail - (unitWidth * total);
9565         
9566         var hidden, width, pos = 0 , splithide , left;
9567         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9568             
9569             hidden = 'display:none;';
9570             left = '';
9571             width  = 'width:0px;';
9572             splithide = '';
9573             if(!cm.isHidden(i)){
9574                 hidden = '';
9575                 
9576                 
9577                 // we can honour xs/sm/md/xl ?
9578                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9579                 if (w===0) {
9580                     hidden = 'display:none;';
9581                 }
9582                 // width should return a small number...
9583                 if (i == last) {
9584                     w+=rem; // add the remaining with..
9585                 }
9586                 pos += w;
9587                 left = "left:" + (pos -4) + "px;";
9588                 width = "width:" + w+ "px;";
9589                 
9590             }
9591             if (this.responsive) {
9592                 width = '';
9593                 left = '';
9594                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9595                 splithide = 'display: none;';
9596             }
9597             
9598             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9599             if (this.headEl) {
9600                 if (i == last) {
9601                     splithide = 'display:none;';
9602                 }
9603                 
9604                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9605                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9606                             // this is the popover version..
9607                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9608                 );
9609             }
9610             
9611         }
9612         //Roo.log(styles.join(''));
9613         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9614         
9615     },
9616     
9617     
9618     
9619     onContextMenu : function(e, t)
9620     {
9621         this.processEvent("contextmenu", e);
9622     },
9623     
9624     processEvent : function(name, e)
9625     {
9626         if (name != 'touchstart' ) {
9627             this.fireEvent(name, e);    
9628         }
9629         
9630         var t = e.getTarget();
9631         
9632         var cell = Roo.get(t);
9633         
9634         if(!cell){
9635             return;
9636         }
9637         
9638         if(cell.findParent('tfoot', false, true)){
9639             return;
9640         }
9641         
9642         if(cell.findParent('thead', false, true)){
9643             
9644             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9645                 cell = Roo.get(t).findParent('th', false, true);
9646                 if (!cell) {
9647                     Roo.log("failed to find th in thead?");
9648                     Roo.log(e.getTarget());
9649                     return;
9650                 }
9651             }
9652             
9653             var cellIndex = cell.dom.cellIndex;
9654             
9655             var ename = name == 'touchstart' ? 'click' : name;
9656             this.fireEvent("header" + ename, this, cellIndex, e);
9657             
9658             return;
9659         }
9660         
9661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9662             cell = Roo.get(t).findParent('td', false, true);
9663             if (!cell) {
9664                 Roo.log("failed to find th in tbody?");
9665                 Roo.log(e.getTarget());
9666                 return;
9667             }
9668         }
9669         
9670         var row = cell.findParent('tr', false, true);
9671         var cellIndex = cell.dom.cellIndex;
9672         var rowIndex = row.dom.rowIndex - 1;
9673         
9674         if(row !== false){
9675             
9676             this.fireEvent("row" + name, this, rowIndex, e);
9677             
9678             if(cell !== false){
9679             
9680                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9681             }
9682         }
9683         
9684     },
9685     
9686     onMouseover : function(e, el)
9687     {
9688         var cell = Roo.get(el);
9689         
9690         if(!cell){
9691             return;
9692         }
9693         
9694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695             cell = cell.findParent('td', false, true);
9696         }
9697         
9698         var row = cell.findParent('tr', false, true);
9699         var cellIndex = cell.dom.cellIndex;
9700         var rowIndex = row.dom.rowIndex - 1; // start from 0
9701         
9702         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9703         
9704     },
9705     
9706     onMouseout : function(e, el)
9707     {
9708         var cell = Roo.get(el);
9709         
9710         if(!cell){
9711             return;
9712         }
9713         
9714         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715             cell = cell.findParent('td', false, true);
9716         }
9717         
9718         var row = cell.findParent('tr', false, true);
9719         var cellIndex = cell.dom.cellIndex;
9720         var rowIndex = row.dom.rowIndex - 1; // start from 0
9721         
9722         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9723         
9724     },
9725     
9726     onClick : function(e, el)
9727     {
9728         var cell = Roo.get(el);
9729         
9730         if(!cell || (!this.cellSelection && !this.rowSelection)){
9731             return;
9732         }
9733         
9734         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9735             cell = cell.findParent('td', false, true);
9736         }
9737         
9738         if(!cell || typeof(cell) == 'undefined'){
9739             return;
9740         }
9741         
9742         var row = cell.findParent('tr', false, true);
9743         
9744         if(!row || typeof(row) == 'undefined'){
9745             return;
9746         }
9747         
9748         var cellIndex = cell.dom.cellIndex;
9749         var rowIndex = this.getRowIndex(row);
9750         
9751         // why??? - should these not be based on SelectionModel?
9752         //if(this.cellSelection){
9753             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9754         //}
9755         
9756         //if(this.rowSelection){
9757             this.fireEvent('rowclick', this, row, rowIndex, e);
9758         //}
9759          
9760     },
9761         
9762     onDblClick : function(e,el)
9763     {
9764         var cell = Roo.get(el);
9765         
9766         if(!cell || (!this.cellSelection && !this.rowSelection)){
9767             return;
9768         }
9769         
9770         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9771             cell = cell.findParent('td', false, true);
9772         }
9773         
9774         if(!cell || typeof(cell) == 'undefined'){
9775             return;
9776         }
9777         
9778         var row = cell.findParent('tr', false, true);
9779         
9780         if(!row || typeof(row) == 'undefined'){
9781             return;
9782         }
9783         
9784         var cellIndex = cell.dom.cellIndex;
9785         var rowIndex = this.getRowIndex(row);
9786         
9787         if(this.cellSelection){
9788             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9789         }
9790         
9791         if(this.rowSelection){
9792             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9793         }
9794     },
9795     findRowIndex : function(el)
9796     {
9797         var cell = Roo.get(el);
9798         if(!cell) {
9799             return false;
9800         }
9801         var row = cell.findParent('tr', false, true);
9802         
9803         if(!row || typeof(row) == 'undefined'){
9804             return false;
9805         }
9806         return this.getRowIndex(row);
9807     },
9808     sort : function(e,el)
9809     {
9810         var col = Roo.get(el);
9811         
9812         if(!col.hasClass('sortable')){
9813             return;
9814         }
9815         
9816         var sort = col.attr('sort');
9817         var dir = 'ASC';
9818         
9819         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9820             dir = 'DESC';
9821         }
9822         
9823         this.store.sortInfo = {field : sort, direction : dir};
9824         
9825         if (this.footer) {
9826             Roo.log("calling footer first");
9827             this.footer.onClick('first');
9828         } else {
9829         
9830             this.store.load({ params : { start : 0 } });
9831         }
9832     },
9833     
9834     renderHeader : function()
9835     {
9836         var header = {
9837             tag: 'thead',
9838             cn : []
9839         };
9840         
9841         var cm = this.cm;
9842         this.totalWidth = 0;
9843         
9844         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9845             
9846             var config = cm.config[i];
9847             
9848             var c = {
9849                 tag: 'th',
9850                 cls : 'x-hcol-' + i,
9851                 style : '',
9852                 
9853                 html: cm.getColumnHeader(i)
9854             };
9855             
9856             var tooltip = cm.getColumnTooltip(i);
9857             if (tooltip) {
9858                 c.tooltip = tooltip;
9859             }
9860             
9861             
9862             var hh = '';
9863             
9864             if(typeof(config.sortable) != 'undefined' && config.sortable){
9865                 c.cls += ' sortable';
9866                 c.html = '<i class="fa"></i>' + c.html;
9867             }
9868             
9869             // could use BS4 hidden-..-down 
9870             
9871             if(typeof(config.lgHeader) != 'undefined'){
9872                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9873             }
9874             
9875             if(typeof(config.mdHeader) != 'undefined'){
9876                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9877             }
9878             
9879             if(typeof(config.smHeader) != 'undefined'){
9880                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9881             }
9882             
9883             if(typeof(config.xsHeader) != 'undefined'){
9884                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9885             }
9886             
9887             if(hh.length){
9888                 c.html = hh;
9889             }
9890             
9891             if(typeof(config.tooltip) != 'undefined'){
9892                 c.tooltip = config.tooltip;
9893             }
9894             
9895             if(typeof(config.colspan) != 'undefined'){
9896                 c.colspan = config.colspan;
9897             }
9898             
9899             // hidden is handled by CSS now
9900             
9901             if(typeof(config.dataIndex) != 'undefined'){
9902                 c.sort = config.dataIndex;
9903             }
9904             
9905            
9906             
9907             if(typeof(config.align) != 'undefined' && config.align.length){
9908                 c.style += ' text-align:' + config.align + ';';
9909             }
9910             
9911             /* width is done in CSS
9912              *if(typeof(config.width) != 'undefined'){
9913                 c.style += ' width:' + config.width + 'px;';
9914                 this.totalWidth += config.width;
9915             } else {
9916                 this.totalWidth += 100; // assume minimum of 100 per column?
9917             }
9918             */
9919             
9920             if(typeof(config.cls) != 'undefined'){
9921                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9922             }
9923             // this is the bit that doesnt reall work at all...
9924             
9925             if (this.responsive) {
9926                  
9927             
9928                 ['xs','sm','md','lg'].map(function(size){
9929                     
9930                     if(typeof(config[size]) == 'undefined'){
9931                         return;
9932                     }
9933                      
9934                     if (!config[size]) { // 0 = hidden
9935                         // BS 4 '0' is treated as hide that column and below.
9936                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9937                         return;
9938                     }
9939                     
9940                     c.cls += ' col-' + size + '-' + config[size] + (
9941                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9942                     );
9943                     
9944                     
9945                 });
9946             }
9947             // at the end?
9948             
9949             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9950             
9951             
9952             
9953             
9954             header.cn.push(c)
9955         }
9956         
9957         return header;
9958     },
9959     
9960     renderBody : function()
9961     {
9962         var body = {
9963             tag: 'tbody',
9964             cn : [
9965                 {
9966                     tag: 'tr',
9967                     cn : [
9968                         {
9969                             tag : 'td',
9970                             colspan :  this.cm.getColumnCount()
9971                         }
9972                     ]
9973                 }
9974             ]
9975         };
9976         
9977         return body;
9978     },
9979     
9980     renderFooter : function()
9981     {
9982         var footer = {
9983             tag: 'tfoot',
9984             cn : [
9985                 {
9986                     tag: 'tr',
9987                     cn : [
9988                         {
9989                             tag : 'td',
9990                             colspan :  this.cm.getColumnCount()
9991                         }
9992                     ]
9993                 }
9994             ]
9995         };
9996         
9997         return footer;
9998     },
9999     
10000     onLoad : function()
10001     {
10002 //        Roo.log('ds onload');
10003         this.clear();
10004         
10005         var _this = this;
10006         var cm = this.cm;
10007         var ds = this.store;
10008         
10009         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10010             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10011             if (_this.store.sortInfo) {
10012                     
10013                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10014                     e.select('i', true).addClass(['fa-arrow-up']);
10015                 }
10016                 
10017                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10018                     e.select('i', true).addClass(['fa-arrow-down']);
10019                 }
10020             }
10021         });
10022         
10023         var tbody =  this.bodyEl;
10024               
10025         if(ds.getCount() > 0){
10026             ds.data.each(function(d,rowIndex){
10027                 var row =  this.renderRow(cm, ds, rowIndex);
10028                 
10029                 tbody.createChild(row);
10030                 
10031                 var _this = this;
10032                 
10033                 if(row.cellObjects.length){
10034                     Roo.each(row.cellObjects, function(r){
10035                         _this.renderCellObject(r);
10036                     })
10037                 }
10038                 
10039             }, this);
10040         } else if (this.empty_results.length) {
10041             this.el.mask(this.empty_results, 'no-spinner');
10042         }
10043         
10044         var tfoot = this.el.select('tfoot', true).first();
10045         
10046         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10047             
10048             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10049             
10050             var total = this.ds.getTotalCount();
10051             
10052             if(this.footer.pageSize < total){
10053                 this.mainFoot.show();
10054             }
10055         }
10056
10057         if(!this.footerShow && this.footerRow) {
10058
10059             var tr = {
10060                 tag : 'tr',
10061                 cn : []
10062             };
10063
10064             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10065                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10066                 var td = {
10067                     tag: 'td',
10068                     cls : ' x-fcol-' + i,
10069                     html: footer
10070                 };
10071
10072                 tr.cn.push(td);
10073                 
10074             }
10075             
10076             tfoot.dom.innerHTML = '';
10077
10078             tfoot.createChild(tr);
10079         }
10080         
10081         Roo.each(this.el.select('tbody td', true).elements, function(e){
10082             e.on('mouseover', _this.onMouseover, _this);
10083         });
10084         
10085         Roo.each(this.el.select('tbody td', true).elements, function(e){
10086             e.on('mouseout', _this.onMouseout, _this);
10087         });
10088         this.fireEvent('rowsrendered', this);
10089         
10090         this.autoSize();
10091         
10092         this.initCSS(); /// resize cols
10093
10094         
10095     },
10096     
10097     
10098     onUpdate : function(ds,record)
10099     {
10100         this.refreshRow(record);
10101         this.autoSize();
10102     },
10103     
10104     onRemove : function(ds, record, index, isUpdate){
10105         if(isUpdate !== true){
10106             this.fireEvent("beforerowremoved", this, index, record);
10107         }
10108         var bt = this.bodyEl.dom;
10109         
10110         var rows = this.el.select('tbody > tr', true).elements;
10111         
10112         if(typeof(rows[index]) != 'undefined'){
10113             bt.removeChild(rows[index].dom);
10114         }
10115         
10116 //        if(bt.rows[index]){
10117 //            bt.removeChild(bt.rows[index]);
10118 //        }
10119         
10120         if(isUpdate !== true){
10121             //this.stripeRows(index);
10122             //this.syncRowHeights(index, index);
10123             //this.layout();
10124             this.fireEvent("rowremoved", this, index, record);
10125         }
10126     },
10127     
10128     onAdd : function(ds, records, rowIndex)
10129     {
10130         //Roo.log('on Add called');
10131         // - note this does not handle multiple adding very well..
10132         var bt = this.bodyEl.dom;
10133         for (var i =0 ; i < records.length;i++) {
10134             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10135             //Roo.log(records[i]);
10136             //Roo.log(this.store.getAt(rowIndex+i));
10137             this.insertRow(this.store, rowIndex + i, false);
10138             return;
10139         }
10140         
10141     },
10142     
10143     
10144     refreshRow : function(record){
10145         var ds = this.store, index;
10146         if(typeof record == 'number'){
10147             index = record;
10148             record = ds.getAt(index);
10149         }else{
10150             index = ds.indexOf(record);
10151             if (index < 0) {
10152                 return; // should not happen - but seems to 
10153             }
10154         }
10155         this.insertRow(ds, index, true);
10156         this.autoSize();
10157         this.onRemove(ds, record, index+1, true);
10158         this.autoSize();
10159         //this.syncRowHeights(index, index);
10160         //this.layout();
10161         this.fireEvent("rowupdated", this, index, record);
10162     },
10163     // private - called by RowSelection
10164     onRowSelect : function(rowIndex){
10165         var row = this.getRowDom(rowIndex);
10166         row.addClass(['bg-info','info']);
10167     },
10168     // private - called by RowSelection
10169     onRowDeselect : function(rowIndex)
10170     {
10171         if (rowIndex < 0) {
10172             return;
10173         }
10174         var row = this.getRowDom(rowIndex);
10175         row.removeClass(['bg-info','info']);
10176     },
10177       /**
10178      * Focuses the specified row.
10179      * @param {Number} row The row index
10180      */
10181     focusRow : function(row)
10182     {
10183         //Roo.log('GridView.focusRow');
10184         var x = this.bodyEl.dom.scrollLeft;
10185         this.focusCell(row, 0, false);
10186         this.bodyEl.dom.scrollLeft = x;
10187
10188     },
10189      /**
10190      * Focuses the specified cell.
10191      * @param {Number} row The row index
10192      * @param {Number} col The column index
10193      * @param {Boolean} hscroll false to disable horizontal scrolling
10194      */
10195     focusCell : function(row, col, hscroll)
10196     {
10197         //Roo.log('GridView.focusCell');
10198         var el = this.ensureVisible(row, col, hscroll);
10199         // not sure what focusEL achives = it's a <a> pos relative 
10200         //this.focusEl.alignTo(el, "tl-tl");
10201         //if(Roo.isGecko){
10202         //    this.focusEl.focus();
10203         //}else{
10204         //    this.focusEl.focus.defer(1, this.focusEl);
10205         //}
10206     },
10207     
10208      /**
10209      * Scrolls the specified cell into view
10210      * @param {Number} row The row index
10211      * @param {Number} col The column index
10212      * @param {Boolean} hscroll false to disable horizontal scrolling
10213      */
10214     ensureVisible : function(row, col, hscroll)
10215     {
10216         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10217         //return null; //disable for testing.
10218         if(typeof row != "number"){
10219             row = row.rowIndex;
10220         }
10221         if(row < 0 && row >= this.ds.getCount()){
10222             return  null;
10223         }
10224         col = (col !== undefined ? col : 0);
10225         var cm = this.cm;
10226         while(cm.isHidden(col)){
10227             col++;
10228         }
10229
10230         var el = this.getCellDom(row, col);
10231         if(!el){
10232             return null;
10233         }
10234         var c = this.bodyEl.dom;
10235
10236         var ctop = parseInt(el.offsetTop, 10);
10237         var cleft = parseInt(el.offsetLeft, 10);
10238         var cbot = ctop + el.offsetHeight;
10239         var cright = cleft + el.offsetWidth;
10240
10241         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10242         var ch = 0; //?? header is not withing the area?
10243         var stop = parseInt(c.scrollTop, 10);
10244         var sleft = parseInt(c.scrollLeft, 10);
10245         var sbot = stop + ch;
10246         var sright = sleft + c.clientWidth;
10247         /*
10248         Roo.log('GridView.ensureVisible:' +
10249                 ' ctop:' + ctop +
10250                 ' c.clientHeight:' + c.clientHeight +
10251                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10252                 ' stop:' + stop +
10253                 ' cbot:' + cbot +
10254                 ' sbot:' + sbot +
10255                 ' ch:' + ch  
10256                 );
10257         */
10258         if(ctop < stop){
10259             c.scrollTop = ctop;
10260             //Roo.log("set scrolltop to ctop DISABLE?");
10261         }else if(cbot > sbot){
10262             //Roo.log("set scrolltop to cbot-ch");
10263             c.scrollTop = cbot-ch;
10264         }
10265
10266         if(hscroll !== false){
10267             if(cleft < sleft){
10268                 c.scrollLeft = cleft;
10269             }else if(cright > sright){
10270                 c.scrollLeft = cright-c.clientWidth;
10271             }
10272         }
10273
10274         return el;
10275     },
10276     
10277     
10278     insertRow : function(dm, rowIndex, isUpdate){
10279         
10280         if(!isUpdate){
10281             this.fireEvent("beforerowsinserted", this, rowIndex);
10282         }
10283             //var s = this.getScrollState();
10284         var row = this.renderRow(this.cm, this.store, rowIndex);
10285         // insert before rowIndex..
10286         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10287         
10288         var _this = this;
10289                 
10290         if(row.cellObjects.length){
10291             Roo.each(row.cellObjects, function(r){
10292                 _this.renderCellObject(r);
10293             })
10294         }
10295             
10296         if(!isUpdate){
10297             this.fireEvent("rowsinserted", this, rowIndex);
10298             //this.syncRowHeights(firstRow, lastRow);
10299             //this.stripeRows(firstRow);
10300             //this.layout();
10301         }
10302         
10303     },
10304     
10305     
10306     getRowDom : function(rowIndex)
10307     {
10308         var rows = this.el.select('tbody > tr', true).elements;
10309         
10310         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10311         
10312     },
10313     getCellDom : function(rowIndex, colIndex)
10314     {
10315         var row = this.getRowDom(rowIndex);
10316         if (row === false) {
10317             return false;
10318         }
10319         var cols = row.select('td', true).elements;
10320         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10321         
10322     },
10323     
10324     // returns the object tree for a tr..
10325   
10326     
10327     renderRow : function(cm, ds, rowIndex) 
10328     {
10329         var d = ds.getAt(rowIndex);
10330         
10331         var row = {
10332             tag : 'tr',
10333             cls : 'x-row-' + rowIndex,
10334             cn : []
10335         };
10336             
10337         var cellObjects = [];
10338         
10339         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10340             var config = cm.config[i];
10341             
10342             var renderer = cm.getRenderer(i);
10343             var value = '';
10344             var id = false;
10345             
10346             if(typeof(renderer) !== 'undefined'){
10347                 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10348             }
10349             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10350             // and are rendered into the cells after the row is rendered - using the id for the element.
10351             
10352             if(typeof(value) === 'object'){
10353                 id = Roo.id();
10354                 cellObjects.push({
10355                     container : id,
10356                     cfg : value 
10357                 })
10358             }
10359             
10360             var rowcfg = {
10361                 record: d,
10362                 rowIndex : rowIndex,
10363                 colIndex : i,
10364                 rowClass : ''
10365             };
10366
10367             this.fireEvent('rowclass', this, rowcfg);
10368             
10369             var td = {
10370                 tag: 'td',
10371                 // this might end up displaying HTML?
10372                 // this is too messy... - better to only do it on columsn you know are going to be too long
10373                 //tooltip : (typeof(value) === 'object') ? '' : value,
10374                 cls : rowcfg.rowClass + ' x-col-' + i,
10375                 style: '',
10376                 html: (typeof(value) === 'object') ? '' : value
10377             };
10378             
10379             if (id) {
10380                 td.id = id;
10381             }
10382             
10383             if(typeof(config.colspan) != 'undefined'){
10384                 td.colspan = config.colspan;
10385             }
10386             
10387             
10388             
10389             if(typeof(config.align) != 'undefined' && config.align.length){
10390                 td.style += ' text-align:' + config.align + ';';
10391             }
10392             if(typeof(config.valign) != 'undefined' && config.valign.length){
10393                 td.style += ' vertical-align:' + config.valign + ';';
10394             }
10395             /*
10396             if(typeof(config.width) != 'undefined'){
10397                 td.style += ' width:' +  config.width + 'px;';
10398             }
10399             */
10400             
10401             if(typeof(config.cursor) != 'undefined'){
10402                 td.style += ' cursor:' +  config.cursor + ';';
10403             }
10404             
10405             if(typeof(config.cls) != 'undefined'){
10406                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10407             }
10408             if (this.responsive) {
10409                 ['xs','sm','md','lg'].map(function(size){
10410                     
10411                     if(typeof(config[size]) == 'undefined'){
10412                         return;
10413                     }
10414                     
10415                     
10416                       
10417                     if (!config[size]) { // 0 = hidden
10418                         // BS 4 '0' is treated as hide that column and below.
10419                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10420                         return;
10421                     }
10422                     
10423                     td.cls += ' col-' + size + '-' + config[size] + (
10424                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10425                     );
10426                      
10427     
10428                 });
10429             }
10430             row.cn.push(td);
10431            
10432         }
10433         
10434         row.cellObjects = cellObjects;
10435         
10436         return row;
10437           
10438     },
10439     
10440     
10441     
10442     onBeforeLoad : function()
10443     {
10444         this.el.unmask(); // if needed.
10445     },
10446      /**
10447      * Remove all rows
10448      */
10449     clear : function()
10450     {
10451         this.el.select('tbody', true).first().dom.innerHTML = '';
10452     },
10453     /**
10454      * Show or hide a row.
10455      * @param {Number} rowIndex to show or hide
10456      * @param {Boolean} state hide
10457      */
10458     setRowVisibility : function(rowIndex, state)
10459     {
10460         var bt = this.bodyEl.dom;
10461         
10462         var rows = this.el.select('tbody > tr', true).elements;
10463         
10464         if(typeof(rows[rowIndex]) == 'undefined'){
10465             return;
10466         }
10467         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10468         
10469     },
10470     
10471     
10472     getSelectionModel : function(){
10473         if(!this.selModel){
10474             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10475         }
10476         return this.selModel;
10477     },
10478     /*
10479      * Render the Roo.bootstrap object from renderder
10480      */
10481     renderCellObject : function(r)
10482     {
10483         var _this = this;
10484         
10485         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10486         
10487         var t = r.cfg.render(r.container);
10488         
10489         if(r.cfg.cn){
10490             Roo.each(r.cfg.cn, function(c){
10491                 var child = {
10492                     container: t.getChildContainer(),
10493                     cfg: c
10494                 };
10495                 _this.renderCellObject(child);
10496             })
10497         }
10498     },
10499     /**
10500      * get the Row Index from a dom element.
10501      * @param {Roo.Element} row The row to look for
10502      * @returns {Number} the row
10503      */
10504     getRowIndex : function(row)
10505     {
10506         var rowIndex = -1;
10507         
10508         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10509             if(el != row){
10510                 return;
10511             }
10512             
10513             rowIndex = index;
10514         });
10515         
10516         return rowIndex;
10517     },
10518     /**
10519      * get the header TH element for columnIndex
10520      * @param {Number} columnIndex
10521      * @returns {Roo.Element}
10522      */
10523     getHeaderIndex: function(colIndex)
10524     {
10525         var cols = this.headEl.select('th', true).elements;
10526         return cols[colIndex]; 
10527     },
10528     /**
10529      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10530      * @param {domElement} cell to look for
10531      * @returns {Number} the column
10532      */
10533     getCellIndex : function(cell)
10534     {
10535         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10536         if(id){
10537             return parseInt(id[1], 10);
10538         }
10539         return 0;
10540     },
10541      /**
10542      * Returns the grid's underlying element = used by panel.Grid
10543      * @return {Element} The element
10544      */
10545     getGridEl : function(){
10546         return this.el;
10547     },
10548      /**
10549      * Forces a resize - used by panel.Grid
10550      * @return {Element} The element
10551      */
10552     autoSize : function()
10553     {
10554         if(this.disableAutoSize) {
10555             return;
10556         }
10557         //var ctr = Roo.get(this.container.dom.parentElement);
10558         var ctr = Roo.get(this.el.dom);
10559         
10560         var thd = this.getGridEl().select('thead',true).first();
10561         var tbd = this.getGridEl().select('tbody', true).first();
10562         var tfd = this.getGridEl().select('tfoot', true).first();
10563         
10564         var cw = ctr.getWidth();
10565         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10566         
10567         if (tbd) {
10568             
10569             tbd.setWidth(ctr.getWidth());
10570             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10571             // this needs fixing for various usage - currently only hydra job advers I think..
10572             //tdb.setHeight(
10573             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10574             //); 
10575             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10576             cw -= barsize;
10577         }
10578         cw = Math.max(cw, this.totalWidth);
10579         this.getGridEl().select('tbody tr',true).setWidth(cw);
10580         this.initCSS();
10581         
10582         // resize 'expandable coloumn?
10583         
10584         return; // we doe not have a view in this design..
10585         
10586     },
10587     onBodyScroll: function()
10588     {
10589         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10590         if(this.headEl){
10591             this.headEl.setStyle({
10592                 'position' : 'relative',
10593                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10594             });
10595         }
10596         
10597         if(this.lazyLoad){
10598             
10599             var scrollHeight = this.bodyEl.dom.scrollHeight;
10600             
10601             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10602             
10603             var height = this.bodyEl.getHeight();
10604             
10605             if(scrollHeight - height == scrollTop) {
10606                 
10607                 var total = this.ds.getTotalCount();
10608                 
10609                 if(this.footer.cursor + this.footer.pageSize < total){
10610                     
10611                     this.footer.ds.load({
10612                         params : {
10613                             start : this.footer.cursor + this.footer.pageSize,
10614                             limit : this.footer.pageSize
10615                         },
10616                         add : true
10617                     });
10618                 }
10619             }
10620             
10621         }
10622     },
10623     onColumnSplitterMoved : function(i, diff)
10624     {
10625         this.userResized = true;
10626         
10627         var cm = this.colModel;
10628         
10629         var w = this.getHeaderIndex(i).getWidth() + diff;
10630         
10631         
10632         cm.setColumnWidth(i, w, true);
10633         this.initCSS();
10634         //var cid = cm.getColumnId(i); << not used in this version?
10635        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10636         
10637         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10638         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10639         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10640 */
10641         //this.updateSplitters();
10642         //this.layout(); << ??
10643         this.fireEvent("columnresize", i, w);
10644     },
10645     onHeaderChange : function()
10646     {
10647         var header = this.renderHeader();
10648         var table = this.el.select('table', true).first();
10649         
10650         this.headEl.remove();
10651         this.headEl = table.createChild(header, this.bodyEl, false);
10652         
10653         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10654             e.on('click', this.sort, this);
10655         }, this);
10656         
10657         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10658             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10659         }
10660         
10661     },
10662     
10663     onHiddenChange : function(colModel, colIndex, hidden)
10664     {
10665         /*
10666         this.cm.setHidden()
10667         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10668         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10669         
10670         this.CSS.updateRule(thSelector, "display", "");
10671         this.CSS.updateRule(tdSelector, "display", "");
10672         
10673         if(hidden){
10674             this.CSS.updateRule(thSelector, "display", "none");
10675             this.CSS.updateRule(tdSelector, "display", "none");
10676         }
10677         */
10678         // onload calls initCSS()
10679         this.onHeaderChange();
10680         this.onLoad();
10681     },
10682     
10683     setColumnWidth: function(col_index, width)
10684     {
10685         // width = "md-2 xs-2..."
10686         if(!this.colModel.config[col_index]) {
10687             return;
10688         }
10689         
10690         var w = width.split(" ");
10691         
10692         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10693         
10694         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10695         
10696         
10697         for(var j = 0; j < w.length; j++) {
10698             
10699             if(!w[j]) {
10700                 continue;
10701             }
10702             
10703             var size_cls = w[j].split("-");
10704             
10705             if(!Number.isInteger(size_cls[1] * 1)) {
10706                 continue;
10707             }
10708             
10709             if(!this.colModel.config[col_index][size_cls[0]]) {
10710                 continue;
10711             }
10712             
10713             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10714                 continue;
10715             }
10716             
10717             h_row[0].classList.replace(
10718                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10719                 "col-"+size_cls[0]+"-"+size_cls[1]
10720             );
10721             
10722             for(var i = 0; i < rows.length; i++) {
10723                 
10724                 var size_cls = w[j].split("-");
10725                 
10726                 if(!Number.isInteger(size_cls[1] * 1)) {
10727                     continue;
10728                 }
10729                 
10730                 if(!this.colModel.config[col_index][size_cls[0]]) {
10731                     continue;
10732                 }
10733                 
10734                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10735                     continue;
10736                 }
10737                 
10738                 rows[i].classList.replace(
10739                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10740                     "col-"+size_cls[0]+"-"+size_cls[1]
10741                 );
10742             }
10743             
10744             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10745         }
10746     }
10747 });
10748
10749 // currently only used to find the split on drag.. 
10750 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10751
10752 /**
10753  * @depricated
10754 */
10755 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10756 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10757 /*
10758  * - LGPL
10759  *
10760  * table cell
10761  * 
10762  */
10763
10764 /**
10765  * @class Roo.bootstrap.TableCell
10766  * @extends Roo.bootstrap.Component
10767  * @children Roo.bootstrap.Component
10768  * @parent Roo.bootstrap.TableRow
10769  * Bootstrap TableCell class
10770  * 
10771  * @cfg {String} html cell contain text
10772  * @cfg {String} cls cell class
10773  * @cfg {String} tag cell tag (td|th) default td
10774  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10775  * @cfg {String} align Aligns the content in a cell
10776  * @cfg {String} axis Categorizes cells
10777  * @cfg {String} bgcolor Specifies the background color of a cell
10778  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10779  * @cfg {Number} colspan Specifies the number of columns a cell should span
10780  * @cfg {String} headers Specifies one or more header cells a cell is related to
10781  * @cfg {Number} height Sets the height of a cell
10782  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10783  * @cfg {Number} rowspan Sets the number of rows a cell should span
10784  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10785  * @cfg {String} valign Vertical aligns the content in a cell
10786  * @cfg {Number} width Specifies the width of a cell
10787  * 
10788  * @constructor
10789  * Create a new TableCell
10790  * @param {Object} config The config object
10791  */
10792
10793 Roo.bootstrap.TableCell = function(config){
10794     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10795 };
10796
10797 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10798     
10799     html: false,
10800     cls: false,
10801     tag: false,
10802     abbr: false,
10803     align: false,
10804     axis: false,
10805     bgcolor: false,
10806     charoff: false,
10807     colspan: false,
10808     headers: false,
10809     height: false,
10810     nowrap: false,
10811     rowspan: false,
10812     scope: false,
10813     valign: false,
10814     width: false,
10815     
10816     
10817     getAutoCreate : function(){
10818         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10819         
10820         cfg = {
10821             tag: 'td'
10822         };
10823         
10824         if(this.tag){
10825             cfg.tag = this.tag;
10826         }
10827         
10828         if (this.html) {
10829             cfg.html=this.html
10830         }
10831         if (this.cls) {
10832             cfg.cls=this.cls
10833         }
10834         if (this.abbr) {
10835             cfg.abbr=this.abbr
10836         }
10837         if (this.align) {
10838             cfg.align=this.align
10839         }
10840         if (this.axis) {
10841             cfg.axis=this.axis
10842         }
10843         if (this.bgcolor) {
10844             cfg.bgcolor=this.bgcolor
10845         }
10846         if (this.charoff) {
10847             cfg.charoff=this.charoff
10848         }
10849         if (this.colspan) {
10850             cfg.colspan=this.colspan
10851         }
10852         if (this.headers) {
10853             cfg.headers=this.headers
10854         }
10855         if (this.height) {
10856             cfg.height=this.height
10857         }
10858         if (this.nowrap) {
10859             cfg.nowrap=this.nowrap
10860         }
10861         if (this.rowspan) {
10862             cfg.rowspan=this.rowspan
10863         }
10864         if (this.scope) {
10865             cfg.scope=this.scope
10866         }
10867         if (this.valign) {
10868             cfg.valign=this.valign
10869         }
10870         if (this.width) {
10871             cfg.width=this.width
10872         }
10873         
10874         
10875         return cfg;
10876     }
10877    
10878 });
10879
10880  
10881
10882  /*
10883  * - LGPL
10884  *
10885  * table row
10886  * 
10887  */
10888
10889 /**
10890  * @class Roo.bootstrap.TableRow
10891  * @extends Roo.bootstrap.Component
10892  * @children Roo.bootstrap.TableCell
10893  * @parent Roo.bootstrap.TableBody
10894  * Bootstrap TableRow class
10895  * @cfg {String} cls row class
10896  * @cfg {String} align Aligns the content in a table row
10897  * @cfg {String} bgcolor Specifies a background color for a table row
10898  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10899  * @cfg {String} valign Vertical aligns the content in a table row
10900  * 
10901  * @constructor
10902  * Create a new TableRow
10903  * @param {Object} config The config object
10904  */
10905
10906 Roo.bootstrap.TableRow = function(config){
10907     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10908 };
10909
10910 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10911     
10912     cls: false,
10913     align: false,
10914     bgcolor: false,
10915     charoff: false,
10916     valign: false,
10917     
10918     getAutoCreate : function(){
10919         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10920         
10921         cfg = {
10922             tag: 'tr'
10923         };
10924             
10925         if(this.cls){
10926             cfg.cls = this.cls;
10927         }
10928         if(this.align){
10929             cfg.align = this.align;
10930         }
10931         if(this.bgcolor){
10932             cfg.bgcolor = this.bgcolor;
10933         }
10934         if(this.charoff){
10935             cfg.charoff = this.charoff;
10936         }
10937         if(this.valign){
10938             cfg.valign = this.valign;
10939         }
10940         
10941         return cfg;
10942     }
10943    
10944 });
10945
10946  
10947
10948  /*
10949  * - LGPL
10950  *
10951  * table body
10952  * 
10953  */
10954
10955 /**
10956  * @class Roo.bootstrap.TableBody
10957  * @extends Roo.bootstrap.Component
10958  * @children Roo.bootstrap.TableRow
10959  * @parent Roo.bootstrap.Table
10960  * Bootstrap TableBody class
10961  * @cfg {String} cls element class
10962  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10963  * @cfg {String} align Aligns the content inside the element
10964  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10965  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10966  * 
10967  * @constructor
10968  * Create a new TableBody
10969  * @param {Object} config The config object
10970  */
10971
10972 Roo.bootstrap.TableBody = function(config){
10973     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10974 };
10975
10976 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10977     
10978     cls: false,
10979     tag: false,
10980     align: false,
10981     charoff: false,
10982     valign: false,
10983     
10984     getAutoCreate : function(){
10985         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10986         
10987         cfg = {
10988             tag: 'tbody'
10989         };
10990             
10991         if (this.cls) {
10992             cfg.cls=this.cls
10993         }
10994         if(this.tag){
10995             cfg.tag = this.tag;
10996         }
10997         
10998         if(this.align){
10999             cfg.align = this.align;
11000         }
11001         if(this.charoff){
11002             cfg.charoff = this.charoff;
11003         }
11004         if(this.valign){
11005             cfg.valign = this.valign;
11006         }
11007         
11008         return cfg;
11009     }
11010     
11011     
11012 //    initEvents : function()
11013 //    {
11014 //        
11015 //        if(!this.store){
11016 //            return;
11017 //        }
11018 //        
11019 //        this.store = Roo.factory(this.store, Roo.data);
11020 //        this.store.on('load', this.onLoad, this);
11021 //        
11022 //        this.store.load();
11023 //        
11024 //    },
11025 //    
11026 //    onLoad: function () 
11027 //    {   
11028 //        this.fireEvent('load', this);
11029 //    }
11030 //    
11031 //   
11032 });
11033
11034  
11035
11036  /*
11037  * Based on:
11038  * Ext JS Library 1.1.1
11039  * Copyright(c) 2006-2007, Ext JS, LLC.
11040  *
11041  * Originally Released Under LGPL - original licence link has changed is not relivant.
11042  *
11043  * Fork - LGPL
11044  * <script type="text/javascript">
11045  */
11046
11047 // as we use this in bootstrap.
11048 Roo.namespace('Roo.form');
11049  /**
11050  * @class Roo.form.Action
11051  * Internal Class used to handle form actions
11052  * @constructor
11053  * @param {Roo.form.BasicForm} el The form element or its id
11054  * @param {Object} config Configuration options
11055  */
11056
11057  
11058  
11059 // define the action interface
11060 Roo.form.Action = function(form, options){
11061     this.form = form;
11062     this.options = options || {};
11063 };
11064 /**
11065  * Client Validation Failed
11066  * @const 
11067  */
11068 Roo.form.Action.CLIENT_INVALID = 'client';
11069 /**
11070  * Server Validation Failed
11071  * @const 
11072  */
11073 Roo.form.Action.SERVER_INVALID = 'server';
11074  /**
11075  * Connect to Server Failed
11076  * @const 
11077  */
11078 Roo.form.Action.CONNECT_FAILURE = 'connect';
11079 /**
11080  * Reading Data from Server Failed
11081  * @const 
11082  */
11083 Roo.form.Action.LOAD_FAILURE = 'load';
11084
11085 Roo.form.Action.prototype = {
11086     type : 'default',
11087     failureType : undefined,
11088     response : undefined,
11089     result : undefined,
11090
11091     // interface method
11092     run : function(options){
11093
11094     },
11095
11096     // interface method
11097     success : function(response){
11098
11099     },
11100
11101     // interface method
11102     handleResponse : function(response){
11103
11104     },
11105
11106     // default connection failure
11107     failure : function(response){
11108         
11109         this.response = response;
11110         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11111         this.form.afterAction(this, false);
11112     },
11113
11114     processResponse : function(response){
11115         this.response = response;
11116         if(!response.responseText){
11117             return true;
11118         }
11119         this.result = this.handleResponse(response);
11120         return this.result;
11121     },
11122
11123     // utility functions used internally
11124     getUrl : function(appendParams){
11125         var url = this.options.url || this.form.url || this.form.el.dom.action;
11126         if(appendParams){
11127             var p = this.getParams();
11128             if(p){
11129                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11130             }
11131         }
11132         return url;
11133     },
11134
11135     getMethod : function(){
11136         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11137     },
11138
11139     getParams : function(){
11140         var bp = this.form.baseParams;
11141         var p = this.options.params;
11142         if(p){
11143             if(typeof p == "object"){
11144                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11145             }else if(typeof p == 'string' && bp){
11146                 p += '&' + Roo.urlEncode(bp);
11147             }
11148         }else if(bp){
11149             p = Roo.urlEncode(bp);
11150         }
11151         return p;
11152     },
11153
11154     createCallback : function(){
11155         return {
11156             success: this.success,
11157             failure: this.failure,
11158             scope: this,
11159             timeout: (this.form.timeout*1000),
11160             upload: this.form.fileUpload ? this.success : undefined
11161         };
11162     }
11163 };
11164
11165 Roo.form.Action.Submit = function(form, options){
11166     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11167 };
11168
11169 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11170     type : 'submit',
11171
11172     haveProgress : false,
11173     uploadComplete : false,
11174     
11175     // uploadProgress indicator.
11176     uploadProgress : function()
11177     {
11178         if (!this.form.progressUrl) {
11179             return;
11180         }
11181         
11182         if (!this.haveProgress) {
11183             Roo.MessageBox.progress("Uploading", "Uploading");
11184         }
11185         if (this.uploadComplete) {
11186            Roo.MessageBox.hide();
11187            return;
11188         }
11189         
11190         this.haveProgress = true;
11191    
11192         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11193         
11194         var c = new Roo.data.Connection();
11195         c.request({
11196             url : this.form.progressUrl,
11197             params: {
11198                 id : uid
11199             },
11200             method: 'GET',
11201             success : function(req){
11202                //console.log(data);
11203                 var rdata = false;
11204                 var edata;
11205                 try  {
11206                    rdata = Roo.decode(req.responseText)
11207                 } catch (e) {
11208                     Roo.log("Invalid data from server..");
11209                     Roo.log(edata);
11210                     return;
11211                 }
11212                 if (!rdata || !rdata.success) {
11213                     Roo.log(rdata);
11214                     Roo.MessageBox.alert(Roo.encode(rdata));
11215                     return;
11216                 }
11217                 var data = rdata.data;
11218                 
11219                 if (this.uploadComplete) {
11220                    Roo.MessageBox.hide();
11221                    return;
11222                 }
11223                    
11224                 if (data){
11225                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11226                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11227                     );
11228                 }
11229                 this.uploadProgress.defer(2000,this);
11230             },
11231        
11232             failure: function(data) {
11233                 Roo.log('progress url failed ');
11234                 Roo.log(data);
11235             },
11236             scope : this
11237         });
11238            
11239     },
11240     
11241     
11242     run : function()
11243     {
11244         // run get Values on the form, so it syncs any secondary forms.
11245         this.form.getValues();
11246         
11247         var o = this.options;
11248         var method = this.getMethod();
11249         var isPost = method == 'POST';
11250         if(o.clientValidation === false || this.form.isValid()){
11251             
11252             if (this.form.progressUrl) {
11253                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11254                     (new Date() * 1) + '' + Math.random());
11255                     
11256             } 
11257             
11258             
11259             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11260                 form:this.form.el.dom,
11261                 url:this.getUrl(!isPost),
11262                 method: method,
11263                 params:isPost ? this.getParams() : null,
11264                 isUpload: this.form.fileUpload,
11265                 formData : this.form.formData
11266             }));
11267             
11268             this.uploadProgress();
11269
11270         }else if (o.clientValidation !== false){ // client validation failed
11271             this.failureType = Roo.form.Action.CLIENT_INVALID;
11272             this.form.afterAction(this, false);
11273         }
11274     },
11275
11276     success : function(response)
11277     {
11278         this.uploadComplete= true;
11279         if (this.haveProgress) {
11280             Roo.MessageBox.hide();
11281         }
11282         
11283         
11284         var result = this.processResponse(response);
11285         if(result === true || result.success){
11286             this.form.afterAction(this, true);
11287             return;
11288         }
11289         if(result.errors){
11290             this.form.markInvalid(result.errors);
11291             this.failureType = Roo.form.Action.SERVER_INVALID;
11292         }
11293         this.form.afterAction(this, false);
11294     },
11295     failure : function(response)
11296     {
11297         this.uploadComplete= true;
11298         if (this.haveProgress) {
11299             Roo.MessageBox.hide();
11300         }
11301         
11302         this.response = response;
11303         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11304         this.form.afterAction(this, false);
11305     },
11306     
11307     handleResponse : function(response){
11308         if(this.form.errorReader){
11309             var rs = this.form.errorReader.read(response);
11310             var errors = [];
11311             if(rs.records){
11312                 for(var i = 0, len = rs.records.length; i < len; i++) {
11313                     var r = rs.records[i];
11314                     errors[i] = r.data;
11315                 }
11316             }
11317             if(errors.length < 1){
11318                 errors = null;
11319             }
11320             return {
11321                 success : rs.success,
11322                 errors : errors
11323             };
11324         }
11325         var ret = false;
11326         try {
11327             var rt = response.responseText;
11328             if (rt.match(/^\<!--\[CDATA\[/)) {
11329                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11330                 rt = rt.replace(/\]\]--\>$/,'');
11331             }
11332             
11333             ret = Roo.decode(rt);
11334         } catch (e) {
11335             ret = {
11336                 success: false,
11337                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11338                 errors : []
11339             };
11340         }
11341         return ret;
11342         
11343     }
11344 });
11345
11346
11347 Roo.form.Action.Load = function(form, options){
11348     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11349     this.reader = this.form.reader;
11350 };
11351
11352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11353     type : 'load',
11354
11355     run : function(){
11356         
11357         Roo.Ajax.request(Roo.apply(
11358                 this.createCallback(), {
11359                     method:this.getMethod(),
11360                     url:this.getUrl(false),
11361                     params:this.getParams()
11362         }));
11363     },
11364
11365     success : function(response){
11366         
11367         var result = this.processResponse(response);
11368         if(result === true || !result.success || !result.data){
11369             this.failureType = Roo.form.Action.LOAD_FAILURE;
11370             this.form.afterAction(this, false);
11371             return;
11372         }
11373         this.form.clearInvalid();
11374         this.form.setValues(result.data);
11375         this.form.afterAction(this, true);
11376     },
11377
11378     handleResponse : function(response){
11379         if(this.form.reader){
11380             var rs = this.form.reader.read(response);
11381             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11382             return {
11383                 success : rs.success,
11384                 data : data
11385             };
11386         }
11387         return Roo.decode(response.responseText);
11388     }
11389 });
11390
11391 Roo.form.Action.ACTION_TYPES = {
11392     'load' : Roo.form.Action.Load,
11393     'submit' : Roo.form.Action.Submit
11394 };/*
11395  * - LGPL
11396  *
11397  * form
11398  *
11399  */
11400
11401 /**
11402  * @class Roo.bootstrap.form.Form
11403  * @extends Roo.bootstrap.Component
11404  * @children Roo.bootstrap.Component
11405  * Bootstrap Form class
11406  * @cfg {String} method  GET | POST (default POST)
11407  * @cfg {String} labelAlign top | left (default top)
11408  * @cfg {String} align left  | right - for navbars
11409  * @cfg {Boolean} loadMask load mask when submit (default true)
11410
11411  *
11412  * @constructor
11413  * Create a new Form
11414  * @param {Object} config The config object
11415  */
11416
11417
11418 Roo.bootstrap.form.Form = function(config){
11419     
11420     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11421     
11422     Roo.bootstrap.form.Form.popover.apply();
11423     
11424     this.addEvents({
11425         /**
11426          * @event clientvalidation
11427          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11428          * @param {Form} this
11429          * @param {Boolean} valid true if the form has passed client-side validation
11430          */
11431         clientvalidation: true,
11432         /**
11433          * @event beforeaction
11434          * Fires before any action is performed. Return false to cancel the action.
11435          * @param {Form} this
11436          * @param {Action} action The action to be performed
11437          */
11438         beforeaction: true,
11439         /**
11440          * @event actionfailed
11441          * Fires when an action fails.
11442          * @param {Form} this
11443          * @param {Action} action The action that failed
11444          */
11445         actionfailed : true,
11446         /**
11447          * @event actioncomplete
11448          * Fires when an action is completed.
11449          * @param {Form} this
11450          * @param {Action} action The action that completed
11451          */
11452         actioncomplete : true
11453     });
11454 };
11455
11456 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11457
11458      /**
11459      * @cfg {String} method
11460      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11461      */
11462     method : 'POST',
11463     /**
11464      * @cfg {String} url
11465      * The URL to use for form actions if one isn't supplied in the action options.
11466      */
11467     /**
11468      * @cfg {Boolean} fileUpload
11469      * Set to true if this form is a file upload.
11470      */
11471
11472     /**
11473      * @cfg {Object} baseParams
11474      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11475      */
11476
11477     /**
11478      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11479      */
11480     timeout: 30,
11481     /**
11482      * @cfg {Sting} align (left|right) for navbar forms
11483      */
11484     align : 'left',
11485
11486     // private
11487     activeAction : null,
11488
11489     /**
11490      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11491      * element by passing it or its id or mask the form itself by passing in true.
11492      * @type Mixed
11493      */
11494     waitMsgTarget : false,
11495
11496     loadMask : true,
11497     
11498     /**
11499      * @cfg {Boolean} errorMask (true|false) default false
11500      */
11501     errorMask : false,
11502     
11503     /**
11504      * @cfg {Number} maskOffset Default 100
11505      */
11506     maskOffset : 100,
11507     
11508     /**
11509      * @cfg {Boolean} maskBody
11510      */
11511     maskBody : false,
11512
11513     getAutoCreate : function(){
11514
11515         var cfg = {
11516             tag: 'form',
11517             method : this.method || 'POST',
11518             id : this.id || Roo.id(),
11519             cls : ''
11520         };
11521         if (this.parent().xtype.match(/^Nav/)) {
11522             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11523
11524         }
11525
11526         if (this.labelAlign == 'left' ) {
11527             cfg.cls += ' form-horizontal';
11528         }
11529
11530
11531         return cfg;
11532     },
11533     initEvents : function()
11534     {
11535         this.el.on('submit', this.onSubmit, this);
11536         // this was added as random key presses on the form where triggering form submit.
11537         this.el.on('keypress', function(e) {
11538             if (e.getCharCode() != 13) {
11539                 return true;
11540             }
11541             // we might need to allow it for textareas.. and some other items.
11542             // check e.getTarget().
11543
11544             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11545                 return true;
11546             }
11547
11548             Roo.log("keypress blocked");
11549
11550             e.preventDefault();
11551             return false;
11552         });
11553         
11554     },
11555     // private
11556     onSubmit : function(e){
11557         e.stopEvent();
11558     },
11559
11560      /**
11561      * Returns true if client-side validation on the form is successful.
11562      * @return Boolean
11563      */
11564     isValid : function(){
11565         var items = this.getItems();
11566         var valid = true;
11567         var target = false;
11568         
11569         items.each(function(f){
11570             
11571             if(f.validate()){
11572                 return;
11573             }
11574             
11575             Roo.log('invalid field: ' + f.name);
11576             
11577             valid = false;
11578
11579             if(!target && f.el.isVisible(true)){
11580                 target = f;
11581             }
11582            
11583         });
11584         
11585         if(this.errorMask && !valid){
11586             Roo.bootstrap.form.Form.popover.mask(this, target);
11587         }
11588         
11589         return valid;
11590     },
11591     
11592     /**
11593      * Returns true if any fields in this form have changed since their original load.
11594      * @return Boolean
11595      */
11596     isDirty : function(){
11597         var dirty = false;
11598         var items = this.getItems();
11599         items.each(function(f){
11600            if(f.isDirty()){
11601                dirty = true;
11602                return false;
11603            }
11604            return true;
11605         });
11606         return dirty;
11607     },
11608      /**
11609      * Performs a predefined action (submit or load) or custom actions you define on this form.
11610      * @param {String} actionName The name of the action type
11611      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11612      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11613      * accept other config options):
11614      * <pre>
11615 Property          Type             Description
11616 ----------------  ---------------  ----------------------------------------------------------------------------------
11617 url               String           The url for the action (defaults to the form's url)
11618 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11619 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11620 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11621                                    validate the form on the client (defaults to false)
11622      * </pre>
11623      * @return {BasicForm} this
11624      */
11625     doAction : function(action, options){
11626         if(typeof action == 'string'){
11627             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11628         }
11629         if(this.fireEvent('beforeaction', this, action) !== false){
11630             this.beforeAction(action);
11631             action.run.defer(100, action);
11632         }
11633         return this;
11634     },
11635
11636     // private
11637     beforeAction : function(action){
11638         var o = action.options;
11639         
11640         if(this.loadMask){
11641             
11642             if(this.maskBody){
11643                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11644             } else {
11645                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646             }
11647         }
11648         // not really supported yet.. ??
11649
11650         //if(this.waitMsgTarget === true){
11651         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11652         //}else if(this.waitMsgTarget){
11653         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11654         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11655         //}else {
11656         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11657        // }
11658
11659     },
11660
11661     // private
11662     afterAction : function(action, success){
11663         this.activeAction = null;
11664         var o = action.options;
11665
11666         if(this.loadMask){
11667             
11668             if(this.maskBody){
11669                 Roo.get(document.body).unmask();
11670             } else {
11671                 this.el.unmask();
11672             }
11673         }
11674         
11675         //if(this.waitMsgTarget === true){
11676 //            this.el.unmask();
11677         //}else if(this.waitMsgTarget){
11678         //    this.waitMsgTarget.unmask();
11679         //}else{
11680         //    Roo.MessageBox.updateProgress(1);
11681         //    Roo.MessageBox.hide();
11682        // }
11683         //
11684         if(success){
11685             if(o.reset){
11686                 this.reset();
11687             }
11688             Roo.callback(o.success, o.scope, [this, action]);
11689             this.fireEvent('actioncomplete', this, action);
11690
11691         }else{
11692
11693             // failure condition..
11694             // we have a scenario where updates need confirming.
11695             // eg. if a locking scenario exists..
11696             // we look for { errors : { needs_confirm : true }} in the response.
11697             if (
11698                 (typeof(action.result) != 'undefined')  &&
11699                 (typeof(action.result.errors) != 'undefined')  &&
11700                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11701            ){
11702                 var _t = this;
11703                 Roo.log("not supported yet");
11704                  /*
11705
11706                 Roo.MessageBox.confirm(
11707                     "Change requires confirmation",
11708                     action.result.errorMsg,
11709                     function(r) {
11710                         if (r != 'yes') {
11711                             return;
11712                         }
11713                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11714                     }
11715
11716                 );
11717                 */
11718
11719
11720                 return;
11721             }
11722
11723             Roo.callback(o.failure, o.scope, [this, action]);
11724             // show an error message if no failed handler is set..
11725             if (!this.hasListener('actionfailed')) {
11726                 Roo.log("need to add dialog support");
11727                 /*
11728                 Roo.MessageBox.alert("Error",
11729                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11730                         action.result.errorMsg :
11731                         "Saving Failed, please check your entries or try again"
11732                 );
11733                 */
11734             }
11735
11736             this.fireEvent('actionfailed', this, action);
11737         }
11738
11739     },
11740     /**
11741      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11742      * @param {String} id The value to search for
11743      * @return Field
11744      */
11745     findField : function(id){
11746         var items = this.getItems();
11747         var field = items.get(id);
11748         if(!field){
11749              items.each(function(f){
11750                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11751                     field = f;
11752                     return false;
11753                 }
11754                 return true;
11755             });
11756         }
11757         return field || null;
11758     },
11759      /**
11760      * Mark fields in this form invalid in bulk.
11761      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11762      * @return {BasicForm} this
11763      */
11764     markInvalid : function(errors){
11765         if(errors instanceof Array){
11766             for(var i = 0, len = errors.length; i < len; i++){
11767                 var fieldError = errors[i];
11768                 var f = this.findField(fieldError.id);
11769                 if(f){
11770                     f.markInvalid(fieldError.msg);
11771                 }
11772             }
11773         }else{
11774             var field, id;
11775             for(id in errors){
11776                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11777                     field.markInvalid(errors[id]);
11778                 }
11779             }
11780         }
11781         //Roo.each(this.childForms || [], function (f) {
11782         //    f.markInvalid(errors);
11783         //});
11784
11785         return this;
11786     },
11787
11788     /**
11789      * Set values for fields in this form in bulk.
11790      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11791      * @return {BasicForm} this
11792      */
11793     setValues : function(values){
11794         if(values instanceof Array){ // array of objects
11795             for(var i = 0, len = values.length; i < len; i++){
11796                 var v = values[i];
11797                 var f = this.findField(v.id);
11798                 if(f){
11799                     f.setValue(v.value);
11800                     if(this.trackResetOnLoad){
11801                         f.originalValue = f.getValue();
11802                     }
11803                 }
11804             }
11805         }else{ // object hash
11806             var field, id;
11807             for(id in values){
11808                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11809
11810                     if (field.setFromData &&
11811                         field.valueField &&
11812                         field.displayField &&
11813                         // combos' with local stores can
11814                         // be queried via setValue()
11815                         // to set their value..
11816                         (field.store && !field.store.isLocal)
11817                         ) {
11818                         // it's a combo
11819                         var sd = { };
11820                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11821                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11822                         field.setFromData(sd);
11823
11824                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11825                         
11826                         field.setFromData(values);
11827                         
11828                     } else {
11829                         field.setValue(values[id]);
11830                     }
11831
11832
11833                     if(this.trackResetOnLoad){
11834                         field.originalValue = field.getValue();
11835                     }
11836                 }
11837             }
11838         }
11839
11840         //Roo.each(this.childForms || [], function (f) {
11841         //    f.setValues(values);
11842         //});
11843
11844         return this;
11845     },
11846
11847     /**
11848      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11849      * they are returned as an array.
11850      * @param {Boolean} asString
11851      * @return {Object}
11852      */
11853     getValues : function(asString){
11854         //if (this.childForms) {
11855             // copy values from the child forms
11856         //    Roo.each(this.childForms, function (f) {
11857         //        this.setValues(f.getValues());
11858         //    }, this);
11859         //}
11860
11861
11862
11863         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11864         if(asString === true){
11865             return fs;
11866         }
11867         return Roo.urlDecode(fs);
11868     },
11869
11870     /**
11871      * Returns the fields in this form as an object with key/value pairs.
11872      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11873      * @return {Object}
11874      */
11875     getFieldValues : function(with_hidden)
11876     {
11877         var items = this.getItems();
11878         var ret = {};
11879         items.each(function(f){
11880             
11881             if (!f.getName()) {
11882                 return;
11883             }
11884             
11885             var v = f.getValue();
11886             
11887             if (f.inputType =='radio') {
11888                 if (typeof(ret[f.getName()]) == 'undefined') {
11889                     ret[f.getName()] = ''; // empty..
11890                 }
11891
11892                 if (!f.el.dom.checked) {
11893                     return;
11894
11895                 }
11896                 v = f.el.dom.value;
11897
11898             }
11899             
11900             if(f.xtype == 'MoneyField'){
11901                 ret[f.currencyName] = f.getCurrency();
11902             }
11903
11904             // not sure if this supported any more..
11905             if ((typeof(v) == 'object') && f.getRawValue) {
11906                 v = f.getRawValue() ; // dates..
11907             }
11908             // combo boxes where name != hiddenName...
11909             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11910                 ret[f.name] = f.getRawValue();
11911             }
11912             ret[f.getName()] = v;
11913         });
11914
11915         return ret;
11916     },
11917
11918     /**
11919      * Clears all invalid messages in this form.
11920      * @return {BasicForm} this
11921      */
11922     clearInvalid : function(){
11923         var items = this.getItems();
11924
11925         items.each(function(f){
11926            f.clearInvalid();
11927         });
11928
11929         return this;
11930     },
11931
11932     /**
11933      * Resets this form.
11934      * @return {BasicForm} this
11935      */
11936     reset : function(){
11937         var items = this.getItems();
11938         items.each(function(f){
11939             f.reset();
11940         });
11941
11942         Roo.each(this.childForms || [], function (f) {
11943             f.reset();
11944         });
11945
11946
11947         return this;
11948     },
11949     
11950     getItems : function()
11951     {
11952         var r=new Roo.util.MixedCollection(false, function(o){
11953             return o.id || (o.id = Roo.id());
11954         });
11955         var iter = function(el) {
11956             if (el.inputEl) {
11957                 r.add(el);
11958             }
11959             if (!el.items) {
11960                 return;
11961             }
11962             Roo.each(el.items,function(e) {
11963                 iter(e);
11964             });
11965         };
11966
11967         iter(this);
11968         return r;
11969     },
11970     
11971     hideFields : function(items)
11972     {
11973         Roo.each(items, function(i){
11974             
11975             var f = this.findField(i);
11976             
11977             if(!f){
11978                 return;
11979             }
11980             
11981             f.hide();
11982             
11983         }, this);
11984     },
11985     
11986     showFields : function(items)
11987     {
11988         Roo.each(items, function(i){
11989             
11990             var f = this.findField(i);
11991             
11992             if(!f){
11993                 return;
11994             }
11995             
11996             f.show();
11997             
11998         }, this);
11999     }
12000
12001 });
12002
12003 Roo.apply(Roo.bootstrap.form.Form, {
12004     
12005     popover : {
12006         
12007         padding : 5,
12008         
12009         isApplied : false,
12010         
12011         isMasked : false,
12012         
12013         form : false,
12014         
12015         target : false,
12016         
12017         toolTip : false,
12018         
12019         intervalID : false,
12020         
12021         maskEl : false,
12022         
12023         apply : function()
12024         {
12025             if(this.isApplied){
12026                 return;
12027             }
12028             
12029             this.maskEl = {
12030                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12031                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12032                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12033                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12034             };
12035             
12036             this.maskEl.top.enableDisplayMode("block");
12037             this.maskEl.left.enableDisplayMode("block");
12038             this.maskEl.bottom.enableDisplayMode("block");
12039             this.maskEl.right.enableDisplayMode("block");
12040             
12041             this.toolTip = new Roo.bootstrap.Tooltip({
12042                 cls : 'roo-form-error-popover',
12043                 alignment : {
12044                     'left' : ['r-l', [-2,0], 'right'],
12045                     'right' : ['l-r', [2,0], 'left'],
12046                     'bottom' : ['tl-bl', [0,2], 'top'],
12047                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12048                 }
12049             });
12050             
12051             this.toolTip.render(Roo.get(document.body));
12052
12053             this.toolTip.el.enableDisplayMode("block");
12054             
12055             Roo.get(document.body).on('click', function(){
12056                 this.unmask();
12057             }, this);
12058             
12059             Roo.get(document.body).on('touchstart', function(){
12060                 this.unmask();
12061             }, this);
12062             
12063             this.isApplied = true
12064         },
12065         
12066         mask : function(form, target)
12067         {
12068             this.form = form;
12069             
12070             this.target = target;
12071             
12072             if(!this.form.errorMask || !target.el){
12073                 return;
12074             }
12075             
12076             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12077             
12078             Roo.log(scrollable);
12079             
12080             var ot = this.target.el.calcOffsetsTo(scrollable);
12081             
12082             var scrollTo = ot[1] - this.form.maskOffset;
12083             
12084             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12085             
12086             scrollable.scrollTo('top', scrollTo);
12087             
12088             var box = this.target.el.getBox();
12089             Roo.log(box);
12090             var zIndex = Roo.bootstrap.Modal.zIndex++;
12091
12092             
12093             this.maskEl.top.setStyle('position', 'absolute');
12094             this.maskEl.top.setStyle('z-index', zIndex);
12095             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12096             this.maskEl.top.setLeft(0);
12097             this.maskEl.top.setTop(0);
12098             this.maskEl.top.show();
12099             
12100             this.maskEl.left.setStyle('position', 'absolute');
12101             this.maskEl.left.setStyle('z-index', zIndex);
12102             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12103             this.maskEl.left.setLeft(0);
12104             this.maskEl.left.setTop(box.y - this.padding);
12105             this.maskEl.left.show();
12106
12107             this.maskEl.bottom.setStyle('position', 'absolute');
12108             this.maskEl.bottom.setStyle('z-index', zIndex);
12109             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12110             this.maskEl.bottom.setLeft(0);
12111             this.maskEl.bottom.setTop(box.bottom + this.padding);
12112             this.maskEl.bottom.show();
12113
12114             this.maskEl.right.setStyle('position', 'absolute');
12115             this.maskEl.right.setStyle('z-index', zIndex);
12116             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12117             this.maskEl.right.setLeft(box.right + this.padding);
12118             this.maskEl.right.setTop(box.y - this.padding);
12119             this.maskEl.right.show();
12120
12121             this.toolTip.bindEl = this.target.el;
12122
12123             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12124
12125             var tip = this.target.blankText;
12126
12127             if(this.target.getValue() !== '' ) {
12128                 
12129                 if (this.target.invalidText.length) {
12130                     tip = this.target.invalidText;
12131                 } else if (this.target.regexText.length){
12132                     tip = this.target.regexText;
12133                 }
12134             }
12135
12136             this.toolTip.show(tip);
12137
12138             this.intervalID = window.setInterval(function() {
12139                 Roo.bootstrap.form.Form.popover.unmask();
12140             }, 10000);
12141
12142             window.onwheel = function(){ return false;};
12143             
12144             (function(){ this.isMasked = true; }).defer(500, this);
12145             
12146         },
12147         
12148         unmask : function()
12149         {
12150             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12151                 return;
12152             }
12153             
12154             this.maskEl.top.setStyle('position', 'absolute');
12155             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12156             this.maskEl.top.hide();
12157
12158             this.maskEl.left.setStyle('position', 'absolute');
12159             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12160             this.maskEl.left.hide();
12161
12162             this.maskEl.bottom.setStyle('position', 'absolute');
12163             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12164             this.maskEl.bottom.hide();
12165
12166             this.maskEl.right.setStyle('position', 'absolute');
12167             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12168             this.maskEl.right.hide();
12169             
12170             this.toolTip.hide();
12171             
12172             this.toolTip.el.hide();
12173             
12174             window.onwheel = function(){ return true;};
12175             
12176             if(this.intervalID){
12177                 window.clearInterval(this.intervalID);
12178                 this.intervalID = false;
12179             }
12180             
12181             this.isMasked = false;
12182             
12183         }
12184         
12185     }
12186     
12187 });
12188
12189 /*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199 /**
12200  * @class Roo.form.VTypes
12201  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12202  * @static
12203  */
12204 Roo.form.VTypes = function(){
12205     // closure these in so they are only created once.
12206     var alpha = /^[a-zA-Z_]+$/;
12207     var alphanum = /^[a-zA-Z0-9_]+$/;
12208     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12209     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12210
12211     // All these messages and functions are configurable
12212     return {
12213         /**
12214          * The function used to validate email addresses
12215          * @param {String} value The email address
12216          */
12217         email : function(v){
12218             return email.test(v);
12219         },
12220         /**
12221          * The error text to display when the email validation function returns false
12222          * @type String
12223          */
12224         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12225         /**
12226          * The keystroke filter mask to be applied on email input
12227          * @type RegExp
12228          */
12229         emailMask : /[a-z0-9_\.\-@]/i,
12230
12231         /**
12232          * The function used to validate URLs
12233          * @param {String} value The URL
12234          */
12235         url : function(v){
12236             return url.test(v);
12237         },
12238         /**
12239          * The error text to display when the url validation function returns false
12240          * @type String
12241          */
12242         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12243         
12244         /**
12245          * The function used to validate alpha values
12246          * @param {String} value The value
12247          */
12248         alpha : function(v){
12249             return alpha.test(v);
12250         },
12251         /**
12252          * The error text to display when the alpha validation function returns false
12253          * @type String
12254          */
12255         alphaText : 'This field should only contain letters and _',
12256         /**
12257          * The keystroke filter mask to be applied on alpha input
12258          * @type RegExp
12259          */
12260         alphaMask : /[a-z_]/i,
12261
12262         /**
12263          * The function used to validate alphanumeric values
12264          * @param {String} value The value
12265          */
12266         alphanum : function(v){
12267             return alphanum.test(v);
12268         },
12269         /**
12270          * The error text to display when the alphanumeric validation function returns false
12271          * @type String
12272          */
12273         alphanumText : 'This field should only contain letters, numbers and _',
12274         /**
12275          * The keystroke filter mask to be applied on alphanumeric input
12276          * @type RegExp
12277          */
12278         alphanumMask : /[a-z0-9_]/i
12279     };
12280 }();/*
12281  * - LGPL
12282  *
12283  * Input
12284  * 
12285  */
12286
12287 /**
12288  * @class Roo.bootstrap.form.Input
12289  * @extends Roo.bootstrap.Component
12290  * Bootstrap Input class
12291  * @cfg {Boolean} disabled is it disabled
12292  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12293  * @cfg {String} name name of the input
12294  * @cfg {string} fieldLabel - the label associated
12295  * @cfg {string} placeholder - placeholder to put in text.
12296  * @cfg {string} before - input group add on before
12297  * @cfg {string} after - input group add on after
12298  * @cfg {string} size - (lg|sm) or leave empty..
12299  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12300  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12301  * @cfg {Number} md colspan out of 12 for computer-sized screens
12302  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12303  * @cfg {string} value default value of the input
12304  * @cfg {Number} labelWidth set the width of label 
12305  * @cfg {Number} labellg set the width of label (1-12)
12306  * @cfg {Number} labelmd set the width of label (1-12)
12307  * @cfg {Number} labelsm set the width of label (1-12)
12308  * @cfg {Number} labelxs set the width of label (1-12)
12309  * @cfg {String} labelAlign (top|left)
12310  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12311  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12312  * @cfg {String} indicatorpos (left|right) default left
12313  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12314  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12315  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12316  * @cfg {Roo.bootstrap.Button} before Button to show before
12317  * @cfg {Roo.bootstrap.Button} afterButton to show before
12318  * @cfg {String} align (left|center|right) Default left
12319  * @cfg {Boolean} forceFeedback (true|false) Default false
12320  * 
12321  * @constructor
12322  * Create a new Input
12323  * @param {Object} config The config object
12324  */
12325
12326 Roo.bootstrap.form.Input = function(config){
12327     
12328     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12329     
12330     this.addEvents({
12331         /**
12332          * @event focus
12333          * Fires when this field receives input focus.
12334          * @param {Roo.form.Field} this
12335          */
12336         focus : true,
12337         /**
12338          * @event blur
12339          * Fires when this field loses input focus.
12340          * @param {Roo.form.Field} this
12341          */
12342         blur : true,
12343         /**
12344          * @event specialkey
12345          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12346          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12347          * @param {Roo.form.Field} this
12348          * @param {Roo.EventObject} e The event object
12349          */
12350         specialkey : true,
12351         /**
12352          * @event change
12353          * Fires just before the field blurs if the field value has changed.
12354          * @param {Roo.form.Field} this
12355          * @param {Mixed} newValue The new value
12356          * @param {Mixed} oldValue The original value
12357          */
12358         change : true,
12359         /**
12360          * @event invalid
12361          * Fires after the field has been marked as invalid.
12362          * @param {Roo.form.Field} this
12363          * @param {String} msg The validation message
12364          */
12365         invalid : true,
12366         /**
12367          * @event valid
12368          * Fires after the field has been validated with no errors.
12369          * @param {Roo.form.Field} this
12370          */
12371         valid : true,
12372          /**
12373          * @event keyup
12374          * Fires after the key up
12375          * @param {Roo.form.Field} this
12376          * @param {Roo.EventObject}  e The event Object
12377          */
12378         keyup : true,
12379         /**
12380          * @event paste
12381          * Fires after the user pastes into input
12382          * @param {Roo.form.Field} this
12383          * @param {Roo.EventObject}  e The event Object
12384          */
12385         paste : true
12386     });
12387 };
12388
12389 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12390      /**
12391      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12392       automatic validation (defaults to "keyup").
12393      */
12394     validationEvent : "keyup",
12395      /**
12396      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12397      */
12398     validateOnBlur : true,
12399     /**
12400      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12401      */
12402     validationDelay : 250,
12403      /**
12404      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12405      */
12406     focusClass : "x-form-focus",  // not needed???
12407     
12408        
12409     /**
12410      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12411      */
12412     invalidClass : "has-warning",
12413     
12414     /**
12415      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12416      */
12417     validClass : "has-success",
12418     
12419     /**
12420      * @cfg {Boolean} hasFeedback (true|false) default true
12421      */
12422     hasFeedback : true,
12423     
12424     /**
12425      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12426      */
12427     invalidFeedbackClass : "glyphicon-warning-sign",
12428     
12429     /**
12430      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12431      */
12432     validFeedbackClass : "glyphicon-ok",
12433     
12434     /**
12435      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12436      */
12437     selectOnFocus : false,
12438     
12439      /**
12440      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12441      */
12442     maskRe : null,
12443        /**
12444      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12445      */
12446     vtype : null,
12447     
12448       /**
12449      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12450      */
12451     disableKeyFilter : false,
12452     
12453        /**
12454      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12455      */
12456     disabled : false,
12457      /**
12458      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12459      */
12460     allowBlank : true,
12461     /**
12462      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12463      */
12464     blankText : "Please complete this mandatory field",
12465     
12466      /**
12467      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12468      */
12469     minLength : 0,
12470     /**
12471      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12472      */
12473     maxLength : Number.MAX_VALUE,
12474     /**
12475      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12476      */
12477     minLengthText : "The minimum length for this field is {0}",
12478     /**
12479      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12480      */
12481     maxLengthText : "The maximum length for this field is {0}",
12482   
12483     
12484     /**
12485      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12486      * If available, this function will be called only after the basic validators all return true, and will be passed the
12487      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12488      */
12489     validator : null,
12490     /**
12491      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12492      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12493      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12494      */
12495     regex : null,
12496     /**
12497      * @cfg {String} regexText -- Depricated - use Invalid Text
12498      */
12499     regexText : "",
12500     
12501     /**
12502      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12503      */
12504     invalidText : "",
12505     
12506     
12507     
12508     autocomplete: false,
12509     
12510     
12511     fieldLabel : '',
12512     inputType : 'text',
12513     
12514     name : false,
12515     placeholder: false,
12516     before : false,
12517     after : false,
12518     size : false,
12519     hasFocus : false,
12520     preventMark: false,
12521     isFormField : true,
12522     value : '',
12523     labelWidth : 2,
12524     labelAlign : false,
12525     readOnly : false,
12526     align : false,
12527     formatedValue : false,
12528     forceFeedback : false,
12529     
12530     indicatorpos : 'left',
12531     
12532     labellg : 0,
12533     labelmd : 0,
12534     labelsm : 0,
12535     labelxs : 0,
12536     
12537     capture : '',
12538     accept : '',
12539     
12540     parentLabelAlign : function()
12541     {
12542         var parent = this;
12543         while (parent.parent()) {
12544             parent = parent.parent();
12545             if (typeof(parent.labelAlign) !='undefined') {
12546                 return parent.labelAlign;
12547             }
12548         }
12549         return 'left';
12550         
12551     },
12552     
12553     getAutoCreate : function()
12554     {
12555         
12556         var id = Roo.id();
12557         
12558         var cfg = {};
12559         
12560         if(this.inputType != 'hidden'){
12561             cfg.cls = 'form-group' //input-group
12562         }
12563         
12564         var input =  {
12565             tag: 'input',
12566             id : id,
12567             type : this.inputType,
12568             value : this.value,
12569             cls : 'form-control',
12570             placeholder : this.placeholder || '',
12571             autocomplete : this.autocomplete || 'new-password'
12572         };
12573         if (this.inputType == 'file') {
12574             input.style = 'overflow:hidden'; // why not in CSS?
12575         }
12576         
12577         if(this.capture.length){
12578             input.capture = this.capture;
12579         }
12580         
12581         if(this.accept.length){
12582             input.accept = this.accept + "/*";
12583         }
12584         
12585         if(this.align){
12586             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12587         }
12588         
12589         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12590             input.maxLength = this.maxLength;
12591         }
12592         
12593         if (this.disabled) {
12594             input.disabled=true;
12595         }
12596         
12597         if (this.readOnly) {
12598             input.readonly=true;
12599         }
12600         
12601         if (this.name) {
12602             input.name = this.name;
12603         }
12604         
12605         if (this.size) {
12606             input.cls += ' input-' + this.size;
12607         }
12608         
12609         var settings=this;
12610         ['xs','sm','md','lg'].map(function(size){
12611             if (settings[size]) {
12612                 cfg.cls += ' col-' + size + '-' + settings[size];
12613             }
12614         });
12615         
12616         var inputblock = input;
12617         
12618         var feedback = {
12619             tag: 'span',
12620             cls: 'glyphicon form-control-feedback'
12621         };
12622             
12623         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12624             
12625             inputblock = {
12626                 cls : 'has-feedback',
12627                 cn :  [
12628                     input,
12629                     feedback
12630                 ] 
12631             };  
12632         }
12633         
12634         if (this.before || this.after) {
12635             
12636             inputblock = {
12637                 cls : 'input-group',
12638                 cn :  [] 
12639             };
12640             
12641             if (this.before && typeof(this.before) == 'string') {
12642                 
12643                 inputblock.cn.push({
12644                     tag :'span',
12645                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12646                     html : this.before
12647                 });
12648             }
12649             if (this.before && typeof(this.before) == 'object') {
12650                 this.before = Roo.factory(this.before);
12651                 
12652                 inputblock.cn.push({
12653                     tag :'span',
12654                     cls : 'roo-input-before input-group-prepend   input-group-' +
12655                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12656                 });
12657             }
12658             
12659             inputblock.cn.push(input);
12660             
12661             if (this.after && typeof(this.after) == 'string') {
12662                 inputblock.cn.push({
12663                     tag :'span',
12664                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12665                     html : this.after
12666                 });
12667             }
12668             if (this.after && typeof(this.after) == 'object') {
12669                 this.after = Roo.factory(this.after);
12670                 
12671                 inputblock.cn.push({
12672                     tag :'span',
12673                     cls : 'roo-input-after input-group-append  input-group-' +
12674                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12675                 });
12676             }
12677             
12678             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12679                 inputblock.cls += ' has-feedback';
12680                 inputblock.cn.push(feedback);
12681             }
12682         };
12683         
12684         
12685         
12686         cfg = this.getAutoCreateLabel( cfg, inputblock );
12687         
12688        
12689          
12690         
12691         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12692            cfg.cls += ' navbar-form';
12693         }
12694         
12695         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12696             // on BS4 we do this only if not form 
12697             cfg.cls += ' navbar-form';
12698             cfg.tag = 'li';
12699         }
12700         
12701         return cfg;
12702         
12703     },
12704     /**
12705      * autocreate the label - also used by textara... ?? and others?
12706      */
12707     getAutoCreateLabel : function( cfg, inputblock )
12708     {
12709         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12710        
12711         var indicator = {
12712             tag : 'i',
12713             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12714             tooltip : 'This field is required'
12715         };
12716         if (this.allowBlank ) {
12717             indicator.style = this.allowBlank ? ' display:none' : '';
12718         }
12719         if (align ==='left' && this.fieldLabel.length) {
12720             
12721             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12722             
12723             cfg.cn = [
12724                 indicator,
12725                 {
12726                     tag: 'label',
12727                     'for' :  id,
12728                     cls : 'control-label col-form-label',
12729                     html : this.fieldLabel
12730
12731                 },
12732                 {
12733                     cls : "", 
12734                     cn: [
12735                         inputblock
12736                     ]
12737                 }
12738             ];
12739             
12740             var labelCfg = cfg.cn[1];
12741             var contentCfg = cfg.cn[2];
12742             
12743             if(this.indicatorpos == 'right'){
12744                 cfg.cn = [
12745                     {
12746                         tag: 'label',
12747                         'for' :  id,
12748                         cls : 'control-label col-form-label',
12749                         cn : [
12750                             {
12751                                 tag : 'span',
12752                                 html : this.fieldLabel
12753                             },
12754                             indicator
12755                         ]
12756                     },
12757                     {
12758                         cls : "",
12759                         cn: [
12760                             inputblock
12761                         ]
12762                     }
12763
12764                 ];
12765                 
12766                 labelCfg = cfg.cn[0];
12767                 contentCfg = cfg.cn[1];
12768             
12769             }
12770             
12771             if(this.labelWidth > 12){
12772                 labelCfg.style = "width: " + this.labelWidth + 'px';
12773             }
12774             
12775             if(this.labelWidth < 13 && this.labelmd == 0){
12776                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12777             }
12778             
12779             if(this.labellg > 0){
12780                 labelCfg.cls += ' col-lg-' + this.labellg;
12781                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12782             }
12783             
12784             if(this.labelmd > 0){
12785                 labelCfg.cls += ' col-md-' + this.labelmd;
12786                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12787             }
12788             
12789             if(this.labelsm > 0){
12790                 labelCfg.cls += ' col-sm-' + this.labelsm;
12791                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12792             }
12793             
12794             if(this.labelxs > 0){
12795                 labelCfg.cls += ' col-xs-' + this.labelxs;
12796                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12797             }
12798             
12799             
12800         } else if ( this.fieldLabel.length) {
12801                 
12802             
12803             
12804             cfg.cn = [
12805                 {
12806                     tag : 'i',
12807                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12808                     tooltip : 'This field is required',
12809                     style : this.allowBlank ? ' display:none' : '' 
12810                 },
12811                 {
12812                     tag: 'label',
12813                    //cls : 'input-group-addon',
12814                     html : this.fieldLabel
12815
12816                 },
12817
12818                inputblock
12819
12820            ];
12821            
12822            if(this.indicatorpos == 'right'){
12823        
12824                 cfg.cn = [
12825                     {
12826                         tag: 'label',
12827                        //cls : 'input-group-addon',
12828                         html : this.fieldLabel
12829
12830                     },
12831                     {
12832                         tag : 'i',
12833                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12834                         tooltip : 'This field is required',
12835                         style : this.allowBlank ? ' display:none' : '' 
12836                     },
12837
12838                    inputblock
12839
12840                ];
12841
12842             }
12843
12844         } else {
12845             
12846             cfg.cn = [
12847
12848                     inputblock
12849
12850             ];
12851                 
12852                 
12853         };
12854         return cfg;
12855     },
12856     
12857     
12858     /**
12859      * return the real input element.
12860      */
12861     inputEl: function ()
12862     {
12863         return this.el.select('input.form-control',true).first();
12864     },
12865     
12866     tooltipEl : function()
12867     {
12868         return this.inputEl();
12869     },
12870     
12871     indicatorEl : function()
12872     {
12873         if (Roo.bootstrap.version == 4) {
12874             return false; // not enabled in v4 yet.
12875         }
12876         
12877         var indicator = this.el.select('i.roo-required-indicator',true).first();
12878         
12879         if(!indicator){
12880             return false;
12881         }
12882         
12883         return indicator;
12884         
12885     },
12886     
12887     setDisabled : function(v)
12888     {
12889         var i  = this.inputEl().dom;
12890         if (!v) {
12891             i.removeAttribute('disabled');
12892             return;
12893             
12894         }
12895         i.setAttribute('disabled','true');
12896     },
12897     initEvents : function()
12898     {
12899           
12900         this.inputEl().on("keydown" , this.fireKey,  this);
12901         this.inputEl().on("focus", this.onFocus,  this);
12902         this.inputEl().on("blur", this.onBlur,  this);
12903         
12904         this.inputEl().relayEvent('keyup', this);
12905         this.inputEl().relayEvent('paste', this);
12906         
12907         this.indicator = this.indicatorEl();
12908         
12909         if(this.indicator){
12910             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12911         }
12912  
12913         // reference to original value for reset
12914         this.originalValue = this.getValue();
12915         //Roo.form.TextField.superclass.initEvents.call(this);
12916         if(this.validationEvent == 'keyup'){
12917             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12918             this.inputEl().on('keyup', this.filterValidation, this);
12919         }
12920         else if(this.validationEvent !== false){
12921             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12922         }
12923         
12924         if(this.selectOnFocus){
12925             this.on("focus", this.preFocus, this);
12926             
12927         }
12928         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12929             this.inputEl().on("keypress", this.filterKeys, this);
12930         } else {
12931             this.inputEl().relayEvent('keypress', this);
12932         }
12933        /* if(this.grow){
12934             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12935             this.el.on("click", this.autoSize,  this);
12936         }
12937         */
12938         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12939             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12940         }
12941         
12942         if (typeof(this.before) == 'object') {
12943             this.before.render(this.el.select('.roo-input-before',true).first());
12944         }
12945         if (typeof(this.after) == 'object') {
12946             this.after.render(this.el.select('.roo-input-after',true).first());
12947         }
12948         
12949         this.inputEl().on('change', this.onChange, this);
12950         
12951     },
12952     filterValidation : function(e){
12953         if(!e.isNavKeyPress()){
12954             this.validationTask.delay(this.validationDelay);
12955         }
12956     },
12957      /**
12958      * Validates the field value
12959      * @return {Boolean} True if the value is valid, else false
12960      */
12961     validate : function(){
12962         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12963         if(this.disabled || this.validateValue(this.getRawValue())){
12964             this.markValid();
12965             return true;
12966         }
12967         
12968         this.markInvalid();
12969         return false;
12970     },
12971     
12972     
12973     /**
12974      * Validates a value according to the field's validation rules and marks the field as invalid
12975      * if the validation fails
12976      * @param {Mixed} value The value to validate
12977      * @return {Boolean} True if the value is valid, else false
12978      */
12979     validateValue : function(value)
12980     {
12981         if(this.getVisibilityEl().hasClass('hidden')){
12982             return true;
12983         }
12984         
12985         if(value.length < 1)  { // if it's blank
12986             if(this.allowBlank){
12987                 return true;
12988             }
12989             return false;
12990         }
12991         
12992         if(value.length < this.minLength){
12993             return false;
12994         }
12995         if(value.length > this.maxLength){
12996             return false;
12997         }
12998         if(this.vtype){
12999             var vt = Roo.form.VTypes;
13000             if(!vt[this.vtype](value, this)){
13001                 return false;
13002             }
13003         }
13004         if(typeof this.validator == "function"){
13005             var msg = this.validator(value);
13006             if (typeof(msg) == 'string') {
13007                 this.invalidText = msg;
13008             }
13009             if(msg !== true){
13010                 return false;
13011             }
13012         }
13013         
13014         if(this.regex && !this.regex.test(value)){
13015             return false;
13016         }
13017         
13018         return true;
13019     },
13020     
13021      // private
13022     fireKey : function(e){
13023         //Roo.log('field ' + e.getKey());
13024         if(e.isNavKeyPress()){
13025             this.fireEvent("specialkey", this, e);
13026         }
13027     },
13028     focus : function (selectText){
13029         if(this.rendered){
13030             this.inputEl().focus();
13031             if(selectText === true){
13032                 this.inputEl().dom.select();
13033             }
13034         }
13035         return this;
13036     } ,
13037     
13038     onFocus : function(){
13039         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13040            // this.el.addClass(this.focusClass);
13041         }
13042         if(!this.hasFocus){
13043             this.hasFocus = true;
13044             this.startValue = this.getValue();
13045             this.fireEvent("focus", this);
13046         }
13047     },
13048     
13049     beforeBlur : Roo.emptyFn,
13050
13051     
13052     // private
13053     onBlur : function(){
13054         this.beforeBlur();
13055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13056             //this.el.removeClass(this.focusClass);
13057         }
13058         this.hasFocus = false;
13059         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13060             this.validate();
13061         }
13062         var v = this.getValue();
13063         if(String(v) !== String(this.startValue)){
13064             this.fireEvent('change', this, v, this.startValue);
13065         }
13066         this.fireEvent("blur", this);
13067     },
13068     
13069     onChange : function(e)
13070     {
13071         var v = this.getValue();
13072         if(String(v) !== String(this.startValue)){
13073             this.fireEvent('change', this, v, this.startValue);
13074         }
13075         
13076     },
13077     
13078     /**
13079      * Resets the current field value to the originally loaded value and clears any validation messages
13080      */
13081     reset : function(){
13082         this.setValue(this.originalValue);
13083         this.validate();
13084     },
13085      /**
13086      * Returns the name of the field
13087      * @return {Mixed} name The name field
13088      */
13089     getName: function(){
13090         return this.name;
13091     },
13092      /**
13093      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13094      * @return {Mixed} value The field value
13095      */
13096     getValue : function(){
13097         var v = this.inputEl().getValue();
13098         return v;
13099     },
13100     /**
13101      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13102      * @return {Mixed} value The field value
13103      */
13104     getRawValue : function(){
13105         var v = this.inputEl().getValue();
13106         
13107         return v;
13108     },
13109     
13110     /**
13111      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13112      * @param {Mixed} value The value to set
13113      */
13114     setRawValue : function(v){
13115         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13116     },
13117     
13118     selectText : function(start, end){
13119         var v = this.getRawValue();
13120         if(v.length > 0){
13121             start = start === undefined ? 0 : start;
13122             end = end === undefined ? v.length : end;
13123             var d = this.inputEl().dom;
13124             if(d.setSelectionRange){
13125                 d.setSelectionRange(start, end);
13126             }else if(d.createTextRange){
13127                 var range = d.createTextRange();
13128                 range.moveStart("character", start);
13129                 range.moveEnd("character", v.length-end);
13130                 range.select();
13131             }
13132         }
13133     },
13134     
13135     /**
13136      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13137      * @param {Mixed} value The value to set
13138      */
13139     setValue : function(v){
13140         this.value = v;
13141         if(this.rendered){
13142             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13143             this.validate();
13144         }
13145     },
13146     
13147     /*
13148     processValue : function(value){
13149         if(this.stripCharsRe){
13150             var newValue = value.replace(this.stripCharsRe, '');
13151             if(newValue !== value){
13152                 this.setRawValue(newValue);
13153                 return newValue;
13154             }
13155         }
13156         return value;
13157     },
13158   */
13159     preFocus : function(){
13160         
13161         if(this.selectOnFocus){
13162             this.inputEl().dom.select();
13163         }
13164     },
13165     filterKeys : function(e){
13166         var k = e.getKey();
13167         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13168             return;
13169         }
13170         var c = e.getCharCode(), cc = String.fromCharCode(c);
13171         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13172             return;
13173         }
13174         if(!this.maskRe.test(cc)){
13175             e.stopEvent();
13176         }
13177     },
13178      /**
13179      * Clear any invalid styles/messages for this field
13180      */
13181     clearInvalid : function(){
13182         
13183         if(!this.el || this.preventMark){ // not rendered
13184             return;
13185         }
13186         
13187         
13188         this.el.removeClass([this.invalidClass, 'is-invalid']);
13189         
13190         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13191             
13192             var feedback = this.el.select('.form-control-feedback', true).first();
13193             
13194             if(feedback){
13195                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13196             }
13197             
13198         }
13199         
13200         if(this.indicator){
13201             this.indicator.removeClass('visible');
13202             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13203         }
13204         
13205         this.fireEvent('valid', this);
13206     },
13207     
13208      /**
13209      * Mark this field as valid
13210      */
13211     markValid : function()
13212     {
13213         if(!this.el  || this.preventMark){ // not rendered...
13214             return;
13215         }
13216         
13217         this.el.removeClass([this.invalidClass, this.validClass]);
13218         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13219
13220         var feedback = this.el.select('.form-control-feedback', true).first();
13221             
13222         if(feedback){
13223             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13224         }
13225         
13226         if(this.indicator){
13227             this.indicator.removeClass('visible');
13228             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13229         }
13230         
13231         if(this.disabled){
13232             return;
13233         }
13234         
13235            
13236         if(this.allowBlank && !this.getRawValue().length){
13237             return;
13238         }
13239         if (Roo.bootstrap.version == 3) {
13240             this.el.addClass(this.validClass);
13241         } else {
13242             this.inputEl().addClass('is-valid');
13243         }
13244
13245         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13246             
13247             var feedback = this.el.select('.form-control-feedback', true).first();
13248             
13249             if(feedback){
13250                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13251                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13252             }
13253             
13254         }
13255         
13256         this.fireEvent('valid', this);
13257     },
13258     
13259      /**
13260      * Mark this field as invalid
13261      * @param {String} msg The validation message
13262      */
13263     markInvalid : function(msg)
13264     {
13265         if(!this.el  || this.preventMark){ // not rendered
13266             return;
13267         }
13268         
13269         this.el.removeClass([this.invalidClass, this.validClass]);
13270         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13271         
13272         var feedback = this.el.select('.form-control-feedback', true).first();
13273             
13274         if(feedback){
13275             this.el.select('.form-control-feedback', true).first().removeClass(
13276                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13277         }
13278
13279         if(this.disabled){
13280             return;
13281         }
13282         
13283         if(this.allowBlank && !this.getRawValue().length){
13284             return;
13285         }
13286         
13287         if(this.indicator){
13288             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13289             this.indicator.addClass('visible');
13290         }
13291         if (Roo.bootstrap.version == 3) {
13292             this.el.addClass(this.invalidClass);
13293         } else {
13294             this.inputEl().addClass('is-invalid');
13295         }
13296         
13297         
13298         
13299         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13300             
13301             var feedback = this.el.select('.form-control-feedback', true).first();
13302             
13303             if(feedback){
13304                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13305                 
13306                 if(this.getValue().length || this.forceFeedback){
13307                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13308                 }
13309                 
13310             }
13311             
13312         }
13313         
13314         this.fireEvent('invalid', this, msg);
13315     },
13316     // private
13317     SafariOnKeyDown : function(event)
13318     {
13319         // this is a workaround for a password hang bug on chrome/ webkit.
13320         if (this.inputEl().dom.type != 'password') {
13321             return;
13322         }
13323         
13324         var isSelectAll = false;
13325         
13326         if(this.inputEl().dom.selectionEnd > 0){
13327             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13328         }
13329         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13330             event.preventDefault();
13331             this.setValue('');
13332             return;
13333         }
13334         
13335         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13336             
13337             event.preventDefault();
13338             // this is very hacky as keydown always get's upper case.
13339             //
13340             var cc = String.fromCharCode(event.getCharCode());
13341             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13342             
13343         }
13344     },
13345     adjustWidth : function(tag, w){
13346         tag = tag.toLowerCase();
13347         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13348             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13349                 if(tag == 'input'){
13350                     return w + 2;
13351                 }
13352                 if(tag == 'textarea'){
13353                     return w-2;
13354                 }
13355             }else if(Roo.isOpera){
13356                 if(tag == 'input'){
13357                     return w + 2;
13358                 }
13359                 if(tag == 'textarea'){
13360                     return w-2;
13361                 }
13362             }
13363         }
13364         return w;
13365     },
13366     
13367     setFieldLabel : function(v)
13368     {
13369         if(!this.rendered){
13370             return;
13371         }
13372         
13373         if(this.indicatorEl()){
13374             var ar = this.el.select('label > span',true);
13375             
13376             if (ar.elements.length) {
13377                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13378                 this.fieldLabel = v;
13379                 return;
13380             }
13381             
13382             var br = this.el.select('label',true);
13383             
13384             if(br.elements.length) {
13385                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13386                 this.fieldLabel = v;
13387                 return;
13388             }
13389             
13390             Roo.log('Cannot Found any of label > span || label in input');
13391             return;
13392         }
13393         
13394         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13395         this.fieldLabel = v;
13396         
13397         
13398     }
13399 });
13400
13401  
13402 /*
13403  * - LGPL
13404  *
13405  * Input
13406  * 
13407  */
13408
13409 /**
13410  * @class Roo.bootstrap.form.TextArea
13411  * @extends Roo.bootstrap.form.Input
13412  * Bootstrap TextArea class
13413  * @cfg {Number} cols Specifies the visible width of a text area
13414  * @cfg {Number} rows Specifies the visible number of lines in a text area
13415  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13416  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13417  * @cfg {string} html text
13418  * 
13419  * @constructor
13420  * Create a new TextArea
13421  * @param {Object} config The config object
13422  */
13423
13424 Roo.bootstrap.form.TextArea = function(config){
13425     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13426    
13427 };
13428
13429 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13430      
13431     cols : false,
13432     rows : 5,
13433     readOnly : false,
13434     warp : 'soft',
13435     resize : false,
13436     value: false,
13437     html: false,
13438     
13439     getAutoCreate : function(){
13440         
13441         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13442         
13443         var id = Roo.id();
13444         
13445         var cfg = {};
13446         
13447         if(this.inputType != 'hidden'){
13448             cfg.cls = 'form-group' //input-group
13449         }
13450         
13451         var input =  {
13452             tag: 'textarea',
13453             id : id,
13454             warp : this.warp,
13455             rows : this.rows,
13456             value : this.value || '',
13457             html: this.html || '',
13458             cls : 'form-control',
13459             placeholder : this.placeholder || '' 
13460             
13461         };
13462         
13463         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13464             input.maxLength = this.maxLength;
13465         }
13466         
13467         if(this.resize){
13468             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13469         }
13470         
13471         if(this.cols){
13472             input.cols = this.cols;
13473         }
13474         
13475         if (this.readOnly) {
13476             input.readonly = true;
13477         }
13478         
13479         if (this.name) {
13480             input.name = this.name;
13481         }
13482         
13483         if (this.size) {
13484             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13485         }
13486         
13487         var settings=this;
13488         ['xs','sm','md','lg'].map(function(size){
13489             if (settings[size]) {
13490                 cfg.cls += ' col-' + size + '-' + settings[size];
13491             }
13492         });
13493         
13494         var inputblock = input;
13495         
13496         if(this.hasFeedback && !this.allowBlank){
13497             
13498             var feedback = {
13499                 tag: 'span',
13500                 cls: 'glyphicon form-control-feedback'
13501             };
13502
13503             inputblock = {
13504                 cls : 'has-feedback',
13505                 cn :  [
13506                     input,
13507                     feedback
13508                 ] 
13509             };  
13510         }
13511         
13512         
13513         if (this.before || this.after) {
13514             
13515             inputblock = {
13516                 cls : 'input-group',
13517                 cn :  [] 
13518             };
13519             if (this.before) {
13520                 inputblock.cn.push({
13521                     tag :'span',
13522                     cls : 'input-group-addon',
13523                     html : this.before
13524                 });
13525             }
13526             
13527             inputblock.cn.push(input);
13528             
13529             if(this.hasFeedback && !this.allowBlank){
13530                 inputblock.cls += ' has-feedback';
13531                 inputblock.cn.push(feedback);
13532             }
13533             
13534             if (this.after) {
13535                 inputblock.cn.push({
13536                     tag :'span',
13537                     cls : 'input-group-addon',
13538                     html : this.after
13539                 });
13540             }
13541             
13542         }
13543         
13544         
13545         cfg = this.getAutoCreateLabel( cfg, inputblock );
13546
13547          
13548         
13549         if (this.disabled) {
13550             input.disabled=true;
13551         }
13552         
13553         return cfg;
13554         
13555     },
13556     /**
13557      * return the real textarea element.
13558      */
13559     inputEl: function ()
13560     {
13561         return this.el.select('textarea.form-control',true).first();
13562     },
13563     
13564     /**
13565      * Clear any invalid styles/messages for this field
13566      */
13567     clearInvalid : function()
13568     {
13569         
13570         if(!this.el || this.preventMark){ // not rendered
13571             return;
13572         }
13573         
13574         var label = this.el.select('label', true).first();
13575         //var icon = this.el.select('i.fa-star', true).first();
13576         
13577         //if(label && icon){
13578         //    icon.remove();
13579         //}
13580         this.el.removeClass( this.validClass);
13581         this.inputEl().removeClass('is-invalid');
13582          
13583         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13584             
13585             var feedback = this.el.select('.form-control-feedback', true).first();
13586             
13587             if(feedback){
13588                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13589             }
13590             
13591         }
13592         
13593         this.fireEvent('valid', this);
13594     },
13595     
13596      /**
13597      * Mark this field as valid
13598      */
13599     markValid : function()
13600     {
13601         if(!this.el  || this.preventMark){ // not rendered
13602             return;
13603         }
13604         
13605         this.el.removeClass([this.invalidClass, this.validClass]);
13606         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13607         
13608         var feedback = this.el.select('.form-control-feedback', true).first();
13609             
13610         if(feedback){
13611             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13612         }
13613
13614         if(this.disabled || this.allowBlank){
13615             return;
13616         }
13617         
13618         var label = this.el.select('label', true).first();
13619         var icon = this.el.select('i.fa-star', true).first();
13620         
13621         //if(label && icon){
13622         //    icon.remove();
13623         //}
13624         if (Roo.bootstrap.version == 3) {
13625             this.el.addClass(this.validClass);
13626         } else {
13627             this.inputEl().addClass('is-valid');
13628         }
13629         
13630         
13631         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13632             
13633             var feedback = this.el.select('.form-control-feedback', true).first();
13634             
13635             if(feedback){
13636                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13637                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13638             }
13639             
13640         }
13641         
13642         this.fireEvent('valid', this);
13643     },
13644     
13645      /**
13646      * Mark this field as invalid
13647      * @param {String} msg The validation message
13648      */
13649     markInvalid : function(msg)
13650     {
13651         if(!this.el  || this.preventMark){ // not rendered
13652             return;
13653         }
13654         
13655         this.el.removeClass([this.invalidClass, this.validClass]);
13656         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13657         
13658         var feedback = this.el.select('.form-control-feedback', true).first();
13659             
13660         if(feedback){
13661             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13662         }
13663
13664         if(this.disabled){
13665             return;
13666         }
13667         
13668         var label = this.el.select('label', true).first();
13669         //var icon = this.el.select('i.fa-star', true).first();
13670         
13671         //if(!this.getValue().length && label && !icon){
13672           /*  this.el.createChild({
13673                 tag : 'i',
13674                 cls : 'text-danger fa fa-lg fa-star',
13675                 tooltip : 'This field is required',
13676                 style : 'margin-right:5px;'
13677             }, label, true);
13678             */
13679         //}
13680         
13681         if (Roo.bootstrap.version == 3) {
13682             this.el.addClass(this.invalidClass);
13683         } else {
13684             this.inputEl().addClass('is-invalid');
13685         }
13686         
13687         // fixme ... this may be depricated need to test..
13688         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13689             
13690             var feedback = this.el.select('.form-control-feedback', true).first();
13691             
13692             if(feedback){
13693                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13694                 
13695                 if(this.getValue().length || this.forceFeedback){
13696                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13697                 }
13698                 
13699             }
13700             
13701         }
13702         
13703         this.fireEvent('invalid', this, msg);
13704     }
13705 });
13706
13707  
13708 /*
13709  * - LGPL
13710  *
13711  * trigger field - base class for combo..
13712  * 
13713  */
13714  
13715 /**
13716  * @class Roo.bootstrap.form.TriggerField
13717  * @extends Roo.bootstrap.form.Input
13718  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13719  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13720  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13721  * for which you can provide a custom implementation.  For example:
13722  * <pre><code>
13723 var trigger = new Roo.bootstrap.form.TriggerField();
13724 trigger.onTriggerClick = myTriggerFn;
13725 trigger.applyTo('my-field');
13726 </code></pre>
13727  *
13728  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13729  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13730  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13731  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13732  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13733
13734  * @constructor
13735  * Create a new TriggerField.
13736  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13737  * to the base TextField)
13738  */
13739 Roo.bootstrap.form.TriggerField = function(config){
13740     this.mimicing = false;
13741     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13742 };
13743
13744 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13745     /**
13746      * @cfg {String} triggerClass A CSS class to apply to the trigger
13747      */
13748      /**
13749      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13750      */
13751     hideTrigger:false,
13752
13753     /**
13754      * @cfg {Boolean} removable (true|false) special filter default false
13755      */
13756     removable : false,
13757     
13758     /** @cfg {Boolean} grow @hide */
13759     /** @cfg {Number} growMin @hide */
13760     /** @cfg {Number} growMax @hide */
13761
13762     /**
13763      * @hide 
13764      * @method
13765      */
13766     autoSize: Roo.emptyFn,
13767     // private
13768     monitorTab : true,
13769     // private
13770     deferHeight : true,
13771
13772     
13773     actionMode : 'wrap',
13774     
13775     caret : false,
13776     
13777     
13778     getAutoCreate : function(){
13779        
13780         var align = this.labelAlign || this.parentLabelAlign();
13781         
13782         var id = Roo.id();
13783         
13784         var cfg = {
13785             cls: 'form-group' //input-group
13786         };
13787         
13788         
13789         var input =  {
13790             tag: 'input',
13791             id : id,
13792             type : this.inputType,
13793             cls : 'form-control',
13794             autocomplete: 'new-password',
13795             placeholder : this.placeholder || '' 
13796             
13797         };
13798         if (this.name) {
13799             input.name = this.name;
13800         }
13801         if (this.size) {
13802             input.cls += ' input-' + this.size;
13803         }
13804         
13805         if (this.disabled) {
13806             input.disabled=true;
13807         }
13808         
13809         var inputblock = input;
13810         
13811         if(this.hasFeedback && !this.allowBlank){
13812             
13813             var feedback = {
13814                 tag: 'span',
13815                 cls: 'glyphicon form-control-feedback'
13816             };
13817             
13818             if(this.removable && !this.editable  ){
13819                 inputblock = {
13820                     cls : 'has-feedback',
13821                     cn :  [
13822                         inputblock,
13823                         {
13824                             tag: 'button',
13825                             html : 'x',
13826                             cls : 'roo-combo-removable-btn close'
13827                         },
13828                         feedback
13829                     ] 
13830                 };
13831             } else {
13832                 inputblock = {
13833                     cls : 'has-feedback',
13834                     cn :  [
13835                         inputblock,
13836                         feedback
13837                     ] 
13838                 };
13839             }
13840
13841         } else {
13842             if(this.removable && !this.editable ){
13843                 inputblock = {
13844                     cls : 'roo-removable',
13845                     cn :  [
13846                         inputblock,
13847                         {
13848                             tag: 'button',
13849                             html : 'x',
13850                             cls : 'roo-combo-removable-btn close'
13851                         }
13852                     ] 
13853                 };
13854             }
13855         }
13856         
13857         if (this.before || this.after) {
13858             
13859             inputblock = {
13860                 cls : 'input-group',
13861                 cn :  [] 
13862             };
13863             if (this.before) {
13864                 inputblock.cn.push({
13865                     tag :'span',
13866                     cls : 'input-group-addon input-group-prepend input-group-text',
13867                     html : this.before
13868                 });
13869             }
13870             
13871             inputblock.cn.push(input);
13872             
13873             if(this.hasFeedback && !this.allowBlank){
13874                 inputblock.cls += ' has-feedback';
13875                 inputblock.cn.push(feedback);
13876             }
13877             
13878             if (this.after) {
13879                 inputblock.cn.push({
13880                     tag :'span',
13881                     cls : 'input-group-addon input-group-append input-group-text',
13882                     html : this.after
13883                 });
13884             }
13885             
13886         };
13887         
13888       
13889         
13890         var ibwrap = inputblock;
13891         
13892         if(this.multiple){
13893             ibwrap = {
13894                 tag: 'ul',
13895                 cls: 'roo-select2-choices',
13896                 cn:[
13897                     {
13898                         tag: 'li',
13899                         cls: 'roo-select2-search-field',
13900                         cn: [
13901
13902                             inputblock
13903                         ]
13904                     }
13905                 ]
13906             };
13907                 
13908         }
13909         
13910         var combobox = {
13911             cls: 'roo-select2-container input-group',
13912             cn: [
13913                  {
13914                     tag: 'input',
13915                     type : 'hidden',
13916                     cls: 'form-hidden-field'
13917                 },
13918                 ibwrap
13919             ]
13920         };
13921         
13922         if(!this.multiple && this.showToggleBtn){
13923             
13924             var caret = {
13925                         tag: 'span',
13926                         cls: 'caret'
13927              };
13928             if (this.caret != false) {
13929                 caret = {
13930                      tag: 'i',
13931                      cls: 'fa fa-' + this.caret
13932                 };
13933                 
13934             }
13935             
13936             combobox.cn.push({
13937                 tag :'span',
13938                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13939                 cn : [
13940                     Roo.bootstrap.version == 3 ? caret : '',
13941                     {
13942                         tag: 'span',
13943                         cls: 'combobox-clear',
13944                         cn  : [
13945                             {
13946                                 tag : 'i',
13947                                 cls: 'icon-remove'
13948                             }
13949                         ]
13950                     }
13951                 ]
13952
13953             })
13954         }
13955         
13956         if(this.multiple){
13957             combobox.cls += ' roo-select2-container-multi';
13958         }
13959          var indicator = {
13960             tag : 'i',
13961             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13962             tooltip : 'This field is required'
13963         };
13964       
13965         if (this.allowBlank) {
13966             indicator = {
13967                 tag : 'i',
13968                 style : 'display:none'
13969             };
13970         }
13971          
13972         
13973         
13974         if (align ==='left' && this.fieldLabel.length) {
13975             
13976             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13977
13978             cfg.cn = [
13979                 indicator,
13980                 {
13981                     tag: 'label',
13982                     'for' :  id,
13983                     cls : 'control-label',
13984                     html : this.fieldLabel
13985
13986                 },
13987                 {
13988                     cls : "", 
13989                     cn: [
13990                         combobox
13991                     ]
13992                 }
13993
13994             ];
13995             
13996             var labelCfg = cfg.cn[1];
13997             var contentCfg = cfg.cn[2];
13998             
13999             if(this.indicatorpos == 'right'){
14000                 cfg.cn = [
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label',
14005                         cn : [
14006                             {
14007                                 tag : 'span',
14008                                 html : this.fieldLabel
14009                             },
14010                             indicator
14011                         ]
14012                     },
14013                     {
14014                         cls : "", 
14015                         cn: [
14016                             combobox
14017                         ]
14018                     }
14019
14020                 ];
14021                 
14022                 labelCfg = cfg.cn[0];
14023                 contentCfg = cfg.cn[1];
14024             }
14025             
14026             if(this.labelWidth > 12){
14027                 labelCfg.style = "width: " + this.labelWidth + 'px';
14028             }
14029             
14030             if(this.labelWidth < 13 && this.labelmd == 0){
14031                 this.labelmd = this.labelWidth;
14032             }
14033             
14034             if(this.labellg > 0){
14035                 labelCfg.cls += ' col-lg-' + this.labellg;
14036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14037             }
14038             
14039             if(this.labelmd > 0){
14040                 labelCfg.cls += ' col-md-' + this.labelmd;
14041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14042             }
14043             
14044             if(this.labelsm > 0){
14045                 labelCfg.cls += ' col-sm-' + this.labelsm;
14046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14047             }
14048             
14049             if(this.labelxs > 0){
14050                 labelCfg.cls += ' col-xs-' + this.labelxs;
14051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14052             }
14053             
14054         } else if ( this.fieldLabel.length) {
14055 //                Roo.log(" label");
14056             cfg.cn = [
14057                 indicator,
14058                {
14059                    tag: 'label',
14060                    //cls : 'input-group-addon',
14061                    html : this.fieldLabel
14062
14063                },
14064
14065                combobox
14066
14067             ];
14068             
14069             if(this.indicatorpos == 'right'){
14070                 
14071                 cfg.cn = [
14072                     {
14073                        tag: 'label',
14074                        cn : [
14075                            {
14076                                tag : 'span',
14077                                html : this.fieldLabel
14078                            },
14079                            indicator
14080                        ]
14081
14082                     },
14083                     combobox
14084
14085                 ];
14086
14087             }
14088
14089         } else {
14090             
14091 //                Roo.log(" no label && no align");
14092                 cfg = combobox
14093                      
14094                 
14095         }
14096         
14097         var settings=this;
14098         ['xs','sm','md','lg'].map(function(size){
14099             if (settings[size]) {
14100                 cfg.cls += ' col-' + size + '-' + settings[size];
14101             }
14102         });
14103         
14104         return cfg;
14105         
14106     },
14107     
14108     
14109     
14110     // private
14111     onResize : function(w, h){
14112 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 //        if(typeof w == 'number'){
14114 //            var x = w - this.trigger.getWidth();
14115 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14116 //            this.trigger.setStyle('left', x+'px');
14117 //        }
14118     },
14119
14120     // private
14121     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14122
14123     // private
14124     getResizeEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     getPositionEl : function(){
14130         return this.inputEl();
14131     },
14132
14133     // private
14134     alignErrorIcon : function(){
14135         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136     },
14137
14138     // private
14139     initEvents : function(){
14140         
14141         this.createList();
14142         
14143         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145         if(!this.multiple && this.showToggleBtn){
14146             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147             if(this.hideTrigger){
14148                 this.trigger.setDisplayed(false);
14149             }
14150             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151         }
14152         
14153         if(this.multiple){
14154             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14155         }
14156         
14157         if(this.removable && !this.editable && !this.tickable){
14158             var close = this.closeTriggerEl();
14159             
14160             if(close){
14161                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162                 close.on('click', this.removeBtnClick, this, close);
14163             }
14164         }
14165         
14166         //this.trigger.addClassOnOver('x-form-trigger-over');
14167         //this.trigger.addClassOnClick('x-form-trigger-click');
14168         
14169         //if(!this.width){
14170         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171         //}
14172     },
14173     
14174     closeTriggerEl : function()
14175     {
14176         var close = this.el.select('.roo-combo-removable-btn', true).first();
14177         return close ? close : false;
14178     },
14179     
14180     removeBtnClick : function(e, h, el)
14181     {
14182         e.preventDefault();
14183         
14184         if(this.fireEvent("remove", this) !== false){
14185             this.reset();
14186             this.fireEvent("afterremove", this)
14187         }
14188     },
14189     
14190     createList : function()
14191     {
14192         this.list = Roo.get(document.body).createChild({
14193             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194             cls: 'typeahead typeahead-long dropdown-menu shadow',
14195             style: 'display:none'
14196         });
14197         
14198         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14199         
14200     },
14201
14202     // private
14203     initTrigger : function(){
14204        
14205     },
14206
14207     // private
14208     onDestroy : function(){
14209         if(this.trigger){
14210             this.trigger.removeAllListeners();
14211           //  this.trigger.remove();
14212         }
14213         //if(this.wrap){
14214         //    this.wrap.remove();
14215         //}
14216         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217     },
14218
14219     // private
14220     onFocus : function(){
14221         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222         /*
14223         if(!this.mimicing){
14224             this.wrap.addClass('x-trigger-wrap-focus');
14225             this.mimicing = true;
14226             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227             if(this.monitorTab){
14228                 this.el.on("keydown", this.checkTab, this);
14229             }
14230         }
14231         */
14232     },
14233
14234     // private
14235     checkTab : function(e){
14236         if(e.getKey() == e.TAB){
14237             this.triggerBlur();
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         // do nothing
14244     },
14245
14246     // private
14247     mimicBlur : function(e, t){
14248         /*
14249         if(!this.wrap.contains(t) && this.validateBlur()){
14250             this.triggerBlur();
14251         }
14252         */
14253     },
14254
14255     // private
14256     triggerBlur : function(){
14257         this.mimicing = false;
14258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259         if(this.monitorTab){
14260             this.el.un("keydown", this.checkTab, this);
14261         }
14262         //this.wrap.removeClass('x-trigger-wrap-focus');
14263         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264     },
14265
14266     // private
14267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268     validateBlur : function(e, t){
14269         return true;
14270     },
14271
14272     // private
14273     onDisable : function(){
14274         this.inputEl().dom.disabled = true;
14275         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276         //if(this.wrap){
14277         //    this.wrap.addClass('x-item-disabled');
14278         //}
14279     },
14280
14281     // private
14282     onEnable : function(){
14283         this.inputEl().dom.disabled = false;
14284         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285         //if(this.wrap){
14286         //    this.el.removeClass('x-item-disabled');
14287         //}
14288     },
14289
14290     // private
14291     onShow : function(){
14292         var ae = this.getActionEl();
14293         
14294         if(ae){
14295             ae.dom.style.display = '';
14296             ae.dom.style.visibility = 'visible';
14297         }
14298     },
14299
14300     // private
14301     
14302     onHide : function(){
14303         var ae = this.getActionEl();
14304         ae.dom.style.display = 'none';
14305     },
14306
14307     /**
14308      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14309      * by an implementing function.
14310      * @method
14311      * @param {EventObject} e
14312      */
14313     onTriggerClick : Roo.emptyFn
14314 });
14315  
14316 /*
14317 * Licence: LGPL
14318 */
14319
14320 /**
14321  * @class Roo.bootstrap.form.CardUploader
14322  * @extends Roo.bootstrap.Button
14323  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324  * @cfg {Number} errorTimeout default 3000
14325  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14326  * @cfg {Array}  html The button text.
14327
14328  *
14329  * @constructor
14330  * Create a new CardUploader
14331  * @param {Object} config The config object
14332  */
14333
14334 Roo.bootstrap.form.CardUploader = function(config){
14335     
14336  
14337     
14338     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14339     
14340     
14341     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14342         return r.data.id
14343      });
14344     
14345      this.addEvents({
14346          // raw events
14347         /**
14348          * @event preview
14349          * When a image is clicked on - and needs to display a slideshow or similar..
14350          * @param {Roo.bootstrap.Card} this
14351          * @param {Object} The image information data 
14352          *
14353          */
14354         'preview' : true,
14355          /**
14356          * @event download
14357          * When a the download link is clicked
14358          * @param {Roo.bootstrap.Card} this
14359          * @param {Object} The image information data  contains 
14360          */
14361         'download' : true
14362         
14363     });
14364 };
14365  
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14367     
14368      
14369     errorTimeout : 3000,
14370      
14371     images : false,
14372    
14373     fileCollection : false,
14374     allowBlank : true,
14375     
14376     getAutoCreate : function()
14377     {
14378         
14379         var cfg =  {
14380             cls :'form-group' ,
14381             cn : [
14382                
14383                 {
14384                     tag: 'label',
14385                    //cls : 'input-group-addon',
14386                     html : this.fieldLabel
14387
14388                 },
14389
14390                 {
14391                     tag: 'input',
14392                     type : 'hidden',
14393                     name : this.name,
14394                     value : this.value,
14395                     cls : 'd-none  form-control'
14396                 },
14397                 
14398                 {
14399                     tag: 'input',
14400                     multiple : 'multiple',
14401                     type : 'file',
14402                     cls : 'd-none  roo-card-upload-selector'
14403                 },
14404                 
14405                 {
14406                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14407                 },
14408                 {
14409                     cls : 'card-columns roo-card-uploader-container'
14410                 }
14411
14412             ]
14413         };
14414            
14415          
14416         return cfg;
14417     },
14418     
14419     getChildContainer : function() /// what children are added to.
14420     {
14421         return this.containerEl;
14422     },
14423    
14424     getButtonContainer : function() /// what children are added to.
14425     {
14426         return this.el.select(".roo-card-uploader-button-container").first();
14427     },
14428    
14429     initEvents : function()
14430     {
14431         
14432         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433         
14434         var t = this;
14435         this.addxtype({
14436             xns: Roo.bootstrap,
14437
14438             xtype : 'Button',
14439             container_method : 'getButtonContainer' ,            
14440             html :  this.html, // fix changable?
14441             cls : 'w-100 ',
14442             listeners : {
14443                 'click' : function(btn, e) {
14444                     t.onClick(e);
14445                 }
14446             }
14447         });
14448         
14449         
14450         
14451         
14452         this.urlAPI = (window.createObjectURL && window) || 
14453                                 (window.URL && URL.revokeObjectURL && URL) || 
14454                                 (window.webkitURL && webkitURL);
14455                         
14456          
14457          
14458          
14459         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460         
14461         this.selectorEl.on('change', this.onFileSelected, this);
14462         if (this.images) {
14463             var t = this;
14464             this.images.forEach(function(img) {
14465                 t.addCard(img)
14466             });
14467             this.images = false;
14468         }
14469         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470          
14471        
14472     },
14473     
14474    
14475     onClick : function(e)
14476     {
14477         e.preventDefault();
14478          
14479         this.selectorEl.dom.click();
14480          
14481     },
14482     
14483     onFileSelected : function(e)
14484     {
14485         e.preventDefault();
14486         
14487         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488             return;
14489         }
14490         
14491         Roo.each(this.selectorEl.dom.files, function(file){    
14492             this.addFile(file);
14493         }, this);
14494          
14495     },
14496     
14497       
14498     
14499       
14500     
14501     addFile : function(file)
14502     {
14503            
14504         if(typeof(file) === 'string'){
14505             throw "Add file by name?"; // should not happen
14506             return;
14507         }
14508         
14509         if(!file || !this.urlAPI){
14510             return;
14511         }
14512         
14513         // file;
14514         // file.type;
14515         
14516         var _this = this;
14517         
14518         
14519         var url = _this.urlAPI.createObjectURL( file);
14520            
14521         this.addCard({
14522             id : Roo.bootstrap.form.CardUploader.ID--,
14523             is_uploaded : false,
14524             src : url,
14525             srcfile : file,
14526             title : file.name,
14527             mimetype : file.type,
14528             preview : false,
14529             is_deleted : 0
14530         });
14531         
14532     },
14533     
14534     /**
14535      * addCard - add an Attachment to the uploader
14536      * @param data - the data about the image to upload
14537      *
14538      * {
14539           id : 123
14540           title : "Title of file",
14541           is_uploaded : false,
14542           src : "http://.....",
14543           srcfile : { the File upload object },
14544           mimetype : file.type,
14545           preview : false,
14546           is_deleted : 0
14547           .. any other data...
14548         }
14549      *
14550      * 
14551     */
14552     
14553     addCard : function (data)
14554     {
14555         // hidden input element?
14556         // if the file is not an image...
14557         //then we need to use something other that and header_image
14558         var t = this;
14559         //   remove.....
14560         var footer = [
14561             {
14562                 xns : Roo.bootstrap,
14563                 xtype : 'CardFooter',
14564                  items: [
14565                     {
14566                         xns : Roo.bootstrap,
14567                         xtype : 'Element',
14568                         cls : 'd-flex',
14569                         items : [
14570                             
14571                             {
14572                                 xns : Roo.bootstrap,
14573                                 xtype : 'Button',
14574                                 html : String.format("<small>{0}</small>", data.title),
14575                                 cls : 'col-10 text-left',
14576                                 size: 'sm',
14577                                 weight: 'link',
14578                                 fa : 'download',
14579                                 listeners : {
14580                                     click : function() {
14581                                      
14582                                         t.fireEvent( "download", t, data );
14583                                     }
14584                                 }
14585                             },
14586                           
14587                             {
14588                                 xns : Roo.bootstrap,
14589                                 xtype : 'Button',
14590                                 style: 'max-height: 28px; ',
14591                                 size : 'sm',
14592                                 weight: 'danger',
14593                                 cls : 'col-2',
14594                                 fa : 'times',
14595                                 listeners : {
14596                                     click : function() {
14597                                         t.removeCard(data.id)
14598                                     }
14599                                 }
14600                             }
14601                         ]
14602                     }
14603                     
14604                 ] 
14605             }
14606             
14607         ];
14608         
14609         var cn = this.addxtype(
14610             {
14611                  
14612                 xns : Roo.bootstrap,
14613                 xtype : 'Card',
14614                 closeable : true,
14615                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14617                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14618                 data : data,
14619                 html : false,
14620                  
14621                 items : footer,
14622                 initEvents : function() {
14623                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14624                     var card = this;
14625                     this.imgEl = this.el.select('.card-img-top').first();
14626                     if (this.imgEl) {
14627                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628                         this.imgEl.set({ 'pointer' : 'cursor' });
14629                                   
14630                     }
14631                     this.getCardFooter().addClass('p-1');
14632                     
14633                   
14634                 }
14635                 
14636             }
14637         );
14638         // dont' really need ot update items.
14639         // this.items.push(cn);
14640         this.fileCollection.add(cn);
14641         
14642         if (!data.srcfile) {
14643             this.updateInput();
14644             return;
14645         }
14646             
14647         var _t = this;
14648         var reader = new FileReader();
14649         reader.addEventListener("load", function() {  
14650             data.srcdata =  reader.result;
14651             _t.updateInput();
14652         });
14653         reader.readAsDataURL(data.srcfile);
14654         
14655         
14656         
14657     },
14658     removeCard : function(id)
14659     {
14660         
14661         var card  = this.fileCollection.get(id);
14662         card.data.is_deleted = 1;
14663         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664         //this.fileCollection.remove(card);
14665         //this.items = this.items.filter(function(e) { return e != card });
14666         // dont' really need ot update items.
14667         card.el.dom.parentNode.removeChild(card.el.dom);
14668         this.updateInput();
14669
14670         
14671     },
14672     reset: function()
14673     {
14674         this.fileCollection.each(function(card) {
14675             if (card.el.dom && card.el.dom.parentNode) {
14676                 card.el.dom.parentNode.removeChild(card.el.dom);
14677             }
14678         });
14679         this.fileCollection.clear();
14680         this.updateInput();
14681     },
14682     
14683     updateInput : function()
14684     {
14685          var data = [];
14686         this.fileCollection.each(function(e) {
14687             data.push(e.data);
14688             
14689         });
14690         this.inputEl().dom.value = JSON.stringify(data);
14691         
14692         
14693         
14694     }
14695     
14696     
14697 });
14698
14699
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711
14712 /**
14713  * @class Roo.data.SortTypes
14714  * @static
14715  * Defines the default sorting (casting?) comparison functions used when sorting data.
14716  */
14717 Roo.data.SortTypes = {
14718     /**
14719      * Default sort that does nothing
14720      * @param {Mixed} s The value being converted
14721      * @return {Mixed} The comparison value
14722      */
14723     none : function(s){
14724         return s;
14725     },
14726     
14727     /**
14728      * The regular expression used to strip tags
14729      * @type {RegExp}
14730      * @property
14731      */
14732     stripTagsRE : /<\/?[^>]+>/gi,
14733     
14734     /**
14735      * Strips all HTML tags to sort on text only
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asText : function(s){
14740         return String(s).replace(this.stripTagsRE, "");
14741     },
14742     
14743     /**
14744      * Strips all HTML tags to sort on text only - Case insensitive
14745      * @param {Mixed} s The value being converted
14746      * @return {String} The comparison value
14747      */
14748     asUCText : function(s){
14749         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750     },
14751     
14752     /**
14753      * Case insensitive string
14754      * @param {Mixed} s The value being converted
14755      * @return {String} The comparison value
14756      */
14757     asUCString : function(s) {
14758         return String(s).toUpperCase();
14759     },
14760     
14761     /**
14762      * Date sorting
14763      * @param {Mixed} s The value being converted
14764      * @return {Number} The comparison value
14765      */
14766     asDate : function(s) {
14767         if(!s){
14768             return 0;
14769         }
14770         if(s instanceof Date){
14771             return s.getTime();
14772         }
14773         return Date.parse(String(s));
14774     },
14775     
14776     /**
14777      * Float sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Float} The comparison value
14780      */
14781     asFloat : function(s) {
14782         var val = parseFloat(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     },
14788     
14789     /**
14790      * Integer sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asInt : function(s) {
14795         var val = parseInt(String(s).replace(/,/g, ""));
14796         if(isNaN(val)) {
14797             val = 0;
14798         }
14799         return val;
14800     }
14801 };/*
14802  * Based on:
14803  * Ext JS Library 1.1.1
14804  * Copyright(c) 2006-2007, Ext JS, LLC.
14805  *
14806  * Originally Released Under LGPL - original licence link has changed is not relivant.
14807  *
14808  * Fork - LGPL
14809  * <script type="text/javascript">
14810  */
14811
14812 /**
14813 * @class Roo.data.Record
14814  * Instances of this class encapsulate both record <em>definition</em> information, and record
14815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816  * to access Records cached in an {@link Roo.data.Store} object.<br>
14817  * <p>
14818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14820  * objects.<br>
14821  * <p>
14822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823  * @constructor
14824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825  * {@link #create}. The parameters are the same.
14826  * @param {Array} data An associative Array of data values keyed by the field name.
14827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829  * not specified an integer id is generated.
14830  */
14831 Roo.data.Record = function(data, id){
14832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14833     this.data = data;
14834 };
14835
14836 /**
14837  * Generate a constructor for a specific record layout.
14838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840  * Each field definition object may contain the following properties: <ul>
14841  * <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,
14842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848  * this may be omitted.</p></li>
14849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850  * <ul><li>auto (Default, implies no conversion)</li>
14851  * <li>string</li>
14852  * <li>int</li>
14853  * <li>float</li>
14854  * <li>boolean</li>
14855  * <li>date</li></ul></p></li>
14856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859  * by the Reader into an object that will be stored in the Record. It is passed the
14860  * following parameters:<ul>
14861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862  * </ul></p></li>
14863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864  * </ul>
14865  * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867     {name: 'title', mapping: 'topic_title'},
14868     {name: 'author', mapping: 'username'},
14869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871     {name: 'lastPoster', mapping: 'user2'},
14872     {name: 'excerpt', mapping: 'post_text'}
14873 );
14874
14875 var myNewRecord = new TopicRecord({
14876     title: 'Do my job please',
14877     author: 'noobie',
14878     totalPosts: 1,
14879     lastPost: new Date(),
14880     lastPoster: 'Animal',
14881     excerpt: 'No way dude!'
14882 });
14883 myStore.add(myNewRecord);
14884 </code></pre>
14885  * @method create
14886  * @static
14887  */
14888 Roo.data.Record.create = function(o){
14889     var f = function(){
14890         f.superclass.constructor.apply(this, arguments);
14891     };
14892     Roo.extend(f, Roo.data.Record);
14893     var p = f.prototype;
14894     p.fields = new Roo.util.MixedCollection(false, function(field){
14895         return field.name;
14896     });
14897     for(var i = 0, len = o.length; i < len; i++){
14898         p.fields.add(new Roo.data.Field(o[i]));
14899     }
14900     f.getField = function(name){
14901         return p.fields.get(name);  
14902     };
14903     return f;
14904 };
14905
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14910
14911 Roo.data.Record.prototype = {
14912     /**
14913      * Readonly flag - true if this record has been modified.
14914      * @type Boolean
14915      */
14916     dirty : false,
14917     editing : false,
14918     error: null,
14919     modified: null,
14920
14921     // private
14922     join : function(store){
14923         this.store = store;
14924     },
14925
14926     /**
14927      * Set the named field to the specified value.
14928      * @param {String} name The name of the field to set.
14929      * @param {Object} value The value to set the field to.
14930      */
14931     set : function(name, value){
14932         if(this.data[name] == value){
14933             return;
14934         }
14935         this.dirty = true;
14936         if(!this.modified){
14937             this.modified = {};
14938         }
14939         if(typeof this.modified[name] == 'undefined'){
14940             this.modified[name] = this.data[name];
14941         }
14942         this.data[name] = value;
14943         if(!this.editing && this.store){
14944             this.store.afterEdit(this);
14945         }       
14946     },
14947
14948     /**
14949      * Get the value of the named field.
14950      * @param {String} name The name of the field to get the value of.
14951      * @return {Object} The value of the field.
14952      */
14953     get : function(name){
14954         return this.data[name]; 
14955     },
14956
14957     // private
14958     beginEdit : function(){
14959         this.editing = true;
14960         this.modified = {}; 
14961     },
14962
14963     // private
14964     cancelEdit : function(){
14965         this.editing = false;
14966         delete this.modified;
14967     },
14968
14969     // private
14970     endEdit : function(){
14971         this.editing = false;
14972         if(this.dirty && this.store){
14973             this.store.afterEdit(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Rejects all changes made to the Record since either creation, or the last commit operation.
14980      * Modified fields are reverted to their original values.
14981      * <p>
14982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983      * of reject operations.
14984      */
14985     reject : function(){
14986         var m = this.modified;
14987         for(var n in m){
14988             if(typeof m[n] != "function"){
14989                 this.data[n] = m[n];
14990             }
14991         }
14992         this.dirty = false;
14993         delete this.modified;
14994         this.editing = false;
14995         if(this.store){
14996             this.store.afterReject(this);
14997         }
14998     },
14999
15000     /**
15001      * Usually called by the {@link Roo.data.Store} which owns the Record.
15002      * Commits all changes made to the Record since either creation, or the last commit operation.
15003      * <p>
15004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005      * of commit operations.
15006      */
15007     commit : function(){
15008         this.dirty = false;
15009         delete this.modified;
15010         this.editing = false;
15011         if(this.store){
15012             this.store.afterCommit(this);
15013         }
15014     },
15015
15016     // private
15017     hasError : function(){
15018         return this.error != null;
15019     },
15020
15021     // private
15022     clearError : function(){
15023         this.error = null;
15024     },
15025
15026     /**
15027      * Creates a copy of this record.
15028      * @param {String} id (optional) A new record id if you don't want to use this record's id
15029      * @return {Record}
15030      */
15031     copy : function(newId) {
15032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033     }
15034 };/*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044
15045
15046
15047 /**
15048  * @class Roo.data.Store
15049  * @extends Roo.util.Observable
15050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052  * <p>
15053  * 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
15054  * has no knowledge of the format of the data returned by the Proxy.<br>
15055  * <p>
15056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057  * instances from the data object. These records are cached and made available through accessor functions.
15058  * @constructor
15059  * Creates a new Store.
15060  * @param {Object} config A config object containing the objects needed for the Store to access data,
15061  * and read the data into Records.
15062  */
15063 Roo.data.Store = function(config){
15064     this.data = new Roo.util.MixedCollection(false);
15065     this.data.getKey = function(o){
15066         return o.id;
15067     };
15068     this.baseParams = {};
15069     // private
15070     this.paramNames = {
15071         "start" : "start",
15072         "limit" : "limit",
15073         "sort" : "sort",
15074         "dir" : "dir",
15075         "multisort" : "_multisort"
15076     };
15077
15078     if(config && config.data){
15079         this.inlineData = config.data;
15080         delete config.data;
15081     }
15082
15083     Roo.apply(this, config);
15084     
15085     if(this.reader){ // reader passed
15086         this.reader = Roo.factory(this.reader, Roo.data);
15087         this.reader.xmodule = this.xmodule || false;
15088         if(!this.recordType){
15089             this.recordType = this.reader.recordType;
15090         }
15091         if(this.reader.onMetaChange){
15092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093         }
15094     }
15095
15096     if(this.recordType){
15097         this.fields = this.recordType.prototype.fields;
15098     }
15099     this.modified = [];
15100
15101     this.addEvents({
15102         /**
15103          * @event datachanged
15104          * Fires when the data cache has changed, and a widget which is using this Store
15105          * as a Record cache should refresh its view.
15106          * @param {Store} this
15107          */
15108         datachanged : true,
15109         /**
15110          * @event metachange
15111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112          * @param {Store} this
15113          * @param {Object} meta The JSON metadata
15114          */
15115         metachange : true,
15116         /**
15117          * @event add
15118          * Fires when Records have been added to the Store
15119          * @param {Store} this
15120          * @param {Roo.data.Record[]} records The array of Records added
15121          * @param {Number} index The index at which the record(s) were added
15122          */
15123         add : true,
15124         /**
15125          * @event remove
15126          * Fires when a Record has been removed from the Store
15127          * @param {Store} this
15128          * @param {Roo.data.Record} record The Record that was removed
15129          * @param {Number} index The index at which the record was removed
15130          */
15131         remove : true,
15132         /**
15133          * @event update
15134          * Fires when a Record has been updated
15135          * @param {Store} this
15136          * @param {Roo.data.Record} record The Record that was updated
15137          * @param {String} operation The update operation being performed.  Value may be one of:
15138          * <pre><code>
15139  Roo.data.Record.EDIT
15140  Roo.data.Record.REJECT
15141  Roo.data.Record.COMMIT
15142          * </code></pre>
15143          */
15144         update : true,
15145         /**
15146          * @event clear
15147          * Fires when the data cache has been cleared.
15148          * @param {Store} this
15149          */
15150         clear : true,
15151         /**
15152          * @event beforeload
15153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15154          * the load action will be canceled.
15155          * @param {Store} this
15156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157          */
15158         beforeload : true,
15159         /**
15160          * @event beforeloadadd
15161          * Fires after a new set of Records has been loaded.
15162          * @param {Store} this
15163          * @param {Roo.data.Record[]} records The Records that were loaded
15164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165          */
15166         beforeloadadd : true,
15167         /**
15168          * @event load
15169          * Fires after a new set of Records has been loaded, before they are added to the store.
15170          * @param {Store} this
15171          * @param {Roo.data.Record[]} records The Records that were loaded
15172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173          * @params {Object} return from reader
15174          */
15175         load : true,
15176         /**
15177          * @event loadexception
15178          * Fires if an exception occurs in the Proxy during loading.
15179          * Called with the signature of the Proxy's "loadexception" event.
15180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15181          * 
15182          * @param {Proxy} 
15183          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15184          * @param {Object} opts - load Options
15185          * @param {Object} jsonData from your request (normally this contains the Exception)
15186          */
15187         loadexception : true
15188     });
15189     
15190     if(this.proxy){
15191         this.proxy = Roo.factory(this.proxy, Roo.data);
15192         this.proxy.xmodule = this.xmodule || false;
15193         this.relayEvents(this.proxy,  ["loadexception"]);
15194     }
15195     this.sortToggle = {};
15196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197
15198     Roo.data.Store.superclass.constructor.call(this);
15199
15200     if(this.inlineData){
15201         this.loadData(this.inlineData);
15202         delete this.inlineData;
15203     }
15204 };
15205
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207      /**
15208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15209     * without a remote query - used by combo/forms at present.
15210     */
15211     
15212     /**
15213     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15214     */
15215     /**
15216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15217     */
15218     /**
15219     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15221     */
15222     /**
15223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224     * on any HTTP request
15225     */
15226     /**
15227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15228     */
15229     /**
15230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231     */
15232     multiSort: false,
15233     /**
15234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236     */
15237     remoteSort : false,
15238
15239     /**
15240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241      * loaded or when a record is removed. (defaults to false).
15242     */
15243     pruneModifiedRecords : false,
15244
15245     // private
15246     lastOptions : null,
15247
15248     /**
15249      * Add Records to the Store and fires the add event.
15250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251      */
15252     add : function(records){
15253         records = [].concat(records);
15254         for(var i = 0, len = records.length; i < len; i++){
15255             records[i].join(this);
15256         }
15257         var index = this.data.length;
15258         this.data.addAll(records);
15259         this.fireEvent("add", this, records, index);
15260     },
15261
15262     /**
15263      * Remove a Record from the Store and fires the remove event.
15264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265      */
15266     remove : function(record){
15267         var index = this.data.indexOf(record);
15268         this.data.removeAt(index);
15269  
15270         if(this.pruneModifiedRecords){
15271             this.modified.remove(record);
15272         }
15273         this.fireEvent("remove", this, record, index);
15274     },
15275
15276     /**
15277      * Remove all Records from the Store and fires the clear event.
15278      */
15279     removeAll : function(){
15280         this.data.clear();
15281         if(this.pruneModifiedRecords){
15282             this.modified = [];
15283         }
15284         this.fireEvent("clear", this);
15285     },
15286
15287     /**
15288      * Inserts Records to the Store at the given index and fires the add event.
15289      * @param {Number} index The start index at which to insert the passed Records.
15290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291      */
15292     insert : function(index, records){
15293         records = [].concat(records);
15294         for(var i = 0, len = records.length; i < len; i++){
15295             this.data.insert(index, records[i]);
15296             records[i].join(this);
15297         }
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Get the index within the cache of the passed Record.
15303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304      * @return {Number} The index of the passed Record. Returns -1 if not found.
15305      */
15306     indexOf : function(record){
15307         return this.data.indexOf(record);
15308     },
15309
15310     /**
15311      * Get the index within the cache of the Record with the passed id.
15312      * @param {String} id The id of the Record to find.
15313      * @return {Number} The index of the Record. Returns -1 if not found.
15314      */
15315     indexOfId : function(id){
15316         return this.data.indexOfKey(id);
15317     },
15318
15319     /**
15320      * Get the Record with the specified id.
15321      * @param {String} id The id of the Record to find.
15322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323      */
15324     getById : function(id){
15325         return this.data.key(id);
15326     },
15327
15328     /**
15329      * Get the Record at the specified index.
15330      * @param {Number} index The index of the Record to find.
15331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332      */
15333     getAt : function(index){
15334         return this.data.itemAt(index);
15335     },
15336
15337     /**
15338      * Returns a range of Records between specified indices.
15339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341      * @return {Roo.data.Record[]} An array of Records
15342      */
15343     getRange : function(start, end){
15344         return this.data.getRange(start, end);
15345     },
15346
15347     // private
15348     storeOptions : function(o){
15349         o = Roo.apply({}, o);
15350         delete o.callback;
15351         delete o.scope;
15352         this.lastOptions = o;
15353     },
15354
15355     /**
15356      * Loads the Record cache from the configured Proxy using the configured Reader.
15357      * <p>
15358      * If using remote paging, then the first load call must specify the <em>start</em>
15359      * and <em>limit</em> properties in the options.params property to establish the initial
15360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361      * <p>
15362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363      * and this call will return before the new data has been loaded. Perform any post-processing
15364      * in a callback function, or in a "load" event handler.</strong>
15365      * <p>
15366      * @param {Object} options An object containing properties which control loading options:<ul>
15367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15369      * <pre>
15370                 {
15371                     data : data,  // array of key=>value data like JsonReader
15372                     total : data.length,
15373                     success : true
15374                     
15375                 }
15376         </pre>
15377             }.</li>
15378      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379      * passed the following arguments:<ul>
15380      * <li>r : Roo.data.Record[]</li>
15381      * <li>options: Options object from the load call</li>
15382      * <li>success: Boolean success indicator</li></ul></li>
15383      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15385      * </ul>
15386      */
15387     load : function(options){
15388         options = options || {};
15389         if(this.fireEvent("beforeload", this, options) !== false){
15390             this.storeOptions(options);
15391             var p = Roo.apply(options.params || {}, this.baseParams);
15392             // if meta was not loaded from remote source.. try requesting it.
15393             if (!this.reader.metaFromRemote) {
15394                 p._requestMeta = 1;
15395             }
15396             if(this.sortInfo && this.remoteSort){
15397                 var pn = this.paramNames;
15398                 p[pn["sort"]] = this.sortInfo.field;
15399                 p[pn["dir"]] = this.sortInfo.direction;
15400             }
15401             if (this.multiSort) {
15402                 var pn = this.paramNames;
15403                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15404             }
15405             
15406             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15407         }
15408     },
15409
15410     /**
15411      * Reloads the Record cache from the configured Proxy using the configured Reader and
15412      * the options from the last load operation performed.
15413      * @param {Object} options (optional) An object containing properties which may override the options
15414      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415      * the most recently used options are reused).
15416      */
15417     reload : function(options){
15418         this.load(Roo.applyIf(options||{}, this.lastOptions));
15419     },
15420
15421     // private
15422     // Called as a callback by the Reader during a load operation.
15423     loadRecords : function(o, options, success){
15424          
15425         if(!o){
15426             if(success !== false){
15427                 this.fireEvent("load", this, [], options, o);
15428             }
15429             if(options.callback){
15430                 options.callback.call(options.scope || this, [], options, false);
15431             }
15432             return;
15433         }
15434         // if data returned failure - throw an exception.
15435         if (o.success === false) {
15436             // show a message if no listener is registered.
15437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439             }
15440             // loadmask wil be hooked into this..
15441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15442             return;
15443         }
15444         var r = o.records, t = o.totalRecords || r.length;
15445         
15446         this.fireEvent("beforeloadadd", this, r, options, o);
15447         
15448         if(!options || options.add !== true){
15449             if(this.pruneModifiedRecords){
15450                 this.modified = [];
15451             }
15452             for(var i = 0, len = r.length; i < len; i++){
15453                 r[i].join(this);
15454             }
15455             if(this.snapshot){
15456                 this.data = this.snapshot;
15457                 delete this.snapshot;
15458             }
15459             this.data.clear();
15460             this.data.addAll(r);
15461             this.totalLength = t;
15462             this.applySort();
15463             this.fireEvent("datachanged", this);
15464         }else{
15465             this.totalLength = Math.max(t, this.data.length+r.length);
15466             this.add(r);
15467         }
15468         
15469         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470                 
15471             var e = new Roo.data.Record({});
15472
15473             e.set(this.parent.displayField, this.parent.emptyTitle);
15474             e.set(this.parent.valueField, '');
15475
15476             this.insert(0, e);
15477         }
15478             
15479         this.fireEvent("load", this, r, options, o);
15480         if(options.callback){
15481             options.callback.call(options.scope || this, r, options, true);
15482         }
15483     },
15484
15485
15486     /**
15487      * Loads data from a passed data block. A Reader which understands the format of the data
15488      * must have been configured in the constructor.
15489      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15490      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492      */
15493     loadData : function(o, append){
15494         var r = this.reader.readRecords(o);
15495         this.loadRecords(r, {add: append}, true);
15496     },
15497     
15498      /**
15499      * using 'cn' the nested child reader read the child array into it's child stores.
15500      * @param {Object} rec The record with a 'children array
15501      */
15502     loadDataFromChildren : function(rec)
15503     {
15504         this.loadData(this.reader.toLoadData(rec));
15505     },
15506     
15507
15508     /**
15509      * Gets the number of cached records.
15510      * <p>
15511      * <em>If using paging, this may not be the total size of the dataset. If the data object
15512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513      * the data set size</em>
15514      */
15515     getCount : function(){
15516         return this.data.length || 0;
15517     },
15518
15519     /**
15520      * Gets the total number of records in the dataset as returned by the server.
15521      * <p>
15522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523      * the dataset size</em>
15524      */
15525     getTotalCount : function(){
15526         return this.totalLength || 0;
15527     },
15528
15529     /**
15530      * Returns the sort state of the Store as an object with two properties:
15531      * <pre><code>
15532  field {String} The name of the field by which the Records are sorted
15533  direction {String} The sort order, "ASC" or "DESC"
15534      * </code></pre>
15535      */
15536     getSortState : function(){
15537         return this.sortInfo;
15538     },
15539
15540     // private
15541     applySort : function(){
15542         if(this.sortInfo && !this.remoteSort){
15543             var s = this.sortInfo, f = s.field;
15544             var st = this.fields.get(f).sortType;
15545             var fn = function(r1, r2){
15546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548             };
15549             this.data.sort(s.direction, fn);
15550             if(this.snapshot && this.snapshot != this.data){
15551                 this.snapshot.sort(s.direction, fn);
15552             }
15553         }
15554     },
15555
15556     /**
15557      * Sets the default sort column and order to be used by the next load operation.
15558      * @param {String} fieldName The name of the field to sort by.
15559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560      */
15561     setDefaultSort : function(field, dir){
15562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563     },
15564
15565     /**
15566      * Sort the Records.
15567      * If remote sorting is used, the sort is performed on the server, and the cache is
15568      * reloaded. If local sorting is used, the cache is sorted internally.
15569      * @param {String} fieldName The name of the field to sort by.
15570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571      */
15572     sort : function(fieldName, dir){
15573         var f = this.fields.get(fieldName);
15574         if(!dir){
15575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576             
15577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15579             }else{
15580                 dir = f.sortDir;
15581             }
15582         }
15583         this.sortToggle[f.name] = dir;
15584         this.sortInfo = {field: f.name, direction: dir};
15585         if(!this.remoteSort){
15586             this.applySort();
15587             this.fireEvent("datachanged", this);
15588         }else{
15589             this.load(this.lastOptions);
15590         }
15591     },
15592
15593     /**
15594      * Calls the specified function for each of the Records in the cache.
15595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596      * Returning <em>false</em> aborts and exits the iteration.
15597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598      */
15599     each : function(fn, scope){
15600         this.data.each(fn, scope);
15601     },
15602
15603     /**
15604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15605      * (e.g., during paging).
15606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607      */
15608     getModifiedRecords : function(){
15609         return this.modified;
15610     },
15611
15612     // private
15613     createFilterFn : function(property, value, anyMatch){
15614         if(!value.exec){ // not a regex
15615             value = String(value);
15616             if(value.length == 0){
15617                 return false;
15618             }
15619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620         }
15621         return function(r){
15622             return value.test(r.data[property]);
15623         };
15624     },
15625
15626     /**
15627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628      * @param {String} property A field on your records
15629      * @param {Number} start The record index to start at (defaults to 0)
15630      * @param {Number} end The last record index to include (defaults to length - 1)
15631      * @return {Number} The sum
15632      */
15633     sum : function(property, start, end){
15634         var rs = this.data.items, v = 0;
15635         start = start || 0;
15636         end = (end || end === 0) ? end : rs.length-1;
15637
15638         for(var i = start; i <= end; i++){
15639             v += (rs[i].data[property] || 0);
15640         }
15641         return v;
15642     },
15643
15644     /**
15645      * Filter the records by a specified property.
15646      * @param {String} field A field on your records
15647      * @param {String/RegExp} value Either a string that the field
15648      * should start with or a RegExp to test against the field
15649      * @param {Boolean} anyMatch True to match any part not just the beginning
15650      */
15651     filter : function(property, value, anyMatch){
15652         var fn = this.createFilterFn(property, value, anyMatch);
15653         return fn ? this.filterBy(fn) : this.clearFilter();
15654     },
15655
15656     /**
15657      * Filter by a function. The specified function will be called with each
15658      * record in this data source. If the function returns true the record is included,
15659      * otherwise it is filtered.
15660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661      * @param {Object} scope (optional) The scope of the function (defaults to this)
15662      */
15663     filterBy : function(fn, scope){
15664         this.snapshot = this.snapshot || this.data;
15665         this.data = this.queryBy(fn, scope||this);
15666         this.fireEvent("datachanged", this);
15667     },
15668
15669     /**
15670      * Query the records by a specified property.
15671      * @param {String} field A field on your records
15672      * @param {String/RegExp} value Either a string that the field
15673      * should start with or a RegExp to test against the field
15674      * @param {Boolean} anyMatch True to match any part not just the beginning
15675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676      */
15677     query : function(property, value, anyMatch){
15678         var fn = this.createFilterFn(property, value, anyMatch);
15679         return fn ? this.queryBy(fn) : this.data.clone();
15680     },
15681
15682     /**
15683      * Query by a function. The specified function will be called with each
15684      * record in this data source. If the function returns true the record is included
15685      * in the results.
15686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687      * @param {Object} scope (optional) The scope of the function (defaults to this)
15688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689      **/
15690     queryBy : function(fn, scope){
15691         var data = this.snapshot || this.data;
15692         return data.filterBy(fn, scope||this);
15693     },
15694
15695     /**
15696      * Collects unique values for a particular dataIndex from this store.
15697      * @param {String} dataIndex The property to collect
15698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700      * @return {Array} An array of the unique values
15701      **/
15702     collect : function(dataIndex, allowNull, bypassFilter){
15703         var d = (bypassFilter === true && this.snapshot) ?
15704                 this.snapshot.items : this.data.items;
15705         var v, sv, r = [], l = {};
15706         for(var i = 0, len = d.length; i < len; i++){
15707             v = d[i].data[dataIndex];
15708             sv = String(v);
15709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710                 l[sv] = true;
15711                 r[r.length] = v;
15712             }
15713         }
15714         return r;
15715     },
15716
15717     /**
15718      * Revert to a view of the Record cache with no filtering applied.
15719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720      */
15721     clearFilter : function(suppressEvent){
15722         if(this.snapshot && this.snapshot != this.data){
15723             this.data = this.snapshot;
15724             delete this.snapshot;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("datachanged", this);
15727             }
15728         }
15729     },
15730
15731     // private
15732     afterEdit : function(record){
15733         if(this.modified.indexOf(record) == -1){
15734             this.modified.push(record);
15735         }
15736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737     },
15738     
15739     // private
15740     afterReject : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743     },
15744
15745     // private
15746     afterCommit : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749     },
15750
15751     /**
15752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754      */
15755     commitChanges : function(){
15756         var m = this.modified.slice(0);
15757         this.modified = [];
15758         for(var i = 0, len = m.length; i < len; i++){
15759             m[i].commit();
15760         }
15761     },
15762
15763     /**
15764      * Cancel outstanding changes on all changed records.
15765      */
15766     rejectChanges : function(){
15767         var m = this.modified.slice(0);
15768         this.modified = [];
15769         for(var i = 0, len = m.length; i < len; i++){
15770             m[i].reject();
15771         }
15772     },
15773
15774     onMetaChange : function(meta, rtype, o){
15775         this.recordType = rtype;
15776         this.fields = rtype.prototype.fields;
15777         delete this.snapshot;
15778         this.sortInfo = meta.sortInfo || this.sortInfo;
15779         this.modified = [];
15780         this.fireEvent('metachange', this, this.reader.meta);
15781     },
15782     
15783     moveIndex : function(data, type)
15784     {
15785         var index = this.indexOf(data);
15786         
15787         var newIndex = index + type;
15788         
15789         this.remove(data);
15790         
15791         this.insert(newIndex, data);
15792         
15793     }
15794 });/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806  * @class Roo.data.SimpleStore
15807  * @extends Roo.data.Store
15808  * Small helper class to make creating Stores from Array data easier.
15809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810  * @cfg {Array} fields An array of field definition objects, or field name strings.
15811  * @cfg {Object} an existing reader (eg. copied from another store)
15812  * @cfg {Array} data The multi-dimensional array of data
15813  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15814  * @cfg {Roo.data.Reader} reader  [not-required] 
15815  * @constructor
15816  * @param {Object} config
15817  */
15818 Roo.data.SimpleStore = function(config)
15819 {
15820     Roo.data.SimpleStore.superclass.constructor.call(this, {
15821         isLocal : true,
15822         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15823                 id: config.id
15824             },
15825             Roo.data.Record.create(config.fields)
15826         ),
15827         proxy : new Roo.data.MemoryProxy(config.data)
15828     });
15829     this.load();
15830 };
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843 /**
15844  * @extends Roo.data.Store
15845  * @class Roo.data.JsonStore
15846  * Small helper class to make creating Stores for JSON data easier. <br/>
15847 <pre><code>
15848 var store = new Roo.data.JsonStore({
15849     url: 'get-images.php',
15850     root: 'images',
15851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15852 });
15853 </code></pre>
15854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855  * JsonReader and HttpProxy (unless inline data is provided).</b>
15856  * @cfg {Array} fields An array of field definition objects, or field name strings.
15857  * @constructor
15858  * @param {Object} config
15859  */
15860 Roo.data.JsonStore = function(c){
15861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863         reader: new Roo.data.JsonReader(c, c.fields)
15864     }));
15865 };
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877  
15878 Roo.data.Field = function(config){
15879     if(typeof config == "string"){
15880         config = {name: config};
15881     }
15882     Roo.apply(this, config);
15883     
15884     if(!this.type){
15885         this.type = "auto";
15886     }
15887     
15888     var st = Roo.data.SortTypes;
15889     // named sortTypes are supported, here we look them up
15890     if(typeof this.sortType == "string"){
15891         this.sortType = st[this.sortType];
15892     }
15893     
15894     // set default sortType for strings and dates
15895     if(!this.sortType){
15896         switch(this.type){
15897             case "string":
15898                 this.sortType = st.asUCString;
15899                 break;
15900             case "date":
15901                 this.sortType = st.asDate;
15902                 break;
15903             default:
15904                 this.sortType = st.none;
15905         }
15906     }
15907
15908     // define once
15909     var stripRe = /[\$,%]/g;
15910
15911     // prebuilt conversion function for this field, instead of
15912     // switching every time we're reading a value
15913     if(!this.convert){
15914         var cv, dateFormat = this.dateFormat;
15915         switch(this.type){
15916             case "":
15917             case "auto":
15918             case undefined:
15919                 cv = function(v){ return v; };
15920                 break;
15921             case "string":
15922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923                 break;
15924             case "int":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15928                     };
15929                 break;
15930             case "float":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15934                     };
15935                 break;
15936             case "bool":
15937             case "boolean":
15938                 cv = function(v){ return v === true || v === "true" || v == 1; };
15939                 break;
15940             case "date":
15941                 cv = function(v){
15942                     if(!v){
15943                         return '';
15944                     }
15945                     if(v instanceof Date){
15946                         return v;
15947                     }
15948                     if(dateFormat){
15949                         if(dateFormat == "timestamp"){
15950                             return new Date(v*1000);
15951                         }
15952                         return Date.parseDate(v, dateFormat);
15953                     }
15954                     var parsed = Date.parse(v);
15955                     return parsed ? new Date(parsed) : null;
15956                 };
15957              break;
15958             
15959         }
15960         this.convert = cv;
15961     }
15962 };
15963
15964 Roo.data.Field.prototype = {
15965     dateFormat: null,
15966     defaultValue: "",
15967     mapping: null,
15968     sortType : null,
15969     sortDir : "ASC"
15970 };/*
15971  * Based on:
15972  * Ext JS Library 1.1.1
15973  * Copyright(c) 2006-2007, Ext JS, LLC.
15974  *
15975  * Originally Released Under LGPL - original licence link has changed is not relivant.
15976  *
15977  * Fork - LGPL
15978  * <script type="text/javascript">
15979  */
15980  
15981 // Base class for reading structured data from a data source.  This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15983
15984 /**
15985  * @class Roo.data.DataReader
15986  * @abstract
15987  * Base class for reading structured data from a data source.  This class is intended to be
15988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15989  */
15990
15991 Roo.data.DataReader = function(meta, recordType){
15992     
15993     this.meta = meta;
15994     
15995     this.recordType = recordType instanceof Array ? 
15996         Roo.data.Record.create(recordType) : recordType;
15997 };
15998
15999 Roo.data.DataReader.prototype = {
16000     
16001     
16002     readerType : 'Data',
16003      /**
16004      * Create an empty record
16005      * @param {Object} data (optional) - overlay some values
16006      * @return {Roo.data.Record} record created.
16007      */
16008     newRow :  function(d) {
16009         var da =  {};
16010         this.recordType.prototype.fields.each(function(c) {
16011             switch( c.type) {
16012                 case 'int' : da[c.name] = 0; break;
16013                 case 'date' : da[c.name] = new Date(); break;
16014                 case 'float' : da[c.name] = 0.0; break;
16015                 case 'boolean' : da[c.name] = false; break;
16016                 default : da[c.name] = ""; break;
16017             }
16018             
16019         });
16020         return new this.recordType(Roo.apply(da, d));
16021     }
16022     
16023     
16024 };/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.DataProxy
16037  * @extends Roo.util.Observable
16038  * @abstract
16039  * This class is an abstract base class for implementations which provide retrieval of
16040  * unformatted data objects.<br>
16041  * <p>
16042  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043  * (of the appropriate type which knows how to parse the data object) to provide a block of
16044  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045  * <p>
16046  * Custom implementations must implement the load method as described in
16047  * {@link Roo.data.HttpProxy#load}.
16048  */
16049 Roo.data.DataProxy = function(){
16050     this.addEvents({
16051         /**
16052          * @event beforeload
16053          * Fires before a network request is made to retrieve a data object.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} params The params parameter to the load function.
16056          */
16057         beforeload : true,
16058         /**
16059          * @event load
16060          * Fires before the load method's callback is called.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} o The data object.
16063          * @param {Object} arg The callback argument object passed to the load function.
16064          */
16065         load : true,
16066         /**
16067          * @event loadexception
16068          * Fires if an Exception occurs during data retrieval.
16069          * @param {Object} This DataProxy object.
16070          * @param {Object} o The data object.
16071          * @param {Object} arg The callback argument object passed to the load function.
16072          * @param {Object} e The Exception.
16073          */
16074         loadexception : true
16075     });
16076     Roo.data.DataProxy.superclass.constructor.call(this);
16077 };
16078
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16080
16081     /**
16082      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083      */
16084 /*
16085  * Based on:
16086  * Ext JS Library 1.1.1
16087  * Copyright(c) 2006-2007, Ext JS, LLC.
16088  *
16089  * Originally Released Under LGPL - original licence link has changed is not relivant.
16090  *
16091  * Fork - LGPL
16092  * <script type="text/javascript">
16093  */
16094 /**
16095  * @class Roo.data.MemoryProxy
16096  * @extends Roo.data.DataProxy
16097  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098  * to the Reader when its load method is called.
16099  * @constructor
16100  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16101  */
16102 Roo.data.MemoryProxy = function(config){
16103     var data = config;
16104     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105         data = config.data;
16106     }
16107     Roo.data.MemoryProxy.superclass.constructor.call(this);
16108     this.data = data;
16109 };
16110
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16112     
16113     /**
16114      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16115      */
16116     /**
16117      * Load data from the requested source (in this case an in-memory
16118      * data object passed to the constructor), read the data object into
16119      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120      * process that block using the passed callback.
16121      * @param {Object} params This parameter is not used by the MemoryProxy class.
16122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123      * object into a block of Roo.data.Records.
16124      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125      * The function must be passed <ul>
16126      * <li>The Record block object</li>
16127      * <li>The "arg" argument from the load function</li>
16128      * <li>A boolean success indicator</li>
16129      * </ul>
16130      * @param {Object} scope The scope in which to call the callback
16131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132      */
16133     load : function(params, reader, callback, scope, arg){
16134         params = params || {};
16135         var result;
16136         try {
16137             result = reader.readRecords(params.data ? params.data :this.data);
16138         }catch(e){
16139             this.fireEvent("loadexception", this, arg, null, e);
16140             callback.call(scope, null, arg, false);
16141             return;
16142         }
16143         callback.call(scope, result, arg, true);
16144     },
16145     
16146     // private
16147     update : function(params, records){
16148         
16149     }
16150 });/*
16151  * Based on:
16152  * Ext JS Library 1.1.1
16153  * Copyright(c) 2006-2007, Ext JS, LLC.
16154  *
16155  * Originally Released Under LGPL - original licence link has changed is not relivant.
16156  *
16157  * Fork - LGPL
16158  * <script type="text/javascript">
16159  */
16160 /**
16161  * @class Roo.data.HttpProxy
16162  * @extends Roo.data.DataProxy
16163  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164  * configured to reference a certain URL.<br><br>
16165  * <p>
16166  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167  * from which the running page was served.<br><br>
16168  * <p>
16169  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170  * <p>
16171  * Be aware that to enable the browser to parse an XML document, the server must set
16172  * the Content-Type header in the HTTP response to "text/xml".
16173  * @constructor
16174  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176  * will be used to make the request.
16177  */
16178 Roo.data.HttpProxy = function(conn){
16179     Roo.data.HttpProxy.superclass.constructor.call(this);
16180     // is conn a conn config or a real conn?
16181     this.conn = conn;
16182     this.useAjax = !conn || !conn.events;
16183   
16184 };
16185
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187     // thse are take from connection...
16188     
16189     /**
16190      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16191      */
16192     /**
16193      * @cfg {Object} extraParams  An object containing properties which are used as
16194      * extra parameters to each request made by this object. (defaults to undefined)
16195      */
16196     /**
16197      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16198      *  to each request made by this object. (defaults to undefined)
16199      */
16200     /**
16201      * @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)
16202      */
16203     /**
16204      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16205      */
16206      /**
16207      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16208      * @type Boolean
16209      */
16210   
16211
16212     /**
16213      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214      * @type Boolean
16215      */
16216     /**
16217      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219      * a finer-grained basis than the DataProxy events.
16220      */
16221     getConnection : function(){
16222         return this.useAjax ? Roo.Ajax : this.conn;
16223     },
16224
16225     /**
16226      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228      * process that block using the passed callback.
16229      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230      * for the request to the remote server.
16231      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232      * object into a block of Roo.data.Records.
16233      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234      * The function must be passed <ul>
16235      * <li>The Record block object</li>
16236      * <li>The "arg" argument from the load function</li>
16237      * <li>A boolean success indicator</li>
16238      * </ul>
16239      * @param {Object} scope The scope in which to call the callback
16240      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241      */
16242     load : function(params, reader, callback, scope, arg){
16243         if(this.fireEvent("beforeload", this, params) !== false){
16244             var  o = {
16245                 params : params || {},
16246                 request: {
16247                     callback : callback,
16248                     scope : scope,
16249                     arg : arg
16250                 },
16251                 reader: reader,
16252                 callback : this.loadResponse,
16253                 scope: this
16254             };
16255             if(this.useAjax){
16256                 Roo.applyIf(o, this.conn);
16257                 if(this.activeRequest){
16258                     Roo.Ajax.abort(this.activeRequest);
16259                 }
16260                 this.activeRequest = Roo.Ajax.request(o);
16261             }else{
16262                 this.conn.request(o);
16263             }
16264         }else{
16265             callback.call(scope||this, null, arg, false);
16266         }
16267     },
16268
16269     // private
16270     loadResponse : function(o, success, response){
16271         delete this.activeRequest;
16272         if(!success){
16273             this.fireEvent("loadexception", this, o, response);
16274             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16275             return;
16276         }
16277         var result;
16278         try {
16279             result = o.reader.read(response);
16280         }catch(e){
16281             o.success = false;
16282             o.raw = { errorMsg : response.responseText };
16283             this.fireEvent("loadexception", this, o, response, e);
16284             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285             return;
16286         }
16287         
16288         this.fireEvent("load", this, o, o.request.arg);
16289         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290     },
16291
16292     // private
16293     update : function(dataSet){
16294
16295     },
16296
16297     // private
16298     updateResponse : function(dataSet){
16299
16300     }
16301 });/*
16302  * Based on:
16303  * Ext JS Library 1.1.1
16304  * Copyright(c) 2006-2007, Ext JS, LLC.
16305  *
16306  * Originally Released Under LGPL - original licence link has changed is not relivant.
16307  *
16308  * Fork - LGPL
16309  * <script type="text/javascript">
16310  */
16311
16312 /**
16313  * @class Roo.data.ScriptTagProxy
16314  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315  * other than the originating domain of the running page.<br><br>
16316  * <p>
16317  * <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
16318  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319  * <p>
16320  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321  * source code that is used as the source inside a &lt;script> tag.<br><br>
16322  * <p>
16323  * In order for the browser to process the returned data, the server must wrap the data object
16324  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326  * depending on whether the callback name was passed:
16327  * <p>
16328  * <pre><code>
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16331 if (cb != null) {
16332     scriptTag = true;
16333     response.setContentType("text/javascript");
16334 } else {
16335     response.setContentType("application/x-json");
16336 }
16337 Writer out = response.getWriter();
16338 if (scriptTag) {
16339     out.write(cb + "(");
16340 }
16341 out.print(dataBlock.toJsonString());
16342 if (scriptTag) {
16343     out.write(");");
16344 }
16345 </pre></code>
16346  *
16347  * @constructor
16348  * @param {Object} config A configuration object.
16349  */
16350 Roo.data.ScriptTagProxy = function(config){
16351     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352     Roo.apply(this, config);
16353     this.head = document.getElementsByTagName("head")[0];
16354 };
16355
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359     /**
16360      * @cfg {String} url The URL from which to request the data object.
16361      */
16362     /**
16363      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364      */
16365     timeout : 30000,
16366     /**
16367      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368      * the server the name of the callback function set up by the load call to process the returned data object.
16369      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370      * javascript output which calls this named function passing the data object as its only parameter.
16371      */
16372     callbackParam : "callback",
16373     /**
16374      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375      * name to the request.
16376      */
16377     nocache : true,
16378
16379     /**
16380      * Load data from the configured URL, read the data object into
16381      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382      * process that block using the passed callback.
16383      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384      * for the request to the remote server.
16385      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386      * object into a block of Roo.data.Records.
16387      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388      * The function must be passed <ul>
16389      * <li>The Record block object</li>
16390      * <li>The "arg" argument from the load function</li>
16391      * <li>A boolean success indicator</li>
16392      * </ul>
16393      * @param {Object} scope The scope in which to call the callback
16394      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395      */
16396     load : function(params, reader, callback, scope, arg){
16397         if(this.fireEvent("beforeload", this, params) !== false){
16398
16399             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400
16401             var url = this.url;
16402             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403             if(this.nocache){
16404                 url += "&_dc=" + (new Date().getTime());
16405             }
16406             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16407             var trans = {
16408                 id : transId,
16409                 cb : "stcCallback"+transId,
16410                 scriptId : "stcScript"+transId,
16411                 params : params,
16412                 arg : arg,
16413                 url : url,
16414                 callback : callback,
16415                 scope : scope,
16416                 reader : reader
16417             };
16418             var conn = this;
16419
16420             window[trans.cb] = function(o){
16421                 conn.handleResponse(o, trans);
16422             };
16423
16424             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425
16426             if(this.autoAbort !== false){
16427                 this.abort();
16428             }
16429
16430             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431
16432             var script = document.createElement("script");
16433             script.setAttribute("src", url);
16434             script.setAttribute("type", "text/javascript");
16435             script.setAttribute("id", trans.scriptId);
16436             this.head.appendChild(script);
16437
16438             this.trans = trans;
16439         }else{
16440             callback.call(scope||this, null, arg, false);
16441         }
16442     },
16443
16444     // private
16445     isLoading : function(){
16446         return this.trans ? true : false;
16447     },
16448
16449     /**
16450      * Abort the current server request.
16451      */
16452     abort : function(){
16453         if(this.isLoading()){
16454             this.destroyTrans(this.trans);
16455         }
16456     },
16457
16458     // private
16459     destroyTrans : function(trans, isLoaded){
16460         this.head.removeChild(document.getElementById(trans.scriptId));
16461         clearTimeout(trans.timeoutId);
16462         if(isLoaded){
16463             window[trans.cb] = undefined;
16464             try{
16465                 delete window[trans.cb];
16466             }catch(e){}
16467         }else{
16468             // if hasn't been loaded, wait for load to remove it to prevent script error
16469             window[trans.cb] = function(){
16470                 window[trans.cb] = undefined;
16471                 try{
16472                     delete window[trans.cb];
16473                 }catch(e){}
16474             };
16475         }
16476     },
16477
16478     // private
16479     handleResponse : function(o, trans){
16480         this.trans = false;
16481         this.destroyTrans(trans, true);
16482         var result;
16483         try {
16484             result = trans.reader.readRecords(o);
16485         }catch(e){
16486             this.fireEvent("loadexception", this, o, trans.arg, e);
16487             trans.callback.call(trans.scope||window, null, trans.arg, false);
16488             return;
16489         }
16490         this.fireEvent("load", this, o, trans.arg);
16491         trans.callback.call(trans.scope||window, result, trans.arg, true);
16492     },
16493
16494     // private
16495     handleFailure : function(trans){
16496         this.trans = false;
16497         this.destroyTrans(trans, false);
16498         this.fireEvent("loadexception", this, null, trans.arg);
16499         trans.callback.call(trans.scope||window, null, trans.arg, false);
16500     }
16501 });/*
16502  * Based on:
16503  * Ext JS Library 1.1.1
16504  * Copyright(c) 2006-2007, Ext JS, LLC.
16505  *
16506  * Originally Released Under LGPL - original licence link has changed is not relivant.
16507  *
16508  * Fork - LGPL
16509  * <script type="text/javascript">
16510  */
16511
16512 /**
16513  * @class Roo.data.JsonReader
16514  * @extends Roo.data.DataReader
16515  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516  * based on mappings in a provided Roo.data.Record constructor.
16517  * 
16518  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519  * in the reply previously. 
16520  * 
16521  * <p>
16522  * Example code:
16523  * <pre><code>
16524 var RecordDef = Roo.data.Record.create([
16525     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16526     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16527 ]);
16528 var myReader = new Roo.data.JsonReader({
16529     totalProperty: "results",    // The property which contains the total dataset size (optional)
16530     root: "rows",                // The property which contains an Array of row objects
16531     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16532 }, RecordDef);
16533 </code></pre>
16534  * <p>
16535  * This would consume a JSON file like this:
16536  * <pre><code>
16537 { 'results': 2, 'rows': [
16538     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16540 }
16541 </code></pre>
16542  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544  * paged from the remote server.
16545  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546  * @cfg {String} root name of the property which contains the Array of row objects.
16547  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548  * @cfg {Array} fields Array of field definition objects
16549  * @constructor
16550  * Create a new JsonReader
16551  * @param {Object} meta Metadata configuration options
16552  * @param {Object} recordType Either an Array of field definition objects,
16553  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554  */
16555 Roo.data.JsonReader = function(meta, recordType){
16556     
16557     meta = meta || {};
16558     // set some defaults:
16559     Roo.applyIf(meta, {
16560         totalProperty: 'total',
16561         successProperty : 'success',
16562         root : 'data',
16563         id : 'id'
16564     });
16565     
16566     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 };
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569     
16570     readerType : 'Json',
16571     
16572     /**
16573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16574      * Used by Store query builder to append _requestMeta to params.
16575      * 
16576      */
16577     metaFromRemote : false,
16578     /**
16579      * This method is only used by a DataProxy which has retrieved data from a remote server.
16580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581      * @return {Object} data A data block which is used by an Roo.data.Store object as
16582      * a cache of Roo.data.Records.
16583      */
16584     read : function(response){
16585         var json = response.responseText;
16586        
16587         var o = /* eval:var:o */ eval("("+json+")");
16588         if(!o) {
16589             throw {message: "JsonReader.read: Json object not found"};
16590         }
16591         
16592         if(o.metaData){
16593             
16594             delete this.ef;
16595             this.metaFromRemote = true;
16596             this.meta = o.metaData;
16597             this.recordType = Roo.data.Record.create(o.metaData.fields);
16598             this.onMetaChange(this.meta, this.recordType, o);
16599         }
16600         return this.readRecords(o);
16601     },
16602
16603     // private function a store will implement
16604     onMetaChange : function(meta, recordType, o){
16605
16606     },
16607
16608     /**
16609          * @ignore
16610          */
16611     simpleAccess: function(obj, subsc) {
16612         return obj[subsc];
16613     },
16614
16615         /**
16616          * @ignore
16617          */
16618     getJsonAccessor: function(){
16619         var re = /[\[\.]/;
16620         return function(expr) {
16621             try {
16622                 return(re.test(expr))
16623                     ? new Function("obj", "return obj." + expr)
16624                     : function(obj){
16625                         return obj[expr];
16626                     };
16627             } catch(e){}
16628             return Roo.emptyFn;
16629         };
16630     }(),
16631
16632     /**
16633      * Create a data block containing Roo.data.Records from an XML document.
16634      * @param {Object} o An object which contains an Array of row objects in the property specified
16635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636      * which contains the total size of the dataset.
16637      * @return {Object} data A data block which is used by an Roo.data.Store object as
16638      * a cache of Roo.data.Records.
16639      */
16640     readRecords : function(o){
16641         /**
16642          * After any data loads, the raw JSON data is available for further custom processing.
16643          * @type Object
16644          */
16645         this.o = o;
16646         var s = this.meta, Record = this.recordType,
16647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648
16649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16650         if (!this.ef) {
16651             if(s.totalProperty) {
16652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16653                 }
16654                 if(s.successProperty) {
16655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16656                 }
16657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658                 if (s.id) {
16659                         var g = this.getJsonAccessor(s.id);
16660                         this.getId = function(rec) {
16661                                 var r = g(rec);  
16662                                 return (r === undefined || r === "") ? null : r;
16663                         };
16664                 } else {
16665                         this.getId = function(){return null;};
16666                 }
16667             this.ef = [];
16668             for(var jj = 0; jj < fl; jj++){
16669                 f = fi[jj];
16670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671                 this.ef[jj] = this.getJsonAccessor(map);
16672             }
16673         }
16674
16675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676         if(s.totalProperty){
16677             var vt = parseInt(this.getTotal(o), 10);
16678             if(!isNaN(vt)){
16679                 totalRecords = vt;
16680             }
16681         }
16682         if(s.successProperty){
16683             var vs = this.getSuccess(o);
16684             if(vs === false || vs === 'false'){
16685                 success = false;
16686             }
16687         }
16688         var records = [];
16689         for(var i = 0; i < c; i++){
16690             var n = root[i];
16691             var values = {};
16692             var id = this.getId(n);
16693             for(var j = 0; j < fl; j++){
16694                 f = fi[j];
16695                                 var v = this.ef[j](n);
16696                                 if (!f.convert) {
16697                                         Roo.log('missing convert for ' + f.name);
16698                                         Roo.log(f);
16699                                         continue;
16700                                 }
16701                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702             }
16703                         if (!Record) {
16704                                 return {
16705                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706                                         success : false,
16707                                         records : [],
16708                                         totalRecords : 0
16709                                 };
16710                         }
16711             var record = new Record(values, id);
16712             record.json = n;
16713             records[i] = record;
16714         }
16715         return {
16716             raw : o,
16717             success : success,
16718             records : records,
16719             totalRecords : totalRecords
16720         };
16721     },
16722     // used when loading children.. @see loadDataFromChildren
16723     toLoadData: function(rec)
16724     {
16725         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727         return { data : data, total : data.length };
16728         
16729     }
16730 });/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.data.ArrayReader
16743  * @extends Roo.data.DataReader
16744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745  * Each element of that Array represents a row of data fields. The
16746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748  * <p>
16749  * Example code:.
16750  * <pre><code>
16751 var RecordDef = Roo.data.Record.create([
16752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16754 ]);
16755 var myReader = new Roo.data.ArrayReader({
16756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16757 }, RecordDef);
16758 </code></pre>
16759  * <p>
16760  * This would consume an Array like this:
16761  * <pre><code>
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763   </code></pre>
16764  
16765  * @constructor
16766  * Create a new JsonReader
16767  * @param {Object} meta Metadata configuration options.
16768  * @param {Object|Array} recordType Either an Array of field definition objects
16769  * 
16770  * @cfg {Array} fields Array of field definition objects
16771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772  * as specified to {@link Roo.data.Record#create},
16773  * or an {@link Roo.data.Record} object
16774  *
16775  * 
16776  * created using {@link Roo.data.Record#create}.
16777  */
16778 Roo.data.ArrayReader = function(meta, recordType)
16779 {    
16780     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16781 };
16782
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16784     
16785       /**
16786      * Create a data block containing Roo.data.Records from an XML document.
16787      * @param {Object} o An Array of row objects which represents the dataset.
16788      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789      * a cache of Roo.data.Records.
16790      */
16791     readRecords : function(o)
16792     {
16793         var sid = this.meta ? this.meta.id : null;
16794         var recordType = this.recordType, fields = recordType.prototype.fields;
16795         var records = [];
16796         var root = o;
16797         for(var i = 0; i < root.length; i++){
16798             var n = root[i];
16799             var values = {};
16800             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801             for(var j = 0, jlen = fields.length; j < jlen; j++){
16802                 var f = fields.items[j];
16803                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805                 v = f.convert(v);
16806                 values[f.name] = v;
16807             }
16808             var record = new recordType(values, id);
16809             record.json = n;
16810             records[records.length] = record;
16811         }
16812         return {
16813             records : records,
16814             totalRecords : records.length
16815         };
16816     },
16817     // used when loading children.. @see loadDataFromChildren
16818     toLoadData: function(rec)
16819     {
16820         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822         
16823     }
16824     
16825     
16826 });/*
16827  * - LGPL
16828  * * 
16829  */
16830
16831 /**
16832  * @class Roo.bootstrap.form.ComboBox
16833  * @extends Roo.bootstrap.form.TriggerField
16834  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835  * @cfg {Boolean} append (true|false) default false
16836  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841  * @cfg {Boolean} animate default true
16842  * @cfg {Boolean} emptyResultText only for touch device
16843  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844  * @cfg {String} emptyTitle default ''
16845  * @cfg {Number} width fixed with? experimental
16846  * @constructor
16847  * Create a new ComboBox.
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.bootstrap.form.ComboBox = function(config){
16851     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event expand
16855          * Fires when the dropdown list is expanded
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'expand' : true,
16859         /**
16860          * @event collapse
16861          * Fires when the dropdown list is collapsed
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'collapse' : true,
16865         /**
16866          * @event beforeselect
16867          * Fires before a list item is selected. Return false to cancel the selection.
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'beforeselect' : true,
16873         /**
16874          * @event select
16875          * Fires when a list item is selected
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878         * @param {Number} index The index of the selected item in the dropdown list
16879         */
16880         'select' : true,
16881         /**
16882          * @event beforequery
16883          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884          * The event object passed has these properties:
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         * @param {String} query The query
16887         * @param {Boolean} forceAll true to force "all" query
16888         * @param {Boolean} cancel true to cancel the query
16889         * @param {Object} e The query event object
16890         */
16891         'beforequery': true,
16892          /**
16893          * @event add
16894          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'add' : true,
16898         /**
16899          * @event edit
16900          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16903         */
16904         'edit' : true,
16905         /**
16906          * @event remove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'remove' : true,
16911         /**
16912          * @event afterremove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'afterremove' : true,
16917         /**
16918          * @event specialfilter
16919          * Fires when specialfilter
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'specialfilter' : true,
16923         /**
16924          * @event tick
16925          * Fires when tick the element
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'tick' : true,
16929         /**
16930          * @event touchviewdisplay
16931          * Fires when touch view require special display (default is using displayField)
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             * @param {Object} cfg set html .
16934             */
16935         'touchviewdisplay' : true
16936         
16937     });
16938     
16939     this.item = [];
16940     this.tickItems = [];
16941     
16942     this.selectedIndex = -1;
16943     if(this.mode == 'local'){
16944         if(config.queryDelay === undefined){
16945             this.queryDelay = 10;
16946         }
16947         if(config.minChars === undefined){
16948             this.minChars = 0;
16949         }
16950     }
16951 };
16952
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16954      
16955     /**
16956      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957      * rendering into an Roo.Editor, defaults to false)
16958      */
16959     /**
16960      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16962      */
16963     /**
16964      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16965      */
16966     /**
16967      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968      * the dropdown list (defaults to undefined, with no header element)
16969      */
16970
16971      /**
16972      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16973      */
16974      
16975      /**
16976      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977      */
16978     listWidth: undefined,
16979     /**
16980      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'text' if mode = 'local')
16982      */
16983     displayField: undefined,
16984     
16985     /**
16986      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'value' if mode = 'local'). 
16988      * Note: use of a valueField requires the user make a selection
16989      * in order for a value to be mapped.
16990      */
16991     valueField: undefined,
16992     /**
16993      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16994      */
16995     modalTitle : '',
16996     
16997     /**
16998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999      * field's data value (defaults to the underlying DOM element's name)
17000      */
17001     hiddenName: undefined,
17002     /**
17003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004      */
17005     listClass: '',
17006     /**
17007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008      */
17009     selectedClass: 'active',
17010     
17011     /**
17012      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013      */
17014     shadow:'sides',
17015     /**
17016      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017      * anchor positions (defaults to 'tl-bl')
17018      */
17019     listAlign: 'tl-bl?',
17020     /**
17021      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022      */
17023     maxHeight: 300,
17024     /**
17025      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17026      * query specified by the allQuery config option (defaults to 'query')
17027      */
17028     triggerAction: 'query',
17029     /**
17030      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031      * (defaults to 4, does not apply if editable = false)
17032      */
17033     minChars : 4,
17034     /**
17035      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037      */
17038     typeAhead: false,
17039     /**
17040      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042      */
17043     queryDelay: 500,
17044     /**
17045      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17047      */
17048     pageSize: 0,
17049     /**
17050      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17051      * when editable = true (defaults to false)
17052      */
17053     selectOnFocus:false,
17054     /**
17055      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056      */
17057     queryParam: 'query',
17058     /**
17059      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17060      * when mode = 'remote' (defaults to 'Loading...')
17061      */
17062     loadingText: 'Loading...',
17063     /**
17064      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065      */
17066     resizable: false,
17067     /**
17068      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069      */
17070     handleHeight : 8,
17071     /**
17072      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073      * traditional select (defaults to true)
17074      */
17075     editable: true,
17076     /**
17077      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078      */
17079     allQuery: '',
17080     /**
17081      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082      */
17083     mode: 'remote',
17084     /**
17085      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086      * listWidth has a higher value)
17087      */
17088     minListWidth : 70,
17089     /**
17090      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091      * allow the user to set arbitrary text into the field (defaults to false)
17092      */
17093     forceSelection:false,
17094     /**
17095      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096      * if typeAhead = true (defaults to 250)
17097      */
17098     typeAheadDelay : 250,
17099     /**
17100      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102      */
17103     valueNotFoundText : undefined,
17104     /**
17105      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106      */
17107     blockFocus : false,
17108     
17109     /**
17110      * @cfg {Boolean} disableClear Disable showing of clear button.
17111      */
17112     disableClear : false,
17113     /**
17114      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17115      */
17116     alwaysQuery : false,
17117     
17118     /**
17119      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17120      */
17121     multiple : false,
17122     
17123     /**
17124      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     invalidClass : "has-warning",
17127     
17128     /**
17129      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130      */
17131     validClass : "has-success",
17132     
17133     /**
17134      * @cfg {Boolean} specialFilter (true|false) special filter default false
17135      */
17136     specialFilter : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140      */
17141     mobileTouchView : true,
17142     
17143     /**
17144      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145      */
17146     useNativeIOS : false,
17147     
17148     /**
17149      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150      */
17151     mobile_restrict_height : false,
17152     
17153     ios_options : false,
17154     
17155     //private
17156     addicon : false,
17157     editicon: false,
17158     
17159     page: 0,
17160     hasQuery: false,
17161     append: false,
17162     loadNext: false,
17163     autoFocus : true,
17164     tickable : false,
17165     btnPosition : 'right',
17166     triggerList : true,
17167     showToggleBtn : true,
17168     animate : true,
17169     emptyResultText: 'Empty',
17170     triggerText : 'Select',
17171     emptyTitle : '',
17172     width : false,
17173     
17174     // element that contains real text value.. (when hidden is used..)
17175     
17176     getAutoCreate : function()
17177     {   
17178         var cfg = false;
17179         //render
17180         /*
17181          * Render classic select for iso
17182          */
17183         
17184         if(Roo.isIOS && this.useNativeIOS){
17185             cfg = this.getAutoCreateNativeIOS();
17186             return cfg;
17187         }
17188         
17189         /*
17190          * Touch Devices
17191          */
17192         
17193         if(Roo.isTouch && this.mobileTouchView){
17194             cfg = this.getAutoCreateTouchView();
17195             return cfg;;
17196         }
17197         
17198         /*
17199          *  Normal ComboBox
17200          */
17201         if(!this.tickable){
17202             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17203             return cfg;
17204         }
17205         
17206         /*
17207          *  ComboBox with tickable selections
17208          */
17209              
17210         var align = this.labelAlign || this.parentLabelAlign();
17211         
17212         cfg = {
17213             cls : 'form-group roo-combobox-tickable' //input-group
17214         };
17215         
17216         var btn_text_select = '';
17217         var btn_text_done = '';
17218         var btn_text_cancel = '';
17219         
17220         if (this.btn_text_show) {
17221             btn_text_select = 'Select';
17222             btn_text_done = 'Done';
17223             btn_text_cancel = 'Cancel'; 
17224         }
17225         
17226         var buttons = {
17227             tag : 'div',
17228             cls : 'tickable-buttons',
17229             cn : [
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234                     //html : this.triggerText
17235                     html: btn_text_select
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'ok',
17241                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242                     //html : 'Done'
17243                     html: btn_text_done
17244                 },
17245                 {
17246                     tag : 'button',
17247                     type : 'button',
17248                     name : 'cancel',
17249                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250                     //html : 'Cancel'
17251                     html: btn_text_cancel
17252                 }
17253             ]
17254         };
17255         
17256         if(this.editable){
17257             buttons.cn.unshift({
17258                 tag: 'input',
17259                 cls: 'roo-select2-search-field-input'
17260             });
17261         }
17262         
17263         var _this = this;
17264         
17265         Roo.each(buttons.cn, function(c){
17266             if (_this.size) {
17267                 c.cls += ' btn-' + _this.size;
17268             }
17269
17270             if (_this.disabled) {
17271                 c.disabled = true;
17272             }
17273         });
17274         
17275         var box = {
17276             tag: 'div',
17277             style : 'display: contents',
17278             cn: [
17279                 {
17280                     tag: 'input',
17281                     type : 'hidden',
17282                     cls: 'form-hidden-field'
17283                 },
17284                 {
17285                     tag: 'ul',
17286                     cls: 'roo-select2-choices',
17287                     cn:[
17288                         {
17289                             tag: 'li',
17290                             cls: 'roo-select2-search-field',
17291                             cn: [
17292                                 buttons
17293                             ]
17294                         }
17295                     ]
17296                 }
17297             ]
17298         };
17299         
17300         var combobox = {
17301             cls: 'roo-select2-container input-group roo-select2-container-multi',
17302             cn: [
17303                 
17304                 box
17305 //                {
17306 //                    tag: 'ul',
17307 //                    cls: 'typeahead typeahead-long dropdown-menu',
17308 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17309 //                }
17310             ]
17311         };
17312         
17313         if(this.hasFeedback && !this.allowBlank){
17314             
17315             var feedback = {
17316                 tag: 'span',
17317                 cls: 'glyphicon form-control-feedback'
17318             };
17319
17320             combobox.cn.push(feedback);
17321         }
17322         
17323         
17324         
17325         var indicator = {
17326             tag : 'i',
17327             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328             tooltip : 'This field is required'
17329         };
17330          
17331         if (this.allowBlank) {
17332             indicator = {
17333                 tag : 'i',
17334                 style : 'display:none'
17335             };
17336         } 
17337         if (align ==='left' && this.fieldLabel.length) {
17338             
17339             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17340             
17341             cfg.cn = [
17342                 indicator,
17343                 {
17344                     tag: 'label',
17345                     'for' :  id,
17346                     cls : 'control-label col-form-label',
17347                     html : this.fieldLabel
17348
17349                 },
17350                 {
17351                     cls : "", 
17352                     cn: [
17353                         combobox
17354                     ]
17355                 }
17356
17357             ];
17358             
17359             var labelCfg = cfg.cn[1];
17360             var contentCfg = cfg.cn[2];
17361             
17362
17363             if(this.indicatorpos == 'right'){
17364                 
17365                 cfg.cn = [
17366                     {
17367                         tag: 'label',
17368                         'for' :  id,
17369                         cls : 'control-label col-form-label',
17370                         cn : [
17371                             {
17372                                 tag : 'span',
17373                                 html : this.fieldLabel
17374                             },
17375                             indicator
17376                         ]
17377                     },
17378                     {
17379                         cls : "",
17380                         cn: [
17381                             combobox
17382                         ]
17383                     }
17384
17385                 ];
17386                 
17387                 
17388                 
17389                 labelCfg = cfg.cn[0];
17390                 contentCfg = cfg.cn[1];
17391             
17392             }
17393             
17394             if(this.labelWidth > 12){
17395                 labelCfg.style = "width: " + this.labelWidth + 'px';
17396             }
17397             if(this.width * 1 > 0){
17398                 contentCfg.style = "width: " + this.width + 'px';
17399             }
17400             if(this.labelWidth < 13 && this.labelmd == 0){
17401                 this.labelmd = this.labelWidth;
17402             }
17403             
17404             if(this.labellg > 0){
17405                 labelCfg.cls += ' col-lg-' + this.labellg;
17406                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17407             }
17408             
17409             if(this.labelmd > 0){
17410                 labelCfg.cls += ' col-md-' + this.labelmd;
17411                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17412             }
17413             
17414             if(this.labelsm > 0){
17415                 labelCfg.cls += ' col-sm-' + this.labelsm;
17416                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17417             }
17418             
17419             if(this.labelxs > 0){
17420                 labelCfg.cls += ' col-xs-' + this.labelxs;
17421                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17422             }
17423                 
17424                 
17425         } else if ( this.fieldLabel.length) {
17426 //                Roo.log(" label");
17427                  cfg.cn = [
17428                    indicator,
17429                     {
17430                         tag: 'label',
17431                         //cls : 'input-group-addon',
17432                         html : this.fieldLabel
17433                     },
17434                     combobox
17435                 ];
17436                 
17437                 if(this.indicatorpos == 'right'){
17438                     cfg.cn = [
17439                         {
17440                             tag: 'label',
17441                             //cls : 'input-group-addon',
17442                             html : this.fieldLabel
17443                         },
17444                         indicator,
17445                         combobox
17446                     ];
17447                     
17448                 }
17449
17450         } else {
17451             
17452 //                Roo.log(" no label && no align");
17453                 cfg = combobox
17454                      
17455                 
17456         }
17457          
17458         var settings=this;
17459         ['xs','sm','md','lg'].map(function(size){
17460             if (settings[size]) {
17461                 cfg.cls += ' col-' + size + '-' + settings[size];
17462             }
17463         });
17464         
17465         return cfg;
17466         
17467     },
17468     
17469     _initEventsCalled : false,
17470     
17471     // private
17472     initEvents: function()
17473     {   
17474         if (this._initEventsCalled) { // as we call render... prevent looping...
17475             return;
17476         }
17477         this._initEventsCalled = true;
17478         
17479         if (!this.store) {
17480             throw "can not find store for combo";
17481         }
17482         
17483         this.indicator = this.indicatorEl();
17484         
17485         this.store = Roo.factory(this.store, Roo.data);
17486         this.store.parent = this;
17487         
17488         // if we are building from html. then this element is so complex, that we can not really
17489         // use the rendered HTML.
17490         // so we have to trash and replace the previous code.
17491         if (Roo.XComponent.build_from_html) {
17492             // remove this element....
17493             var e = this.el.dom, k=0;
17494             while (e ) { e = e.previousSibling;  ++k;}
17495
17496             this.el.remove();
17497             
17498             this.el=false;
17499             this.rendered = false;
17500             
17501             this.render(this.parent().getChildContainer(true), k);
17502         }
17503         
17504         if(Roo.isIOS && this.useNativeIOS){
17505             this.initIOSView();
17506             return;
17507         }
17508         
17509         /*
17510          * Touch Devices
17511          */
17512         
17513         if(Roo.isTouch && this.mobileTouchView){
17514             this.initTouchView();
17515             return;
17516         }
17517         
17518         if(this.tickable){
17519             this.initTickableEvents();
17520             return;
17521         }
17522         
17523         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17524         
17525         if(this.hiddenName){
17526             
17527             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17528             
17529             this.hiddenField.dom.value =
17530                 this.hiddenValue !== undefined ? this.hiddenValue :
17531                 this.value !== undefined ? this.value : '';
17532
17533             // prevent input submission
17534             this.el.dom.removeAttribute('name');
17535             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17536              
17537              
17538         }
17539         //if(Roo.isGecko){
17540         //    this.el.dom.setAttribute('autocomplete', 'off');
17541         //}
17542         
17543         var cls = 'x-combo-list';
17544         
17545         //this.list = new Roo.Layer({
17546         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17547         //});
17548         
17549         var _this = this;
17550         
17551         (function(){
17552             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17553             _this.list.setWidth(lw);
17554         }).defer(100);
17555         
17556         this.list.on('mouseover', this.onViewOver, this);
17557         this.list.on('mousemove', this.onViewMove, this);
17558         this.list.on('scroll', this.onViewScroll, this);
17559         
17560         /*
17561         this.list.swallowEvent('mousewheel');
17562         this.assetHeight = 0;
17563
17564         if(this.title){
17565             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17566             this.assetHeight += this.header.getHeight();
17567         }
17568
17569         this.innerList = this.list.createChild({cls:cls+'-inner'});
17570         this.innerList.on('mouseover', this.onViewOver, this);
17571         this.innerList.on('mousemove', this.onViewMove, this);
17572         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17573         
17574         if(this.allowBlank && !this.pageSize && !this.disableClear){
17575             this.footer = this.list.createChild({cls:cls+'-ft'});
17576             this.pageTb = new Roo.Toolbar(this.footer);
17577            
17578         }
17579         if(this.pageSize){
17580             this.footer = this.list.createChild({cls:cls+'-ft'});
17581             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17582                     {pageSize: this.pageSize});
17583             
17584         }
17585         
17586         if (this.pageTb && this.allowBlank && !this.disableClear) {
17587             var _this = this;
17588             this.pageTb.add(new Roo.Toolbar.Fill(), {
17589                 cls: 'x-btn-icon x-btn-clear',
17590                 text: '&#160;',
17591                 handler: function()
17592                 {
17593                     _this.collapse();
17594                     _this.clearValue();
17595                     _this.onSelect(false, -1);
17596                 }
17597             });
17598         }
17599         if (this.footer) {
17600             this.assetHeight += this.footer.getHeight();
17601         }
17602         */
17603             
17604         if(!this.tpl){
17605             this.tpl = Roo.bootstrap.version == 4 ?
17606                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17607                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17608         }
17609
17610         this.view = new Roo.View(this.list, this.tpl, {
17611             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17612         });
17613         //this.view.wrapEl.setDisplayed(false);
17614         this.view.on('click', this.onViewClick, this);
17615         
17616         
17617         this.store.on('beforeload', this.onBeforeLoad, this);
17618         this.store.on('load', this.onLoad, this);
17619         this.store.on('loadexception', this.onLoadException, this);
17620         /*
17621         if(this.resizable){
17622             this.resizer = new Roo.Resizable(this.list,  {
17623                pinned:true, handles:'se'
17624             });
17625             this.resizer.on('resize', function(r, w, h){
17626                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17627                 this.listWidth = w;
17628                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17629                 this.restrictHeight();
17630             }, this);
17631             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17632         }
17633         */
17634         if(!this.editable){
17635             this.editable = true;
17636             this.setEditable(false);
17637         }
17638         
17639         /*
17640         
17641         if (typeof(this.events.add.listeners) != 'undefined') {
17642             
17643             this.addicon = this.wrap.createChild(
17644                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17645        
17646             this.addicon.on('click', function(e) {
17647                 this.fireEvent('add', this);
17648             }, this);
17649         }
17650         if (typeof(this.events.edit.listeners) != 'undefined') {
17651             
17652             this.editicon = this.wrap.createChild(
17653                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17654             if (this.addicon) {
17655                 this.editicon.setStyle('margin-left', '40px');
17656             }
17657             this.editicon.on('click', function(e) {
17658                 
17659                 // we fire even  if inothing is selected..
17660                 this.fireEvent('edit', this, this.lastData );
17661                 
17662             }, this);
17663         }
17664         */
17665         
17666         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17667             "up" : function(e){
17668                 this.inKeyMode = true;
17669                 this.selectPrev();
17670             },
17671
17672             "down" : function(e){
17673                 if(!this.isExpanded()){
17674                     this.onTriggerClick();
17675                 }else{
17676                     this.inKeyMode = true;
17677                     this.selectNext();
17678                 }
17679             },
17680
17681             "enter" : function(e){
17682 //                this.onViewClick();
17683                 //return true;
17684                 this.collapse();
17685                 
17686                 if(this.fireEvent("specialkey", this, e)){
17687                     this.onViewClick(false);
17688                 }
17689                 
17690                 return true;
17691             },
17692
17693             "esc" : function(e){
17694                 this.collapse();
17695             },
17696
17697             "tab" : function(e){
17698                 this.collapse();
17699                 
17700                 if(this.fireEvent("specialkey", this, e)){
17701                     this.onViewClick(false);
17702                 }
17703                 
17704                 return true;
17705             },
17706
17707             scope : this,
17708
17709             doRelay : function(foo, bar, hname){
17710                 if(hname == 'down' || this.scope.isExpanded()){
17711                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17712                 }
17713                 return true;
17714             },
17715
17716             forceKeyDown: true
17717         });
17718         
17719         
17720         this.queryDelay = Math.max(this.queryDelay || 10,
17721                 this.mode == 'local' ? 10 : 250);
17722         
17723         
17724         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17725         
17726         if(this.typeAhead){
17727             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17728         }
17729         if(this.editable !== false){
17730             this.inputEl().on("keyup", this.onKeyUp, this);
17731         }
17732         if(this.forceSelection){
17733             this.inputEl().on('blur', this.doForce, this);
17734         }
17735         
17736         if(this.multiple){
17737             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17738             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17739         }
17740     },
17741     
17742     initTickableEvents: function()
17743     {   
17744         this.createList();
17745         
17746         if(this.hiddenName){
17747             
17748             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17749             
17750             this.hiddenField.dom.value =
17751                 this.hiddenValue !== undefined ? this.hiddenValue :
17752                 this.value !== undefined ? this.value : '';
17753
17754             // prevent input submission
17755             this.el.dom.removeAttribute('name');
17756             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17757              
17758              
17759         }
17760         
17761 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17762         
17763         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17764         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17765         if(this.triggerList){
17766             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17767         }
17768          
17769         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17770         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17771         
17772         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17773         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17774         
17775         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17776         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17777         
17778         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17779         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17781         
17782         this.okBtn.hide();
17783         this.cancelBtn.hide();
17784         
17785         var _this = this;
17786         
17787         (function(){
17788             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17789             _this.list.setWidth(lw);
17790         }).defer(100);
17791         
17792         this.list.on('mouseover', this.onViewOver, this);
17793         this.list.on('mousemove', this.onViewMove, this);
17794         
17795         this.list.on('scroll', this.onViewScroll, this);
17796         
17797         if(!this.tpl){
17798             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17799                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17800         }
17801
17802         this.view = new Roo.View(this.list, this.tpl, {
17803             singleSelect:true,
17804             tickable:true,
17805             parent:this,
17806             store: this.store,
17807             selectedClass: this.selectedClass
17808         });
17809         
17810         //this.view.wrapEl.setDisplayed(false);
17811         this.view.on('click', this.onViewClick, this);
17812         
17813         
17814         
17815         this.store.on('beforeload', this.onBeforeLoad, this);
17816         this.store.on('load', this.onLoad, this);
17817         this.store.on('loadexception', this.onLoadException, this);
17818         
17819         if(this.editable){
17820             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17821                 "up" : function(e){
17822                     this.inKeyMode = true;
17823                     this.selectPrev();
17824                 },
17825
17826                 "down" : function(e){
17827                     this.inKeyMode = true;
17828                     this.selectNext();
17829                 },
17830
17831                 "enter" : function(e){
17832                     if(this.fireEvent("specialkey", this, e)){
17833                         this.onViewClick(false);
17834                     }
17835                     
17836                     return true;
17837                 },
17838
17839                 "esc" : function(e){
17840                     this.onTickableFooterButtonClick(e, false, false);
17841                 },
17842
17843                 "tab" : function(e){
17844                     this.fireEvent("specialkey", this, e);
17845                     
17846                     this.onTickableFooterButtonClick(e, false, false);
17847                     
17848                     return true;
17849                 },
17850
17851                 scope : this,
17852
17853                 doRelay : function(e, fn, key){
17854                     if(this.scope.isExpanded()){
17855                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17856                     }
17857                     return true;
17858                 },
17859
17860                 forceKeyDown: true
17861             });
17862         }
17863         
17864         this.queryDelay = Math.max(this.queryDelay || 10,
17865                 this.mode == 'local' ? 10 : 250);
17866         
17867         
17868         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17869         
17870         if(this.typeAhead){
17871             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17872         }
17873         
17874         if(this.editable !== false){
17875             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17876         }
17877         
17878         this.indicator = this.indicatorEl();
17879         
17880         if(this.indicator){
17881             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17882             this.indicator.hide();
17883         }
17884         
17885     },
17886
17887     onDestroy : function(){
17888         if(this.view){
17889             this.view.setStore(null);
17890             this.view.el.removeAllListeners();
17891             this.view.el.remove();
17892             this.view.purgeListeners();
17893         }
17894         if(this.list){
17895             this.list.dom.innerHTML  = '';
17896         }
17897         
17898         if(this.store){
17899             this.store.un('beforeload', this.onBeforeLoad, this);
17900             this.store.un('load', this.onLoad, this);
17901             this.store.un('loadexception', this.onLoadException, this);
17902         }
17903         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17904     },
17905
17906     // private
17907     fireKey : function(e){
17908         if(e.isNavKeyPress() && !this.list.isVisible()){
17909             this.fireEvent("specialkey", this, e);
17910         }
17911     },
17912
17913     // private
17914     onResize: function(w, h)
17915     {
17916         
17917         
17918 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17919 //        
17920 //        if(typeof w != 'number'){
17921 //            // we do not handle it!?!?
17922 //            return;
17923 //        }
17924 //        var tw = this.trigger.getWidth();
17925 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17926 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17927 //        var x = w - tw;
17928 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17929 //            
17930 //        //this.trigger.setStyle('left', x+'px');
17931 //        
17932 //        if(this.list && this.listWidth === undefined){
17933 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17934 //            this.list.setWidth(lw);
17935 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17936 //        }
17937         
17938     
17939         
17940     },
17941
17942     /**
17943      * Allow or prevent the user from directly editing the field text.  If false is passed,
17944      * the user will only be able to select from the items defined in the dropdown list.  This method
17945      * is the runtime equivalent of setting the 'editable' config option at config time.
17946      * @param {Boolean} value True to allow the user to directly edit the field text
17947      */
17948     setEditable : function(value){
17949         if(value == this.editable){
17950             return;
17951         }
17952         this.editable = value;
17953         if(!value){
17954             this.inputEl().dom.setAttribute('readOnly', true);
17955             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17956             this.inputEl().addClass('x-combo-noedit');
17957         }else{
17958             this.inputEl().dom.removeAttribute('readOnly');
17959             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17960             this.inputEl().removeClass('x-combo-noedit');
17961         }
17962     },
17963
17964     // private
17965     
17966     onBeforeLoad : function(combo,opts){
17967         if(!this.hasFocus){
17968             return;
17969         }
17970          if (!opts.add) {
17971             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17972          }
17973         this.restrictHeight();
17974         this.selectedIndex = -1;
17975     },
17976
17977     // private
17978     onLoad : function(){
17979         
17980         this.hasQuery = false;
17981         
17982         if(!this.hasFocus){
17983             return;
17984         }
17985         
17986         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17987             this.loading.hide();
17988         }
17989         
17990         if(this.store.getCount() > 0){
17991             
17992             this.expand();
17993             this.restrictHeight();
17994             if(this.lastQuery == this.allQuery){
17995                 if(this.editable && !this.tickable){
17996                     this.inputEl().dom.select();
17997                 }
17998                 
17999                 if(
18000                     !this.selectByValue(this.value, true) &&
18001                     this.autoFocus && 
18002                     (
18003                         !this.store.lastOptions ||
18004                         typeof(this.store.lastOptions.add) == 'undefined' || 
18005                         this.store.lastOptions.add != true
18006                     )
18007                 ){
18008                     this.select(0, true);
18009                 }
18010             }else{
18011                 if(this.autoFocus){
18012                     this.selectNext();
18013                 }
18014                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18015                     this.taTask.delay(this.typeAheadDelay);
18016                 }
18017             }
18018         }else{
18019             this.onEmptyResults();
18020         }
18021         
18022         //this.el.focus();
18023     },
18024     // private
18025     onLoadException : function()
18026     {
18027         this.hasQuery = false;
18028         
18029         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18030             this.loading.hide();
18031         }
18032         
18033         if(this.tickable && this.editable){
18034             return;
18035         }
18036         
18037         this.collapse();
18038         // only causes errors at present
18039         //Roo.log(this.store.reader.jsonData);
18040         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18041             // fixme
18042             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18043         //}
18044         
18045         
18046     },
18047     // private
18048     onTypeAhead : function(){
18049         if(this.store.getCount() > 0){
18050             var r = this.store.getAt(0);
18051             var newValue = r.data[this.displayField];
18052             var len = newValue.length;
18053             var selStart = this.getRawValue().length;
18054             
18055             if(selStart != len){
18056                 this.setRawValue(newValue);
18057                 this.selectText(selStart, newValue.length);
18058             }
18059         }
18060     },
18061
18062     // private
18063     onSelect : function(record, index){
18064         
18065         if(this.fireEvent('beforeselect', this, record, index) !== false){
18066         
18067             this.setFromData(index > -1 ? record.data : false);
18068             
18069             this.collapse();
18070             this.fireEvent('select', this, record, index);
18071         }
18072     },
18073
18074     /**
18075      * Returns the currently selected field value or empty string if no value is set.
18076      * @return {String} value The selected value
18077      */
18078     getValue : function()
18079     {
18080         if(Roo.isIOS && this.useNativeIOS){
18081             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18082         }
18083         
18084         if(this.multiple){
18085             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18086         }
18087         
18088         if(this.valueField){
18089             return typeof this.value != 'undefined' ? this.value : '';
18090         }else{
18091             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18092         }
18093     },
18094     
18095     getRawValue : function()
18096     {
18097         if(Roo.isIOS && this.useNativeIOS){
18098             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18099         }
18100         
18101         var v = this.inputEl().getValue();
18102         
18103         return v;
18104     },
18105
18106     /**
18107      * Clears any text/value currently set in the field
18108      */
18109     clearValue : function(){
18110         
18111         if(this.hiddenField){
18112             this.hiddenField.dom.value = '';
18113         }
18114         this.value = '';
18115         this.setRawValue('');
18116         this.lastSelectionText = '';
18117         this.lastData = false;
18118         
18119         var close = this.closeTriggerEl();
18120         
18121         if(close){
18122             close.hide();
18123         }
18124         
18125         this.validate();
18126         
18127     },
18128
18129     /**
18130      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18131      * will be displayed in the field.  If the value does not match the data value of an existing item,
18132      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18133      * Otherwise the field will be blank (although the value will still be set).
18134      * @param {String} value The value to match
18135      */
18136     setValue : function(v)
18137     {
18138         if(Roo.isIOS && this.useNativeIOS){
18139             this.setIOSValue(v);
18140             return;
18141         }
18142         
18143         if(this.multiple){
18144             this.syncValue();
18145             return;
18146         }
18147         
18148         var text = v;
18149         if(this.valueField){
18150             var r = this.findRecord(this.valueField, v);
18151             if(r){
18152                 text = r.data[this.displayField];
18153             }else if(this.valueNotFoundText !== undefined){
18154                 text = this.valueNotFoundText;
18155             }
18156         }
18157         this.lastSelectionText = text;
18158         if(this.hiddenField){
18159             this.hiddenField.dom.value = v;
18160         }
18161         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18162         this.value = v;
18163         
18164         var close = this.closeTriggerEl();
18165         
18166         if(close){
18167             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18168         }
18169         
18170         this.validate();
18171     },
18172     /**
18173      * @property {Object} the last set data for the element
18174      */
18175     
18176     lastData : false,
18177     /**
18178      * Sets the value of the field based on a object which is related to the record format for the store.
18179      * @param {Object} value the value to set as. or false on reset?
18180      */
18181     setFromData : function(o){
18182         
18183         if(this.multiple){
18184             this.addItem(o);
18185             return;
18186         }
18187             
18188         var dv = ''; // display value
18189         var vv = ''; // value value..
18190         this.lastData = o;
18191         if (this.displayField) {
18192             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18193         } else {
18194             // this is an error condition!!!
18195             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18196         }
18197         
18198         if(this.valueField){
18199             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18200         }
18201         
18202         var close = this.closeTriggerEl();
18203         
18204         if(close){
18205             if(dv.length || vv * 1 > 0){
18206                 close.show() ;
18207                 this.blockFocus=true;
18208             } else {
18209                 close.hide();
18210             }             
18211         }
18212         
18213         if(this.hiddenField){
18214             this.hiddenField.dom.value = vv;
18215             
18216             this.lastSelectionText = dv;
18217             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18218             this.value = vv;
18219             return;
18220         }
18221         // no hidden field.. - we store the value in 'value', but still display
18222         // display field!!!!
18223         this.lastSelectionText = dv;
18224         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18225         this.value = vv;
18226         
18227         
18228         
18229     },
18230     // private
18231     reset : function(){
18232         // overridden so that last data is reset..
18233         
18234         if(this.multiple){
18235             this.clearItem();
18236             return;
18237         }
18238         
18239         this.setValue(this.originalValue);
18240         //this.clearInvalid();
18241         this.lastData = false;
18242         if (this.view) {
18243             this.view.clearSelections();
18244         }
18245         
18246         this.validate();
18247     },
18248     // private
18249     findRecord : function(prop, value){
18250         var record;
18251         if(this.store.getCount() > 0){
18252             this.store.each(function(r){
18253                 if(r.data[prop] == value){
18254                     record = r;
18255                     return false;
18256                 }
18257                 return true;
18258             });
18259         }
18260         return record;
18261     },
18262     
18263     getName: function()
18264     {
18265         // returns hidden if it's set..
18266         if (!this.rendered) {return ''};
18267         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18268         
18269     },
18270     // private
18271     onViewMove : function(e, t){
18272         this.inKeyMode = false;
18273     },
18274
18275     // private
18276     onViewOver : function(e, t){
18277         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18278             return;
18279         }
18280         var item = this.view.findItemFromChild(t);
18281         
18282         if(item){
18283             var index = this.view.indexOf(item);
18284             this.select(index, false);
18285         }
18286     },
18287
18288     // private
18289     onViewClick : function(view, doFocus, el, e)
18290     {
18291         var index = this.view.getSelectedIndexes()[0];
18292         
18293         var r = this.store.getAt(index);
18294         
18295         if(this.tickable){
18296             
18297             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18298                 return;
18299             }
18300             
18301             var rm = false;
18302             var _this = this;
18303             
18304             Roo.each(this.tickItems, function(v,k){
18305                 
18306                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18307                     Roo.log(v);
18308                     _this.tickItems.splice(k, 1);
18309                     
18310                     if(typeof(e) == 'undefined' && view == false){
18311                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18312                     }
18313                     
18314                     rm = true;
18315                     return;
18316                 }
18317             });
18318             
18319             if(rm){
18320                 return;
18321             }
18322             
18323             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18324                 this.tickItems.push(r.data);
18325             }
18326             
18327             if(typeof(e) == 'undefined' && view == false){
18328                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18329             }
18330                     
18331             return;
18332         }
18333         
18334         if(r){
18335             this.onSelect(r, index);
18336         }
18337         if(doFocus !== false && !this.blockFocus){
18338             this.inputEl().focus();
18339         }
18340     },
18341
18342     // private
18343     restrictHeight : function(){
18344         //this.innerList.dom.style.height = '';
18345         //var inner = this.innerList.dom;
18346         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18347         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18348         //this.list.beginUpdate();
18349         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18350         this.list.alignTo(this.inputEl(), this.listAlign);
18351         this.list.alignTo(this.inputEl(), this.listAlign);
18352         //this.list.endUpdate();
18353     },
18354
18355     // private
18356     onEmptyResults : function(){
18357         
18358         if(this.tickable && this.editable){
18359             this.hasFocus = false;
18360             this.restrictHeight();
18361             return;
18362         }
18363         
18364         this.collapse();
18365     },
18366
18367     /**
18368      * Returns true if the dropdown list is expanded, else false.
18369      */
18370     isExpanded : function(){
18371         return this.list.isVisible();
18372     },
18373
18374     /**
18375      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18376      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18377      * @param {String} value The data value of the item to select
18378      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18379      * selected item if it is not currently in view (defaults to true)
18380      * @return {Boolean} True if the value matched an item in the list, else false
18381      */
18382     selectByValue : function(v, scrollIntoView){
18383         if(v !== undefined && v !== null){
18384             var r = this.findRecord(this.valueField || this.displayField, v);
18385             if(r){
18386                 this.select(this.store.indexOf(r), scrollIntoView);
18387                 return true;
18388             }
18389         }
18390         return false;
18391     },
18392
18393     /**
18394      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18395      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18396      * @param {Number} index The zero-based index of the list item to select
18397      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18398      * selected item if it is not currently in view (defaults to true)
18399      */
18400     select : function(index, scrollIntoView){
18401         this.selectedIndex = index;
18402         this.view.select(index);
18403         if(scrollIntoView !== false){
18404             var el = this.view.getNode(index);
18405             /*
18406              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18407              */
18408             if(el){
18409                 this.list.scrollChildIntoView(el, false);
18410             }
18411         }
18412     },
18413
18414     // private
18415     selectNext : function(){
18416         var ct = this.store.getCount();
18417         if(ct > 0){
18418             if(this.selectedIndex == -1){
18419                 this.select(0);
18420             }else if(this.selectedIndex < ct-1){
18421                 this.select(this.selectedIndex+1);
18422             }
18423         }
18424     },
18425
18426     // private
18427     selectPrev : function(){
18428         var ct = this.store.getCount();
18429         if(ct > 0){
18430             if(this.selectedIndex == -1){
18431                 this.select(0);
18432             }else if(this.selectedIndex != 0){
18433                 this.select(this.selectedIndex-1);
18434             }
18435         }
18436     },
18437
18438     // private
18439     onKeyUp : function(e){
18440         if(this.editable !== false && !e.isSpecialKey()){
18441             this.lastKey = e.getKey();
18442             this.dqTask.delay(this.queryDelay);
18443         }
18444     },
18445
18446     // private
18447     validateBlur : function(){
18448         return !this.list || !this.list.isVisible();   
18449     },
18450
18451     // private
18452     initQuery : function(){
18453         
18454         var v = this.getRawValue();
18455         
18456         if(this.tickable && this.editable){
18457             v = this.tickableInputEl().getValue();
18458         }
18459         
18460         this.doQuery(v);
18461     },
18462
18463     // private
18464     doForce : function(){
18465         if(this.inputEl().dom.value.length > 0){
18466             this.inputEl().dom.value =
18467                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18468              
18469         }
18470     },
18471
18472     /**
18473      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18474      * query allowing the query action to be canceled if needed.
18475      * @param {String} query The SQL query to execute
18476      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18477      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18478      * saved in the current store (defaults to false)
18479      */
18480     doQuery : function(q, forceAll){
18481         
18482         if(q === undefined || q === null){
18483             q = '';
18484         }
18485         var qe = {
18486             query: q,
18487             forceAll: forceAll,
18488             combo: this,
18489             cancel:false
18490         };
18491         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18492             return false;
18493         }
18494         q = qe.query;
18495         
18496         forceAll = qe.forceAll;
18497         if(forceAll === true || (q.length >= this.minChars)){
18498             
18499             this.hasQuery = true;
18500             
18501             if(this.lastQuery != q || this.alwaysQuery){
18502                 this.lastQuery = q;
18503                 if(this.mode == 'local'){
18504                     this.selectedIndex = -1;
18505                     if(forceAll){
18506                         this.store.clearFilter();
18507                     }else{
18508                         
18509                         if(this.specialFilter){
18510                             this.fireEvent('specialfilter', this);
18511                             this.onLoad();
18512                             return;
18513                         }
18514                         
18515                         this.store.filter(this.displayField, q);
18516                     }
18517                     
18518                     this.store.fireEvent("datachanged", this.store);
18519                     
18520                     this.onLoad();
18521                     
18522                     
18523                 }else{
18524                     
18525                     this.store.baseParams[this.queryParam] = q;
18526                     
18527                     var options = {params : this.getParams(q)};
18528                     
18529                     if(this.loadNext){
18530                         options.add = true;
18531                         options.params.start = this.page * this.pageSize;
18532                     }
18533                     
18534                     this.store.load(options);
18535                     
18536                     /*
18537                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18538                      *  we should expand the list on onLoad
18539                      *  so command out it
18540                      */
18541 //                    this.expand();
18542                 }
18543             }else{
18544                 this.selectedIndex = -1;
18545                 this.onLoad();   
18546             }
18547         }
18548         
18549         this.loadNext = false;
18550     },
18551     
18552     // private
18553     getParams : function(q){
18554         var p = {};
18555         //p[this.queryParam] = q;
18556         
18557         if(this.pageSize){
18558             p.start = 0;
18559             p.limit = this.pageSize;
18560         }
18561         return p;
18562     },
18563
18564     /**
18565      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18566      */
18567     collapse : function(){
18568         if(!this.isExpanded()){
18569             return;
18570         }
18571         
18572         this.list.hide();
18573         
18574         this.hasFocus = false;
18575         
18576         if(this.tickable){
18577             this.okBtn.hide();
18578             this.cancelBtn.hide();
18579             this.trigger.show();
18580             
18581             if(this.editable){
18582                 this.tickableInputEl().dom.value = '';
18583                 this.tickableInputEl().blur();
18584             }
18585             
18586         }
18587         
18588         Roo.get(document).un('mousedown', this.collapseIf, this);
18589         Roo.get(document).un('mousewheel', this.collapseIf, this);
18590         if (!this.editable) {
18591             Roo.get(document).un('keydown', this.listKeyPress, this);
18592         }
18593         this.fireEvent('collapse', this);
18594         
18595         this.validate();
18596     },
18597
18598     // private
18599     collapseIf : function(e){
18600         var in_combo  = e.within(this.el);
18601         var in_list =  e.within(this.list);
18602         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18603         
18604         if (in_combo || in_list || is_list) {
18605             //e.stopPropagation();
18606             return;
18607         }
18608         
18609         if(this.tickable){
18610             this.onTickableFooterButtonClick(e, false, false);
18611         }
18612
18613         this.collapse();
18614         
18615     },
18616
18617     /**
18618      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18619      */
18620     expand : function(){
18621        
18622         if(this.isExpanded() || !this.hasFocus){
18623             return;
18624         }
18625         
18626         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18627         this.list.setWidth(lw);
18628         
18629         Roo.log('expand');
18630         
18631         this.list.show();
18632         
18633         this.restrictHeight();
18634         
18635         if(this.tickable){
18636             
18637             this.tickItems = Roo.apply([], this.item);
18638             
18639             this.okBtn.show();
18640             this.cancelBtn.show();
18641             this.trigger.hide();
18642             
18643             if(this.editable){
18644                 this.tickableInputEl().focus();
18645             }
18646             
18647         }
18648         
18649         Roo.get(document).on('mousedown', this.collapseIf, this);
18650         Roo.get(document).on('mousewheel', this.collapseIf, this);
18651         if (!this.editable) {
18652             Roo.get(document).on('keydown', this.listKeyPress, this);
18653         }
18654         
18655         this.fireEvent('expand', this);
18656     },
18657
18658     // private
18659     // Implements the default empty TriggerField.onTriggerClick function
18660     onTriggerClick : function(e)
18661     {
18662         Roo.log('trigger click');
18663         
18664         if(this.disabled || !this.triggerList){
18665             return;
18666         }
18667         
18668         this.page = 0;
18669         this.loadNext = false;
18670         
18671         if(this.isExpanded()){
18672             this.collapse();
18673             if (!this.blockFocus) {
18674                 this.inputEl().focus();
18675             }
18676             
18677         }else {
18678             this.hasFocus = true;
18679             if(this.triggerAction == 'all') {
18680                 this.doQuery(this.allQuery, true);
18681             } else {
18682                 this.doQuery(this.getRawValue());
18683             }
18684             if (!this.blockFocus) {
18685                 this.inputEl().focus();
18686             }
18687         }
18688     },
18689     
18690     onTickableTriggerClick : function(e)
18691     {
18692         if(this.disabled){
18693             return;
18694         }
18695         
18696         this.page = 0;
18697         this.loadNext = false;
18698         this.hasFocus = true;
18699         
18700         if(this.triggerAction == 'all') {
18701             this.doQuery(this.allQuery, true);
18702         } else {
18703             this.doQuery(this.getRawValue());
18704         }
18705     },
18706     
18707     onSearchFieldClick : function(e)
18708     {
18709         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18710             this.onTickableFooterButtonClick(e, false, false);
18711             return;
18712         }
18713         
18714         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18715             return;
18716         }
18717         
18718         this.page = 0;
18719         this.loadNext = false;
18720         this.hasFocus = true;
18721         
18722         if(this.triggerAction == 'all') {
18723             this.doQuery(this.allQuery, true);
18724         } else {
18725             this.doQuery(this.getRawValue());
18726         }
18727     },
18728     
18729     listKeyPress : function(e)
18730     {
18731         //Roo.log('listkeypress');
18732         // scroll to first matching element based on key pres..
18733         if (e.isSpecialKey()) {
18734             return false;
18735         }
18736         var k = String.fromCharCode(e.getKey()).toUpperCase();
18737         //Roo.log(k);
18738         var match  = false;
18739         var csel = this.view.getSelectedNodes();
18740         var cselitem = false;
18741         if (csel.length) {
18742             var ix = this.view.indexOf(csel[0]);
18743             cselitem  = this.store.getAt(ix);
18744             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18745                 cselitem = false;
18746             }
18747             
18748         }
18749         
18750         this.store.each(function(v) { 
18751             if (cselitem) {
18752                 // start at existing selection.
18753                 if (cselitem.id == v.id) {
18754                     cselitem = false;
18755                 }
18756                 return true;
18757             }
18758                 
18759             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18760                 match = this.store.indexOf(v);
18761                 return false;
18762             }
18763             return true;
18764         }, this);
18765         
18766         if (match === false) {
18767             return true; // no more action?
18768         }
18769         // scroll to?
18770         this.view.select(match);
18771         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18772         sn.scrollIntoView(sn.dom.parentNode, false);
18773     },
18774     
18775     onViewScroll : function(e, t){
18776         
18777         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){
18778             return;
18779         }
18780         
18781         this.hasQuery = true;
18782         
18783         this.loading = this.list.select('.loading', true).first();
18784         
18785         if(this.loading === null){
18786             this.list.createChild({
18787                 tag: 'div',
18788                 cls: 'loading roo-select2-more-results roo-select2-active',
18789                 html: 'Loading more results...'
18790             });
18791             
18792             this.loading = this.list.select('.loading', true).first();
18793             
18794             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18795             
18796             this.loading.hide();
18797         }
18798         
18799         this.loading.show();
18800         
18801         var _combo = this;
18802         
18803         this.page++;
18804         this.loadNext = true;
18805         
18806         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18807         
18808         return;
18809     },
18810     
18811     addItem : function(o)
18812     {   
18813         var dv = ''; // display value
18814         
18815         if (this.displayField) {
18816             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18817         } else {
18818             // this is an error condition!!!
18819             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18820         }
18821         
18822         if(!dv.length){
18823             return;
18824         }
18825         
18826         var choice = this.choices.createChild({
18827             tag: 'li',
18828             cls: 'roo-select2-search-choice',
18829             cn: [
18830                 {
18831                     tag: 'div',
18832                     html: dv
18833                 },
18834                 {
18835                     tag: 'a',
18836                     href: '#',
18837                     cls: 'roo-select2-search-choice-close fa fa-times',
18838                     tabindex: '-1'
18839                 }
18840             ]
18841             
18842         }, this.searchField);
18843         
18844         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18845         
18846         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18847         
18848         this.item.push(o);
18849         
18850         this.lastData = o;
18851         
18852         this.syncValue();
18853         
18854         this.inputEl().dom.value = '';
18855         
18856         this.validate();
18857     },
18858     
18859     onRemoveItem : function(e, _self, o)
18860     {
18861         e.preventDefault();
18862         
18863         this.lastItem = Roo.apply([], this.item);
18864         
18865         var index = this.item.indexOf(o.data) * 1;
18866         
18867         if( index < 0){
18868             Roo.log('not this item?!');
18869             return;
18870         }
18871         
18872         this.item.splice(index, 1);
18873         o.item.remove();
18874         
18875         this.syncValue();
18876         
18877         this.fireEvent('remove', this, e);
18878         
18879         this.validate();
18880         
18881     },
18882     
18883     syncValue : function()
18884     {
18885         if(!this.item.length){
18886             this.clearValue();
18887             return;
18888         }
18889             
18890         var value = [];
18891         var _this = this;
18892         Roo.each(this.item, function(i){
18893             if(_this.valueField){
18894                 value.push(i[_this.valueField]);
18895                 return;
18896             }
18897
18898             value.push(i);
18899         });
18900
18901         this.value = value.join(',');
18902
18903         if(this.hiddenField){
18904             this.hiddenField.dom.value = this.value;
18905         }
18906         
18907         this.store.fireEvent("datachanged", this.store);
18908         
18909         this.validate();
18910     },
18911     
18912     clearItem : function()
18913     {
18914         if(!this.multiple){
18915             return;
18916         }
18917         
18918         this.item = [];
18919         
18920         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18921            c.remove();
18922         });
18923         
18924         this.syncValue();
18925         
18926         this.validate();
18927         
18928         if(this.tickable && !Roo.isTouch){
18929             this.view.refresh();
18930         }
18931     },
18932     
18933     inputEl: function ()
18934     {
18935         if(Roo.isIOS && this.useNativeIOS){
18936             return this.el.select('select.roo-ios-select', true).first();
18937         }
18938         
18939         if(Roo.isTouch && this.mobileTouchView){
18940             return this.el.select('input.form-control',true).first();
18941         }
18942         
18943         if(this.tickable){
18944             return this.searchField;
18945         }
18946         
18947         return this.el.select('input.form-control',true).first();
18948     },
18949     
18950     onTickableFooterButtonClick : function(e, btn, el)
18951     {
18952         e.preventDefault();
18953         
18954         this.lastItem = Roo.apply([], this.item);
18955         
18956         if(btn && btn.name == 'cancel'){
18957             this.tickItems = Roo.apply([], this.item);
18958             this.collapse();
18959             return;
18960         }
18961         
18962         this.clearItem();
18963         
18964         var _this = this;
18965         
18966         Roo.each(this.tickItems, function(o){
18967             _this.addItem(o);
18968         });
18969         
18970         this.collapse();
18971         
18972     },
18973     
18974     validate : function()
18975     {
18976         if(this.getVisibilityEl().hasClass('hidden')){
18977             return true;
18978         }
18979         
18980         var v = this.getRawValue();
18981         
18982         if(this.multiple){
18983             v = this.getValue();
18984         }
18985         
18986         if(this.disabled || this.allowBlank || v.length){
18987             this.markValid();
18988             return true;
18989         }
18990         
18991         this.markInvalid();
18992         return false;
18993     },
18994     
18995     tickableInputEl : function()
18996     {
18997         if(!this.tickable || !this.editable){
18998             return this.inputEl();
18999         }
19000         
19001         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19002     },
19003     
19004     
19005     getAutoCreateTouchView : function()
19006     {
19007         var id = Roo.id();
19008         
19009         var cfg = {
19010             cls: 'form-group' //input-group
19011         };
19012         
19013         var input =  {
19014             tag: 'input',
19015             id : id,
19016             type : this.inputType,
19017             cls : 'form-control x-combo-noedit',
19018             autocomplete: 'new-password',
19019             placeholder : this.placeholder || '',
19020             readonly : true
19021         };
19022         
19023         if (this.name) {
19024             input.name = this.name;
19025         }
19026         
19027         if (this.size) {
19028             input.cls += ' input-' + this.size;
19029         }
19030         
19031         if (this.disabled) {
19032             input.disabled = true;
19033         }
19034         
19035         var inputblock = {
19036             cls : 'roo-combobox-wrap',
19037             cn : [
19038                 input
19039             ]
19040         };
19041         
19042         if(this.before){
19043             inputblock.cls += ' input-group';
19044             
19045             inputblock.cn.unshift({
19046                 tag :'span',
19047                 cls : 'input-group-addon input-group-prepend input-group-text',
19048                 html : this.before
19049             });
19050         }
19051         
19052         if(this.removable && !this.multiple){
19053             inputblock.cls += ' roo-removable';
19054             
19055             inputblock.cn.push({
19056                 tag: 'button',
19057                 html : 'x',
19058                 cls : 'roo-combo-removable-btn close'
19059             });
19060         }
19061
19062         if(this.hasFeedback && !this.allowBlank){
19063             
19064             inputblock.cls += ' has-feedback';
19065             
19066             inputblock.cn.push({
19067                 tag: 'span',
19068                 cls: 'glyphicon form-control-feedback'
19069             });
19070             
19071         }
19072         
19073         if (this.after) {
19074             
19075             inputblock.cls += (this.before) ? '' : ' input-group';
19076             
19077             inputblock.cn.push({
19078                 tag :'span',
19079                 cls : 'input-group-addon input-group-append input-group-text',
19080                 html : this.after
19081             });
19082         }
19083
19084         
19085         var ibwrap = inputblock;
19086         
19087         if(this.multiple){
19088             ibwrap = {
19089                 tag: 'ul',
19090                 cls: 'roo-select2-choices',
19091                 cn:[
19092                     {
19093                         tag: 'li',
19094                         cls: 'roo-select2-search-field',
19095                         cn: [
19096
19097                             inputblock
19098                         ]
19099                     }
19100                 ]
19101             };
19102         
19103             
19104         }
19105         
19106         var combobox = {
19107             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19108             cn: [
19109                 {
19110                     tag: 'input',
19111                     type : 'hidden',
19112                     cls: 'form-hidden-field'
19113                 },
19114                 ibwrap
19115             ]
19116         };
19117         
19118         if(!this.multiple && this.showToggleBtn){
19119             
19120             var caret = {
19121                 cls: 'caret'
19122             };
19123             
19124             if (this.caret != false) {
19125                 caret = {
19126                      tag: 'i',
19127                      cls: 'fa fa-' + this.caret
19128                 };
19129                 
19130             }
19131             
19132             combobox.cn.push({
19133                 tag :'span',
19134                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19135                 cn : [
19136                     Roo.bootstrap.version == 3 ? caret : '',
19137                     {
19138                         tag: 'span',
19139                         cls: 'combobox-clear',
19140                         cn  : [
19141                             {
19142                                 tag : 'i',
19143                                 cls: 'icon-remove'
19144                             }
19145                         ]
19146                     }
19147                 ]
19148
19149             })
19150         }
19151         
19152         if(this.multiple){
19153             combobox.cls += ' roo-select2-container-multi';
19154         }
19155         
19156         var required =  this.allowBlank ?  {
19157                     tag : 'i',
19158                     style: 'display: none'
19159                 } : {
19160                    tag : 'i',
19161                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19162                    tooltip : 'This field is required'
19163                 };
19164         
19165         var align = this.labelAlign || this.parentLabelAlign();
19166         
19167         if (align ==='left' && this.fieldLabel.length) {
19168
19169             cfg.cn = [
19170                 required,
19171                 {
19172                     tag: 'label',
19173                     cls : 'control-label col-form-label',
19174                     html : this.fieldLabel
19175
19176                 },
19177                 {
19178                     cls : 'roo-combobox-wrap ', 
19179                     cn: [
19180                         combobox
19181                     ]
19182                 }
19183             ];
19184             
19185             var labelCfg = cfg.cn[1];
19186             var contentCfg = cfg.cn[2];
19187             
19188
19189             if(this.indicatorpos == 'right'){
19190                 cfg.cn = [
19191                     {
19192                         tag: 'label',
19193                         'for' :  id,
19194                         cls : 'control-label col-form-label',
19195                         cn : [
19196                             {
19197                                 tag : 'span',
19198                                 html : this.fieldLabel
19199                             },
19200                             required
19201                         ]
19202                     },
19203                     {
19204                         cls : "roo-combobox-wrap ",
19205                         cn: [
19206                             combobox
19207                         ]
19208                     }
19209
19210                 ];
19211                 
19212                 labelCfg = cfg.cn[0];
19213                 contentCfg = cfg.cn[1];
19214             }
19215             
19216            
19217             
19218             if(this.labelWidth > 12){
19219                 labelCfg.style = "width: " + this.labelWidth + 'px';
19220             }
19221            
19222             if(this.labelWidth < 13 && this.labelmd == 0){
19223                 this.labelmd = this.labelWidth;
19224             }
19225             
19226             if(this.labellg > 0){
19227                 labelCfg.cls += ' col-lg-' + this.labellg;
19228                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19229             }
19230             
19231             if(this.labelmd > 0){
19232                 labelCfg.cls += ' col-md-' + this.labelmd;
19233                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19234             }
19235             
19236             if(this.labelsm > 0){
19237                 labelCfg.cls += ' col-sm-' + this.labelsm;
19238                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19239             }
19240             
19241             if(this.labelxs > 0){
19242                 labelCfg.cls += ' col-xs-' + this.labelxs;
19243                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19244             }
19245                 
19246                 
19247         } else if ( this.fieldLabel.length) {
19248             cfg.cn = [
19249                required,
19250                 {
19251                     tag: 'label',
19252                     cls : 'control-label',
19253                     html : this.fieldLabel
19254
19255                 },
19256                 {
19257                     cls : '', 
19258                     cn: [
19259                         combobox
19260                     ]
19261                 }
19262             ];
19263             
19264             if(this.indicatorpos == 'right'){
19265                 cfg.cn = [
19266                     {
19267                         tag: 'label',
19268                         cls : 'control-label',
19269                         html : this.fieldLabel,
19270                         cn : [
19271                             required
19272                         ]
19273                     },
19274                     {
19275                         cls : '', 
19276                         cn: [
19277                             combobox
19278                         ]
19279                     }
19280                 ];
19281             }
19282         } else {
19283             cfg.cn = combobox;    
19284         }
19285         
19286         
19287         var settings = this;
19288         
19289         ['xs','sm','md','lg'].map(function(size){
19290             if (settings[size]) {
19291                 cfg.cls += ' col-' + size + '-' + settings[size];
19292             }
19293         });
19294         
19295         return cfg;
19296     },
19297     
19298     initTouchView : function()
19299     {
19300         this.renderTouchView();
19301         
19302         this.touchViewEl.on('scroll', function(){
19303             this.el.dom.scrollTop = 0;
19304         }, this);
19305         
19306         this.originalValue = this.getValue();
19307         
19308         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19309         
19310         this.inputEl().on("click", this.showTouchView, this);
19311         if (this.triggerEl) {
19312             this.triggerEl.on("click", this.showTouchView, this);
19313         }
19314         
19315         
19316         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19317         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19318         
19319         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19320         
19321         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19322         this.store.on('load', this.onTouchViewLoad, this);
19323         this.store.on('loadexception', this.onTouchViewLoadException, this);
19324         
19325         if(this.hiddenName){
19326             
19327             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19328             
19329             this.hiddenField.dom.value =
19330                 this.hiddenValue !== undefined ? this.hiddenValue :
19331                 this.value !== undefined ? this.value : '';
19332         
19333             this.el.dom.removeAttribute('name');
19334             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19335         }
19336         
19337         if(this.multiple){
19338             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19339             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19340         }
19341         
19342         if(this.removable && !this.multiple){
19343             var close = this.closeTriggerEl();
19344             if(close){
19345                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19346                 close.on('click', this.removeBtnClick, this, close);
19347             }
19348         }
19349         /*
19350          * fix the bug in Safari iOS8
19351          */
19352         this.inputEl().on("focus", function(e){
19353             document.activeElement.blur();
19354         }, this);
19355         
19356         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19357         
19358         return;
19359         
19360         
19361     },
19362     
19363     renderTouchView : function()
19364     {
19365         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19366         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19367         
19368         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19369         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370         
19371         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19372         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373         this.touchViewBodyEl.setStyle('overflow', 'auto');
19374         
19375         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19376         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19377         
19378         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19379         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19380         
19381     },
19382     
19383     showTouchView : function()
19384     {
19385         if(this.disabled){
19386             return;
19387         }
19388         
19389         this.touchViewHeaderEl.hide();
19390
19391         if(this.modalTitle.length){
19392             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19393             this.touchViewHeaderEl.show();
19394         }
19395
19396         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19397         this.touchViewEl.show();
19398
19399         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19400         
19401         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19402         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19403
19404         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19405
19406         if(this.modalTitle.length){
19407             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19408         }
19409         
19410         this.touchViewBodyEl.setHeight(bodyHeight);
19411
19412         if(this.animate){
19413             var _this = this;
19414             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19415         }else{
19416             this.touchViewEl.addClass(['in','show']);
19417         }
19418         
19419         if(this._touchViewMask){
19420             Roo.get(document.body).addClass("x-body-masked");
19421             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19422             this._touchViewMask.setStyle('z-index', 10000);
19423             this._touchViewMask.addClass('show');
19424         }
19425         
19426         this.doTouchViewQuery();
19427         
19428     },
19429     
19430     hideTouchView : function()
19431     {
19432         this.touchViewEl.removeClass(['in','show']);
19433
19434         if(this.animate){
19435             var _this = this;
19436             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19437         }else{
19438             this.touchViewEl.setStyle('display', 'none');
19439         }
19440         
19441         if(this._touchViewMask){
19442             this._touchViewMask.removeClass('show');
19443             Roo.get(document.body).removeClass("x-body-masked");
19444         }
19445     },
19446     
19447     setTouchViewValue : function()
19448     {
19449         if(this.multiple){
19450             this.clearItem();
19451         
19452             var _this = this;
19453
19454             Roo.each(this.tickItems, function(o){
19455                 this.addItem(o);
19456             }, this);
19457         }
19458         
19459         this.hideTouchView();
19460     },
19461     
19462     doTouchViewQuery : function()
19463     {
19464         var qe = {
19465             query: '',
19466             forceAll: true,
19467             combo: this,
19468             cancel:false
19469         };
19470         
19471         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19472             return false;
19473         }
19474         
19475         if(!this.alwaysQuery || this.mode == 'local'){
19476             this.onTouchViewLoad();
19477             return;
19478         }
19479         
19480         this.store.load();
19481     },
19482     
19483     onTouchViewBeforeLoad : function(combo,opts)
19484     {
19485         return;
19486     },
19487
19488     // private
19489     onTouchViewLoad : function()
19490     {
19491         if(this.store.getCount() < 1){
19492             this.onTouchViewEmptyResults();
19493             return;
19494         }
19495         
19496         this.clearTouchView();
19497         
19498         var rawValue = this.getRawValue();
19499         
19500         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19501         
19502         this.tickItems = [];
19503         
19504         this.store.data.each(function(d, rowIndex){
19505             var row = this.touchViewListGroup.createChild(template);
19506             
19507             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19508                 row.addClass(d.data.cls);
19509             }
19510             
19511             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19512                 var cfg = {
19513                     data : d.data,
19514                     html : d.data[this.displayField]
19515                 };
19516                 
19517                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19518                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19519                 }
19520             }
19521             row.removeClass('selected');
19522             if(!this.multiple && this.valueField &&
19523                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19524             {
19525                 // radio buttons..
19526                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19527                 row.addClass('selected');
19528             }
19529             
19530             if(this.multiple && this.valueField &&
19531                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19532             {
19533                 
19534                 // checkboxes...
19535                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19536                 this.tickItems.push(d.data);
19537             }
19538             
19539             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19540             
19541         }, this);
19542         
19543         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19544         
19545         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19546
19547         if(this.modalTitle.length){
19548             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19549         }
19550
19551         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19552         
19553         if(this.mobile_restrict_height && listHeight < bodyHeight){
19554             this.touchViewBodyEl.setHeight(listHeight);
19555         }
19556         
19557         var _this = this;
19558         
19559         if(firstChecked && listHeight > bodyHeight){
19560             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19561         }
19562         
19563     },
19564     
19565     onTouchViewLoadException : function()
19566     {
19567         this.hideTouchView();
19568     },
19569     
19570     onTouchViewEmptyResults : function()
19571     {
19572         this.clearTouchView();
19573         
19574         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19575         
19576         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19577         
19578     },
19579     
19580     clearTouchView : function()
19581     {
19582         this.touchViewListGroup.dom.innerHTML = '';
19583     },
19584     
19585     onTouchViewClick : function(e, el, o)
19586     {
19587         e.preventDefault();
19588         
19589         var row = o.row;
19590         var rowIndex = o.rowIndex;
19591         
19592         var r = this.store.getAt(rowIndex);
19593         
19594         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19595             
19596             if(!this.multiple){
19597                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19598                     c.dom.removeAttribute('checked');
19599                 }, this);
19600
19601                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19602
19603                 this.setFromData(r.data);
19604
19605                 var close = this.closeTriggerEl();
19606
19607                 if(close){
19608                     close.show();
19609                 }
19610
19611                 this.hideTouchView();
19612
19613                 this.fireEvent('select', this, r, rowIndex);
19614
19615                 return;
19616             }
19617
19618             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19619                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19620                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19621                 return;
19622             }
19623
19624             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19625             this.addItem(r.data);
19626             this.tickItems.push(r.data);
19627         }
19628     },
19629     
19630     getAutoCreateNativeIOS : function()
19631     {
19632         var cfg = {
19633             cls: 'form-group' //input-group,
19634         };
19635         
19636         var combobox =  {
19637             tag: 'select',
19638             cls : 'roo-ios-select'
19639         };
19640         
19641         if (this.name) {
19642             combobox.name = this.name;
19643         }
19644         
19645         if (this.disabled) {
19646             combobox.disabled = true;
19647         }
19648         
19649         var settings = this;
19650         
19651         ['xs','sm','md','lg'].map(function(size){
19652             if (settings[size]) {
19653                 cfg.cls += ' col-' + size + '-' + settings[size];
19654             }
19655         });
19656         
19657         cfg.cn = combobox;
19658         
19659         return cfg;
19660         
19661     },
19662     
19663     initIOSView : function()
19664     {
19665         this.store.on('load', this.onIOSViewLoad, this);
19666         
19667         return;
19668     },
19669     
19670     onIOSViewLoad : function()
19671     {
19672         if(this.store.getCount() < 1){
19673             return;
19674         }
19675         
19676         this.clearIOSView();
19677         
19678         if(this.allowBlank) {
19679             
19680             var default_text = '-- SELECT --';
19681             
19682             if(this.placeholder.length){
19683                 default_text = this.placeholder;
19684             }
19685             
19686             if(this.emptyTitle.length){
19687                 default_text += ' - ' + this.emptyTitle + ' -';
19688             }
19689             
19690             var opt = this.inputEl().createChild({
19691                 tag: 'option',
19692                 value : 0,
19693                 html : default_text
19694             });
19695             
19696             var o = {};
19697             o[this.valueField] = 0;
19698             o[this.displayField] = default_text;
19699             
19700             this.ios_options.push({
19701                 data : o,
19702                 el : opt
19703             });
19704             
19705         }
19706         
19707         this.store.data.each(function(d, rowIndex){
19708             
19709             var html = '';
19710             
19711             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19712                 html = d.data[this.displayField];
19713             }
19714             
19715             var value = '';
19716             
19717             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19718                 value = d.data[this.valueField];
19719             }
19720             
19721             var option = {
19722                 tag: 'option',
19723                 value : value,
19724                 html : html
19725             };
19726             
19727             if(this.value == d.data[this.valueField]){
19728                 option['selected'] = true;
19729             }
19730             
19731             var opt = this.inputEl().createChild(option);
19732             
19733             this.ios_options.push({
19734                 data : d.data,
19735                 el : opt
19736             });
19737             
19738         }, this);
19739         
19740         this.inputEl().on('change', function(){
19741            this.fireEvent('select', this);
19742         }, this);
19743         
19744     },
19745     
19746     clearIOSView: function()
19747     {
19748         this.inputEl().dom.innerHTML = '';
19749         
19750         this.ios_options = [];
19751     },
19752     
19753     setIOSValue: function(v)
19754     {
19755         this.value = v;
19756         
19757         if(!this.ios_options){
19758             return;
19759         }
19760         
19761         Roo.each(this.ios_options, function(opts){
19762            
19763            opts.el.dom.removeAttribute('selected');
19764            
19765            if(opts.data[this.valueField] != v){
19766                return;
19767            }
19768            
19769            opts.el.dom.setAttribute('selected', true);
19770            
19771         }, this);
19772     }
19773
19774     /** 
19775     * @cfg {Boolean} grow 
19776     * @hide 
19777     */
19778     /** 
19779     * @cfg {Number} growMin 
19780     * @hide 
19781     */
19782     /** 
19783     * @cfg {Number} growMax 
19784     * @hide 
19785     */
19786     /**
19787      * @hide
19788      * @method autoSize
19789      */
19790 });
19791
19792 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19793     
19794     header : {
19795         tag: 'div',
19796         cls: 'modal-header',
19797         cn: [
19798             {
19799                 tag: 'h4',
19800                 cls: 'modal-title'
19801             }
19802         ]
19803     },
19804     
19805     body : {
19806         tag: 'div',
19807         cls: 'modal-body',
19808         cn: [
19809             {
19810                 tag: 'ul',
19811                 cls: 'list-group'
19812             }
19813         ]
19814     },
19815     
19816     listItemRadio : {
19817         tag: 'li',
19818         cls: 'list-group-item',
19819         cn: [
19820             {
19821                 tag: 'span',
19822                 cls: 'roo-combobox-list-group-item-value'
19823             },
19824             {
19825                 tag: 'div',
19826                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19827                 cn: [
19828                     {
19829                         tag: 'input',
19830                         type: 'radio'
19831                     },
19832                     {
19833                         tag: 'label'
19834                     }
19835                 ]
19836             }
19837         ]
19838     },
19839     
19840     listItemCheckbox : {
19841         tag: 'li',
19842         cls: 'list-group-item',
19843         cn: [
19844             {
19845                 tag: 'span',
19846                 cls: 'roo-combobox-list-group-item-value'
19847             },
19848             {
19849                 tag: 'div',
19850                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19851                 cn: [
19852                     {
19853                         tag: 'input',
19854                         type: 'checkbox'
19855                     },
19856                     {
19857                         tag: 'label'
19858                     }
19859                 ]
19860             }
19861         ]
19862     },
19863     
19864     emptyResult : {
19865         tag: 'div',
19866         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19867     },
19868     
19869     footer : {
19870         tag: 'div',
19871         cls: 'modal-footer',
19872         cn: [
19873             {
19874                 tag: 'div',
19875                 cls: 'row',
19876                 cn: [
19877                     {
19878                         tag: 'div',
19879                         cls: 'col-xs-6 text-left',
19880                         cn: {
19881                             tag: 'button',
19882                             cls: 'btn btn-danger roo-touch-view-cancel',
19883                             html: 'Cancel'
19884                         }
19885                     },
19886                     {
19887                         tag: 'div',
19888                         cls: 'col-xs-6 text-right',
19889                         cn: {
19890                             tag: 'button',
19891                             cls: 'btn btn-success roo-touch-view-ok',
19892                             html: 'OK'
19893                         }
19894                     }
19895                 ]
19896             }
19897         ]
19898         
19899     }
19900 });
19901
19902 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19903     
19904     touchViewTemplate : {
19905         tag: 'div',
19906         cls: 'modal fade roo-combobox-touch-view',
19907         cn: [
19908             {
19909                 tag: 'div',
19910                 cls: 'modal-dialog',
19911                 style : 'position:fixed', // we have to fix position....
19912                 cn: [
19913                     {
19914                         tag: 'div',
19915                         cls: 'modal-content',
19916                         cn: [
19917                             Roo.bootstrap.form.ComboBox.header,
19918                             Roo.bootstrap.form.ComboBox.body,
19919                             Roo.bootstrap.form.ComboBox.footer
19920                         ]
19921                     }
19922                 ]
19923             }
19924         ]
19925     }
19926 });/*
19927  * Based on:
19928  * Ext JS Library 1.1.1
19929  * Copyright(c) 2006-2007, Ext JS, LLC.
19930  *
19931  * Originally Released Under LGPL - original licence link has changed is not relivant.
19932  *
19933  * Fork - LGPL
19934  * <script type="text/javascript">
19935  */
19936
19937 /**
19938  * @class Roo.View
19939  * @extends Roo.util.Observable
19940  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19941  * This class also supports single and multi selection modes. <br>
19942  * Create a data model bound view:
19943  <pre><code>
19944  var store = new Roo.data.Store(...);
19945
19946  var view = new Roo.View({
19947     el : "my-element",
19948     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19949  
19950     singleSelect: true,
19951     selectedClass: "ydataview-selected",
19952     store: store
19953  });
19954
19955  // listen for node click?
19956  view.on("click", function(vw, index, node, e){
19957  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19958  });
19959
19960  // load XML data
19961  dataModel.load("foobar.xml");
19962  </code></pre>
19963  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19964  * <br><br>
19965  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19966  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19967  * 
19968  * Note: old style constructor is still suported (container, template, config)
19969  * 
19970  * @constructor
19971  * Create a new View
19972  * @param {Object} config The config object
19973  * 
19974  */
19975 Roo.View = function(config, depreciated_tpl, depreciated_config){
19976     
19977     this.parent = false;
19978     
19979     if (typeof(depreciated_tpl) == 'undefined') {
19980         // new way.. - universal constructor.
19981         Roo.apply(this, config);
19982         this.el  = Roo.get(this.el);
19983     } else {
19984         // old format..
19985         this.el  = Roo.get(config);
19986         this.tpl = depreciated_tpl;
19987         Roo.apply(this, depreciated_config);
19988     }
19989     this.wrapEl  = this.el.wrap().wrap();
19990     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19991     
19992     
19993     if(typeof(this.tpl) == "string"){
19994         this.tpl = new Roo.Template(this.tpl);
19995     } else {
19996         // support xtype ctors..
19997         this.tpl = new Roo.factory(this.tpl, Roo);
19998     }
19999     
20000     
20001     this.tpl.compile();
20002     
20003     /** @private */
20004     this.addEvents({
20005         /**
20006          * @event beforeclick
20007          * Fires before a click is processed. Returns false to cancel the default action.
20008          * @param {Roo.View} this
20009          * @param {Number} index The index of the target node
20010          * @param {HTMLElement} node The target node
20011          * @param {Roo.EventObject} e The raw event object
20012          */
20013             "beforeclick" : true,
20014         /**
20015          * @event click
20016          * Fires when a template node is clicked.
20017          * @param {Roo.View} this
20018          * @param {Number} index The index of the target node
20019          * @param {HTMLElement} node The target node
20020          * @param {Roo.EventObject} e The raw event object
20021          */
20022             "click" : true,
20023         /**
20024          * @event dblclick
20025          * Fires when a template node is double clicked.
20026          * @param {Roo.View} this
20027          * @param {Number} index The index of the target node
20028          * @param {HTMLElement} node The target node
20029          * @param {Roo.EventObject} e The raw event object
20030          */
20031             "dblclick" : true,
20032         /**
20033          * @event contextmenu
20034          * Fires when a template node is right clicked.
20035          * @param {Roo.View} this
20036          * @param {Number} index The index of the target node
20037          * @param {HTMLElement} node The target node
20038          * @param {Roo.EventObject} e The raw event object
20039          */
20040             "contextmenu" : true,
20041         /**
20042          * @event selectionchange
20043          * Fires when the selected nodes change.
20044          * @param {Roo.View} this
20045          * @param {Array} selections Array of the selected nodes
20046          */
20047             "selectionchange" : true,
20048     
20049         /**
20050          * @event beforeselect
20051          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20052          * @param {Roo.View} this
20053          * @param {HTMLElement} node The node to be selected
20054          * @param {Array} selections Array of currently selected nodes
20055          */
20056             "beforeselect" : true,
20057         /**
20058          * @event preparedata
20059          * Fires on every row to render, to allow you to change the data.
20060          * @param {Roo.View} this
20061          * @param {Object} data to be rendered (change this)
20062          */
20063           "preparedata" : true
20064           
20065           
20066         });
20067
20068
20069
20070     this.el.on({
20071         "click": this.onClick,
20072         "dblclick": this.onDblClick,
20073         "contextmenu": this.onContextMenu,
20074         scope:this
20075     });
20076
20077     this.selections = [];
20078     this.nodes = [];
20079     this.cmp = new Roo.CompositeElementLite([]);
20080     if(this.store){
20081         this.store = Roo.factory(this.store, Roo.data);
20082         this.setStore(this.store, true);
20083     }
20084     
20085     if ( this.footer && this.footer.xtype) {
20086            
20087          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20088         
20089         this.footer.dataSource = this.store;
20090         this.footer.container = fctr;
20091         this.footer = Roo.factory(this.footer, Roo);
20092         fctr.insertFirst(this.el);
20093         
20094         // this is a bit insane - as the paging toolbar seems to detach the el..
20095 //        dom.parentNode.parentNode.parentNode
20096          // they get detached?
20097     }
20098     
20099     
20100     Roo.View.superclass.constructor.call(this);
20101     
20102     
20103 };
20104
20105 Roo.extend(Roo.View, Roo.util.Observable, {
20106     
20107      /**
20108      * @cfg {Roo.data.Store} store Data store to load data from.
20109      */
20110     store : false,
20111     
20112     /**
20113      * @cfg {String|Roo.Element} el The container element.
20114      */
20115     el : '',
20116     
20117     /**
20118      * @cfg {String|Roo.Template} tpl The template used by this View 
20119      */
20120     tpl : false,
20121     /**
20122      * @cfg {String} dataName the named area of the template to use as the data area
20123      *                          Works with domtemplates roo-name="name"
20124      */
20125     dataName: false,
20126     /**
20127      * @cfg {String} selectedClass The css class to add to selected nodes
20128      */
20129     selectedClass : "x-view-selected",
20130      /**
20131      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20132      */
20133     emptyText : "",
20134     
20135     /**
20136      * @cfg {String} text to display on mask (default Loading)
20137      */
20138     mask : false,
20139     /**
20140      * @cfg {Boolean} multiSelect Allow multiple selection
20141      */
20142     multiSelect : false,
20143     /**
20144      * @cfg {Boolean} singleSelect Allow single selection
20145      */
20146     singleSelect:  false,
20147     
20148     /**
20149      * @cfg {Boolean} toggleSelect - selecting 
20150      */
20151     toggleSelect : false,
20152     
20153     /**
20154      * @cfg {Boolean} tickable - selecting 
20155      */
20156     tickable : false,
20157     
20158     /**
20159      * Returns the element this view is bound to.
20160      * @return {Roo.Element}
20161      */
20162     getEl : function(){
20163         return this.wrapEl;
20164     },
20165     
20166     
20167
20168     /**
20169      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20170      */
20171     refresh : function(){
20172         //Roo.log('refresh');
20173         var t = this.tpl;
20174         
20175         // if we are using something like 'domtemplate', then
20176         // the what gets used is:
20177         // t.applySubtemplate(NAME, data, wrapping data..)
20178         // the outer template then get' applied with
20179         //     the store 'extra data'
20180         // and the body get's added to the
20181         //      roo-name="data" node?
20182         //      <span class='roo-tpl-{name}'></span> ?????
20183         
20184         
20185         
20186         this.clearSelections();
20187         this.el.update("");
20188         var html = [];
20189         var records = this.store.getRange();
20190         if(records.length < 1) {
20191             
20192             // is this valid??  = should it render a template??
20193             
20194             this.el.update(this.emptyText);
20195             return;
20196         }
20197         var el = this.el;
20198         if (this.dataName) {
20199             this.el.update(t.apply(this.store.meta)); //????
20200             el = this.el.child('.roo-tpl-' + this.dataName);
20201         }
20202         
20203         for(var i = 0, len = records.length; i < len; i++){
20204             var data = this.prepareData(records[i].data, i, records[i]);
20205             this.fireEvent("preparedata", this, data, i, records[i]);
20206             
20207             var d = Roo.apply({}, data);
20208             
20209             if(this.tickable){
20210                 Roo.apply(d, {'roo-id' : Roo.id()});
20211                 
20212                 var _this = this;
20213             
20214                 Roo.each(this.parent.item, function(item){
20215                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20216                         return;
20217                     }
20218                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20219                 });
20220             }
20221             
20222             html[html.length] = Roo.util.Format.trim(
20223                 this.dataName ?
20224                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20225                     t.apply(d)
20226             );
20227         }
20228         
20229         
20230         
20231         el.update(html.join(""));
20232         this.nodes = el.dom.childNodes;
20233         this.updateIndexes(0);
20234     },
20235     
20236
20237     /**
20238      * Function to override to reformat the data that is sent to
20239      * the template for each node.
20240      * DEPRICATED - use the preparedata event handler.
20241      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20242      * a JSON object for an UpdateManager bound view).
20243      */
20244     prepareData : function(data, index, record)
20245     {
20246         this.fireEvent("preparedata", this, data, index, record);
20247         return data;
20248     },
20249
20250     onUpdate : function(ds, record){
20251         // Roo.log('on update');   
20252         this.clearSelections();
20253         var index = this.store.indexOf(record);
20254         var n = this.nodes[index];
20255         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20256         n.parentNode.removeChild(n);
20257         this.updateIndexes(index, index);
20258     },
20259
20260     
20261     
20262 // --------- FIXME     
20263     onAdd : function(ds, records, index)
20264     {
20265         //Roo.log(['on Add', ds, records, index] );        
20266         this.clearSelections();
20267         if(this.nodes.length == 0){
20268             this.refresh();
20269             return;
20270         }
20271         var n = this.nodes[index];
20272         for(var i = 0, len = records.length; i < len; i++){
20273             var d = this.prepareData(records[i].data, i, records[i]);
20274             if(n){
20275                 this.tpl.insertBefore(n, d);
20276             }else{
20277                 
20278                 this.tpl.append(this.el, d);
20279             }
20280         }
20281         this.updateIndexes(index);
20282     },
20283
20284     onRemove : function(ds, record, index){
20285        // Roo.log('onRemove');
20286         this.clearSelections();
20287         var el = this.dataName  ?
20288             this.el.child('.roo-tpl-' + this.dataName) :
20289             this.el; 
20290         
20291         el.dom.removeChild(this.nodes[index]);
20292         this.updateIndexes(index);
20293     },
20294
20295     /**
20296      * Refresh an individual node.
20297      * @param {Number} index
20298      */
20299     refreshNode : function(index){
20300         this.onUpdate(this.store, this.store.getAt(index));
20301     },
20302
20303     updateIndexes : function(startIndex, endIndex){
20304         var ns = this.nodes;
20305         startIndex = startIndex || 0;
20306         endIndex = endIndex || ns.length - 1;
20307         for(var i = startIndex; i <= endIndex; i++){
20308             ns[i].nodeIndex = i;
20309         }
20310     },
20311
20312     /**
20313      * Changes the data store this view uses and refresh the view.
20314      * @param {Store} store
20315      */
20316     setStore : function(store, initial){
20317         if(!initial && this.store){
20318             this.store.un("datachanged", this.refresh);
20319             this.store.un("add", this.onAdd);
20320             this.store.un("remove", this.onRemove);
20321             this.store.un("update", this.onUpdate);
20322             this.store.un("clear", this.refresh);
20323             this.store.un("beforeload", this.onBeforeLoad);
20324             this.store.un("load", this.onLoad);
20325             this.store.un("loadexception", this.onLoad);
20326         }
20327         if(store){
20328           
20329             store.on("datachanged", this.refresh, this);
20330             store.on("add", this.onAdd, this);
20331             store.on("remove", this.onRemove, this);
20332             store.on("update", this.onUpdate, this);
20333             store.on("clear", this.refresh, this);
20334             store.on("beforeload", this.onBeforeLoad, this);
20335             store.on("load", this.onLoad, this);
20336             store.on("loadexception", this.onLoad, this);
20337         }
20338         
20339         if(store){
20340             this.refresh();
20341         }
20342     },
20343     /**
20344      * onbeforeLoad - masks the loading area.
20345      *
20346      */
20347     onBeforeLoad : function(store,opts)
20348     {
20349          //Roo.log('onBeforeLoad');   
20350         if (!opts.add) {
20351             this.el.update("");
20352         }
20353         this.el.mask(this.mask ? this.mask : "Loading" ); 
20354     },
20355     onLoad : function ()
20356     {
20357         this.el.unmask();
20358     },
20359     
20360
20361     /**
20362      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20363      * @param {HTMLElement} node
20364      * @return {HTMLElement} The template node
20365      */
20366     findItemFromChild : function(node){
20367         var el = this.dataName  ?
20368             this.el.child('.roo-tpl-' + this.dataName,true) :
20369             this.el.dom; 
20370         
20371         if(!node || node.parentNode == el){
20372                     return node;
20373             }
20374             var p = node.parentNode;
20375             while(p && p != el){
20376             if(p.parentNode == el){
20377                 return p;
20378             }
20379             p = p.parentNode;
20380         }
20381             return null;
20382     },
20383
20384     /** @ignore */
20385     onClick : function(e){
20386         var item = this.findItemFromChild(e.getTarget());
20387         if(item){
20388             var index = this.indexOf(item);
20389             if(this.onItemClick(item, index, e) !== false){
20390                 this.fireEvent("click", this, index, item, e);
20391             }
20392         }else{
20393             this.clearSelections();
20394         }
20395     },
20396
20397     /** @ignore */
20398     onContextMenu : function(e){
20399         var item = this.findItemFromChild(e.getTarget());
20400         if(item){
20401             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20402         }
20403     },
20404
20405     /** @ignore */
20406     onDblClick : function(e){
20407         var item = this.findItemFromChild(e.getTarget());
20408         if(item){
20409             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20410         }
20411     },
20412
20413     onItemClick : function(item, index, e)
20414     {
20415         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20416             return false;
20417         }
20418         if (this.toggleSelect) {
20419             var m = this.isSelected(item) ? 'unselect' : 'select';
20420             //Roo.log(m);
20421             var _t = this;
20422             _t[m](item, true, false);
20423             return true;
20424         }
20425         if(this.multiSelect || this.singleSelect){
20426             if(this.multiSelect && e.shiftKey && this.lastSelection){
20427                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20428             }else{
20429                 this.select(item, this.multiSelect && e.ctrlKey);
20430                 this.lastSelection = item;
20431             }
20432             
20433             if(!this.tickable){
20434                 e.preventDefault();
20435             }
20436             
20437         }
20438         return true;
20439     },
20440
20441     /**
20442      * Get the number of selected nodes.
20443      * @return {Number}
20444      */
20445     getSelectionCount : function(){
20446         return this.selections.length;
20447     },
20448
20449     /**
20450      * Get the currently selected nodes.
20451      * @return {Array} An array of HTMLElements
20452      */
20453     getSelectedNodes : function(){
20454         return this.selections;
20455     },
20456
20457     /**
20458      * Get the indexes of the selected nodes.
20459      * @return {Array}
20460      */
20461     getSelectedIndexes : function(){
20462         var indexes = [], s = this.selections;
20463         for(var i = 0, len = s.length; i < len; i++){
20464             indexes.push(s[i].nodeIndex);
20465         }
20466         return indexes;
20467     },
20468
20469     /**
20470      * Clear all selections
20471      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20472      */
20473     clearSelections : function(suppressEvent){
20474         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20475             this.cmp.elements = this.selections;
20476             this.cmp.removeClass(this.selectedClass);
20477             this.selections = [];
20478             if(!suppressEvent){
20479                 this.fireEvent("selectionchange", this, this.selections);
20480             }
20481         }
20482     },
20483
20484     /**
20485      * Returns true if the passed node is selected
20486      * @param {HTMLElement/Number} node The node or node index
20487      * @return {Boolean}
20488      */
20489     isSelected : function(node){
20490         var s = this.selections;
20491         if(s.length < 1){
20492             return false;
20493         }
20494         node = this.getNode(node);
20495         return s.indexOf(node) !== -1;
20496     },
20497
20498     /**
20499      * Selects nodes.
20500      * @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
20501      * @param {Boolean} keepExisting (optional) true to keep existing selections
20502      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20503      */
20504     select : function(nodeInfo, keepExisting, suppressEvent){
20505         if(nodeInfo instanceof Array){
20506             if(!keepExisting){
20507                 this.clearSelections(true);
20508             }
20509             for(var i = 0, len = nodeInfo.length; i < len; i++){
20510                 this.select(nodeInfo[i], true, true);
20511             }
20512             return;
20513         } 
20514         var node = this.getNode(nodeInfo);
20515         if(!node || this.isSelected(node)){
20516             return; // already selected.
20517         }
20518         if(!keepExisting){
20519             this.clearSelections(true);
20520         }
20521         
20522         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20523             Roo.fly(node).addClass(this.selectedClass);
20524             this.selections.push(node);
20525             if(!suppressEvent){
20526                 this.fireEvent("selectionchange", this, this.selections);
20527             }
20528         }
20529         
20530         
20531     },
20532       /**
20533      * Unselects nodes.
20534      * @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
20535      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20536      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20537      */
20538     unselect : function(nodeInfo, keepExisting, suppressEvent)
20539     {
20540         if(nodeInfo instanceof Array){
20541             Roo.each(this.selections, function(s) {
20542                 this.unselect(s, nodeInfo);
20543             }, this);
20544             return;
20545         }
20546         var node = this.getNode(nodeInfo);
20547         if(!node || !this.isSelected(node)){
20548             //Roo.log("not selected");
20549             return; // not selected.
20550         }
20551         // fireevent???
20552         var ns = [];
20553         Roo.each(this.selections, function(s) {
20554             if (s == node ) {
20555                 Roo.fly(node).removeClass(this.selectedClass);
20556
20557                 return;
20558             }
20559             ns.push(s);
20560         },this);
20561         
20562         this.selections= ns;
20563         this.fireEvent("selectionchange", this, this.selections);
20564     },
20565
20566     /**
20567      * Gets a template node.
20568      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20569      * @return {HTMLElement} The node or null if it wasn't found
20570      */
20571     getNode : function(nodeInfo){
20572         if(typeof nodeInfo == "string"){
20573             return document.getElementById(nodeInfo);
20574         }else if(typeof nodeInfo == "number"){
20575             return this.nodes[nodeInfo];
20576         }
20577         return nodeInfo;
20578     },
20579
20580     /**
20581      * Gets a range template nodes.
20582      * @param {Number} startIndex
20583      * @param {Number} endIndex
20584      * @return {Array} An array of nodes
20585      */
20586     getNodes : function(start, end){
20587         var ns = this.nodes;
20588         start = start || 0;
20589         end = typeof end == "undefined" ? ns.length - 1 : end;
20590         var nodes = [];
20591         if(start <= end){
20592             for(var i = start; i <= end; i++){
20593                 nodes.push(ns[i]);
20594             }
20595         } else{
20596             for(var i = start; i >= end; i--){
20597                 nodes.push(ns[i]);
20598             }
20599         }
20600         return nodes;
20601     },
20602
20603     /**
20604      * Finds the index of the passed node
20605      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20606      * @return {Number} The index of the node or -1
20607      */
20608     indexOf : function(node){
20609         node = this.getNode(node);
20610         if(typeof node.nodeIndex == "number"){
20611             return node.nodeIndex;
20612         }
20613         var ns = this.nodes;
20614         for(var i = 0, len = ns.length; i < len; i++){
20615             if(ns[i] == node){
20616                 return i;
20617             }
20618         }
20619         return -1;
20620     }
20621 });
20622 /*
20623  * - LGPL
20624  *
20625  * based on jquery fullcalendar
20626  * 
20627  */
20628
20629 Roo.bootstrap = Roo.bootstrap || {};
20630 /**
20631  * @class Roo.bootstrap.Calendar
20632  * @extends Roo.bootstrap.Component
20633  * Bootstrap Calendar class
20634  * @cfg {Boolean} loadMask (true|false) default false
20635  * @cfg {Object} header generate the user specific header of the calendar, default false
20636
20637  * @constructor
20638  * Create a new Container
20639  * @param {Object} config The config object
20640  */
20641
20642
20643
20644 Roo.bootstrap.Calendar = function(config){
20645     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20646      this.addEvents({
20647         /**
20648              * @event select
20649              * Fires when a date is selected
20650              * @param {DatePicker} this
20651              * @param {Date} date The selected date
20652              */
20653         'select': true,
20654         /**
20655              * @event monthchange
20656              * Fires when the displayed month changes 
20657              * @param {DatePicker} this
20658              * @param {Date} date The selected month
20659              */
20660         'monthchange': true,
20661         /**
20662              * @event evententer
20663              * Fires when mouse over an event
20664              * @param {Calendar} this
20665              * @param {event} Event
20666              */
20667         'evententer': true,
20668         /**
20669              * @event eventleave
20670              * Fires when the mouse leaves an
20671              * @param {Calendar} this
20672              * @param {event}
20673              */
20674         'eventleave': true,
20675         /**
20676              * @event eventclick
20677              * Fires when the mouse click an
20678              * @param {Calendar} this
20679              * @param {event}
20680              */
20681         'eventclick': true
20682         
20683     });
20684
20685 };
20686
20687 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20688     
20689           /**
20690      * @cfg {Roo.data.Store} store
20691      * The data source for the calendar
20692      */
20693         store : false,
20694      /**
20695      * @cfg {Number} startDay
20696      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20697      */
20698     startDay : 0,
20699     
20700     loadMask : false,
20701     
20702     header : false,
20703       
20704     getAutoCreate : function(){
20705         
20706         
20707         var fc_button = function(name, corner, style, content ) {
20708             return Roo.apply({},{
20709                 tag : 'span',
20710                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20711                          (corner.length ?
20712                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20713                             ''
20714                         ),
20715                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20716                 unselectable: 'on'
20717             });
20718         };
20719         
20720         var header = {};
20721         
20722         if(!this.header){
20723             header = {
20724                 tag : 'table',
20725                 cls : 'fc-header',
20726                 style : 'width:100%',
20727                 cn : [
20728                     {
20729                         tag: 'tr',
20730                         cn : [
20731                             {
20732                                 tag : 'td',
20733                                 cls : 'fc-header-left',
20734                                 cn : [
20735                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20736                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20737                                     { tag: 'span', cls: 'fc-header-space' },
20738                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20739
20740
20741                                 ]
20742                             },
20743
20744                             {
20745                                 tag : 'td',
20746                                 cls : 'fc-header-center',
20747                                 cn : [
20748                                     {
20749                                         tag: 'span',
20750                                         cls: 'fc-header-title',
20751                                         cn : {
20752                                             tag: 'H2',
20753                                             html : 'month / year'
20754                                         }
20755                                     }
20756
20757                                 ]
20758                             },
20759                             {
20760                                 tag : 'td',
20761                                 cls : 'fc-header-right',
20762                                 cn : [
20763                               /*      fc_button('month', 'left', '', 'month' ),
20764                                     fc_button('week', '', '', 'week' ),
20765                                     fc_button('day', 'right', '', 'day' )
20766                                 */    
20767
20768                                 ]
20769                             }
20770
20771                         ]
20772                     }
20773                 ]
20774             };
20775         }
20776         
20777         header = this.header;
20778         
20779        
20780         var cal_heads = function() {
20781             var ret = [];
20782             // fixme - handle this.
20783             
20784             for (var i =0; i < Date.dayNames.length; i++) {
20785                 var d = Date.dayNames[i];
20786                 ret.push({
20787                     tag: 'th',
20788                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20789                     html : d.substring(0,3)
20790                 });
20791                 
20792             }
20793             ret[0].cls += ' fc-first';
20794             ret[6].cls += ' fc-last';
20795             return ret;
20796         };
20797         var cal_cell = function(n) {
20798             return  {
20799                 tag: 'td',
20800                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20801                 cn : [
20802                     {
20803                         cn : [
20804                             {
20805                                 cls: 'fc-day-number',
20806                                 html: 'D'
20807                             },
20808                             {
20809                                 cls: 'fc-day-content',
20810                              
20811                                 cn : [
20812                                      {
20813                                         style: 'position: relative;' // height: 17px;
20814                                     }
20815                                 ]
20816                             }
20817                             
20818                             
20819                         ]
20820                     }
20821                 ]
20822                 
20823             }
20824         };
20825         var cal_rows = function() {
20826             
20827             var ret = [];
20828             for (var r = 0; r < 6; r++) {
20829                 var row= {
20830                     tag : 'tr',
20831                     cls : 'fc-week',
20832                     cn : []
20833                 };
20834                 
20835                 for (var i =0; i < Date.dayNames.length; i++) {
20836                     var d = Date.dayNames[i];
20837                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20838
20839                 }
20840                 row.cn[0].cls+=' fc-first';
20841                 row.cn[0].cn[0].style = 'min-height:90px';
20842                 row.cn[6].cls+=' fc-last';
20843                 ret.push(row);
20844                 
20845             }
20846             ret[0].cls += ' fc-first';
20847             ret[4].cls += ' fc-prev-last';
20848             ret[5].cls += ' fc-last';
20849             return ret;
20850             
20851         };
20852         
20853         var cal_table = {
20854             tag: 'table',
20855             cls: 'fc-border-separate',
20856             style : 'width:100%',
20857             cellspacing  : 0,
20858             cn : [
20859                 { 
20860                     tag: 'thead',
20861                     cn : [
20862                         { 
20863                             tag: 'tr',
20864                             cls : 'fc-first fc-last',
20865                             cn : cal_heads()
20866                         }
20867                     ]
20868                 },
20869                 { 
20870                     tag: 'tbody',
20871                     cn : cal_rows()
20872                 }
20873                   
20874             ]
20875         };
20876          
20877          var cfg = {
20878             cls : 'fc fc-ltr',
20879             cn : [
20880                 header,
20881                 {
20882                     cls : 'fc-content',
20883                     style : "position: relative;",
20884                     cn : [
20885                         {
20886                             cls : 'fc-view fc-view-month fc-grid',
20887                             style : 'position: relative',
20888                             unselectable : 'on',
20889                             cn : [
20890                                 {
20891                                     cls : 'fc-event-container',
20892                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20893                                 },
20894                                 cal_table
20895                             ]
20896                         }
20897                     ]
20898     
20899                 }
20900            ] 
20901             
20902         };
20903         
20904          
20905         
20906         return cfg;
20907     },
20908     
20909     
20910     initEvents : function()
20911     {
20912         if(!this.store){
20913             throw "can not find store for calendar";
20914         }
20915         
20916         var mark = {
20917             tag: "div",
20918             cls:"x-dlg-mask",
20919             style: "text-align:center",
20920             cn: [
20921                 {
20922                     tag: "div",
20923                     style: "background-color:white;width:50%;margin:250 auto",
20924                     cn: [
20925                         {
20926                             tag: "img",
20927                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20928                         },
20929                         {
20930                             tag: "span",
20931                             html: "Loading"
20932                         }
20933                         
20934                     ]
20935                 }
20936             ]
20937         };
20938         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20939         
20940         var size = this.el.select('.fc-content', true).first().getSize();
20941         this.maskEl.setSize(size.width, size.height);
20942         this.maskEl.enableDisplayMode("block");
20943         if(!this.loadMask){
20944             this.maskEl.hide();
20945         }
20946         
20947         this.store = Roo.factory(this.store, Roo.data);
20948         this.store.on('load', this.onLoad, this);
20949         this.store.on('beforeload', this.onBeforeLoad, this);
20950         
20951         this.resize();
20952         
20953         this.cells = this.el.select('.fc-day',true);
20954         //Roo.log(this.cells);
20955         this.textNodes = this.el.query('.fc-day-number');
20956         this.cells.addClassOnOver('fc-state-hover');
20957         
20958         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20959         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20960         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20961         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20962         
20963         this.on('monthchange', this.onMonthChange, this);
20964         
20965         this.update(new Date().clearTime());
20966     },
20967     
20968     resize : function() {
20969         var sz  = this.el.getSize();
20970         
20971         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20972         this.el.select('.fc-day-content div',true).setHeight(34);
20973     },
20974     
20975     
20976     // private
20977     showPrevMonth : function(e){
20978         this.update(this.activeDate.add("mo", -1));
20979     },
20980     showToday : function(e){
20981         this.update(new Date().clearTime());
20982     },
20983     // private
20984     showNextMonth : function(e){
20985         this.update(this.activeDate.add("mo", 1));
20986     },
20987
20988     // private
20989     showPrevYear : function(){
20990         this.update(this.activeDate.add("y", -1));
20991     },
20992
20993     // private
20994     showNextYear : function(){
20995         this.update(this.activeDate.add("y", 1));
20996     },
20997
20998     
20999    // private
21000     update : function(date)
21001     {
21002         var vd = this.activeDate;
21003         this.activeDate = date;
21004 //        if(vd && this.el){
21005 //            var t = date.getTime();
21006 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21007 //                Roo.log('using add remove');
21008 //                
21009 //                this.fireEvent('monthchange', this, date);
21010 //                
21011 //                this.cells.removeClass("fc-state-highlight");
21012 //                this.cells.each(function(c){
21013 //                   if(c.dateValue == t){
21014 //                       c.addClass("fc-state-highlight");
21015 //                       setTimeout(function(){
21016 //                            try{c.dom.firstChild.focus();}catch(e){}
21017 //                       }, 50);
21018 //                       return false;
21019 //                   }
21020 //                   return true;
21021 //                });
21022 //                return;
21023 //            }
21024 //        }
21025         
21026         var days = date.getDaysInMonth();
21027         
21028         var firstOfMonth = date.getFirstDateOfMonth();
21029         var startingPos = firstOfMonth.getDay()-this.startDay;
21030         
21031         if(startingPos < this.startDay){
21032             startingPos += 7;
21033         }
21034         
21035         var pm = date.add(Date.MONTH, -1);
21036         var prevStart = pm.getDaysInMonth()-startingPos;
21037 //        
21038         this.cells = this.el.select('.fc-day',true);
21039         this.textNodes = this.el.query('.fc-day-number');
21040         this.cells.addClassOnOver('fc-state-hover');
21041         
21042         var cells = this.cells.elements;
21043         var textEls = this.textNodes;
21044         
21045         Roo.each(cells, function(cell){
21046             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21047         });
21048         
21049         days += startingPos;
21050
21051         // convert everything to numbers so it's fast
21052         var day = 86400000;
21053         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21054         //Roo.log(d);
21055         //Roo.log(pm);
21056         //Roo.log(prevStart);
21057         
21058         var today = new Date().clearTime().getTime();
21059         var sel = date.clearTime().getTime();
21060         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21061         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21062         var ddMatch = this.disabledDatesRE;
21063         var ddText = this.disabledDatesText;
21064         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21065         var ddaysText = this.disabledDaysText;
21066         var format = this.format;
21067         
21068         var setCellClass = function(cal, cell){
21069             cell.row = 0;
21070             cell.events = [];
21071             cell.more = [];
21072             //Roo.log('set Cell Class');
21073             cell.title = "";
21074             var t = d.getTime();
21075             
21076             //Roo.log(d);
21077             
21078             cell.dateValue = t;
21079             if(t == today){
21080                 cell.className += " fc-today";
21081                 cell.className += " fc-state-highlight";
21082                 cell.title = cal.todayText;
21083             }
21084             if(t == sel){
21085                 // disable highlight in other month..
21086                 //cell.className += " fc-state-highlight";
21087                 
21088             }
21089             // disabling
21090             if(t < min) {
21091                 cell.className = " fc-state-disabled";
21092                 cell.title = cal.minText;
21093                 return;
21094             }
21095             if(t > max) {
21096                 cell.className = " fc-state-disabled";
21097                 cell.title = cal.maxText;
21098                 return;
21099             }
21100             if(ddays){
21101                 if(ddays.indexOf(d.getDay()) != -1){
21102                     cell.title = ddaysText;
21103                     cell.className = " fc-state-disabled";
21104                 }
21105             }
21106             if(ddMatch && format){
21107                 var fvalue = d.dateFormat(format);
21108                 if(ddMatch.test(fvalue)){
21109                     cell.title = ddText.replace("%0", fvalue);
21110                     cell.className = " fc-state-disabled";
21111                 }
21112             }
21113             
21114             if (!cell.initialClassName) {
21115                 cell.initialClassName = cell.dom.className;
21116             }
21117             
21118             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21119         };
21120
21121         var i = 0;
21122         
21123         for(; i < startingPos; i++) {
21124             textEls[i].innerHTML = (++prevStart);
21125             d.setDate(d.getDate()+1);
21126             
21127             cells[i].className = "fc-past fc-other-month";
21128             setCellClass(this, cells[i]);
21129         }
21130         
21131         var intDay = 0;
21132         
21133         for(; i < days; i++){
21134             intDay = i - startingPos + 1;
21135             textEls[i].innerHTML = (intDay);
21136             d.setDate(d.getDate()+1);
21137             
21138             cells[i].className = ''; // "x-date-active";
21139             setCellClass(this, cells[i]);
21140         }
21141         var extraDays = 0;
21142         
21143         for(; i < 42; i++) {
21144             textEls[i].innerHTML = (++extraDays);
21145             d.setDate(d.getDate()+1);
21146             
21147             cells[i].className = "fc-future fc-other-month";
21148             setCellClass(this, cells[i]);
21149         }
21150         
21151         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21152         
21153         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21154         
21155         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21156         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21157         
21158         if(totalRows != 6){
21159             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21160             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21161         }
21162         
21163         this.fireEvent('monthchange', this, date);
21164         
21165         
21166         /*
21167         if(!this.internalRender){
21168             var main = this.el.dom.firstChild;
21169             var w = main.offsetWidth;
21170             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21171             Roo.fly(main).setWidth(w);
21172             this.internalRender = true;
21173             // opera does not respect the auto grow header center column
21174             // then, after it gets a width opera refuses to recalculate
21175             // without a second pass
21176             if(Roo.isOpera && !this.secondPass){
21177                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21178                 this.secondPass = true;
21179                 this.update.defer(10, this, [date]);
21180             }
21181         }
21182         */
21183         
21184     },
21185     
21186     findCell : function(dt) {
21187         dt = dt.clearTime().getTime();
21188         var ret = false;
21189         this.cells.each(function(c){
21190             //Roo.log("check " +c.dateValue + '?=' + dt);
21191             if(c.dateValue == dt){
21192                 ret = c;
21193                 return false;
21194             }
21195             return true;
21196         });
21197         
21198         return ret;
21199     },
21200     
21201     findCells : function(ev) {
21202         var s = ev.start.clone().clearTime().getTime();
21203        // Roo.log(s);
21204         var e= ev.end.clone().clearTime().getTime();
21205        // Roo.log(e);
21206         var ret = [];
21207         this.cells.each(function(c){
21208              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21209             
21210             if(c.dateValue > e){
21211                 return ;
21212             }
21213             if(c.dateValue < s){
21214                 return ;
21215             }
21216             ret.push(c);
21217         });
21218         
21219         return ret;    
21220     },
21221     
21222 //    findBestRow: function(cells)
21223 //    {
21224 //        var ret = 0;
21225 //        
21226 //        for (var i =0 ; i < cells.length;i++) {
21227 //            ret  = Math.max(cells[i].rows || 0,ret);
21228 //        }
21229 //        return ret;
21230 //        
21231 //    },
21232     
21233     
21234     addItem : function(ev)
21235     {
21236         // look for vertical location slot in
21237         var cells = this.findCells(ev);
21238         
21239 //        ev.row = this.findBestRow(cells);
21240         
21241         // work out the location.
21242         
21243         var crow = false;
21244         var rows = [];
21245         for(var i =0; i < cells.length; i++) {
21246             
21247             cells[i].row = cells[0].row;
21248             
21249             if(i == 0){
21250                 cells[i].row = cells[i].row + 1;
21251             }
21252             
21253             if (!crow) {
21254                 crow = {
21255                     start : cells[i],
21256                     end :  cells[i]
21257                 };
21258                 continue;
21259             }
21260             if (crow.start.getY() == cells[i].getY()) {
21261                 // on same row.
21262                 crow.end = cells[i];
21263                 continue;
21264             }
21265             // different row.
21266             rows.push(crow);
21267             crow = {
21268                 start: cells[i],
21269                 end : cells[i]
21270             };
21271             
21272         }
21273         
21274         rows.push(crow);
21275         ev.els = [];
21276         ev.rows = rows;
21277         ev.cells = cells;
21278         
21279         cells[0].events.push(ev);
21280         
21281         this.calevents.push(ev);
21282     },
21283     
21284     clearEvents: function() {
21285         
21286         if(!this.calevents){
21287             return;
21288         }
21289         
21290         Roo.each(this.cells.elements, function(c){
21291             c.row = 0;
21292             c.events = [];
21293             c.more = [];
21294         });
21295         
21296         Roo.each(this.calevents, function(e) {
21297             Roo.each(e.els, function(el) {
21298                 el.un('mouseenter' ,this.onEventEnter, this);
21299                 el.un('mouseleave' ,this.onEventLeave, this);
21300                 el.remove();
21301             },this);
21302         },this);
21303         
21304         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21305             e.remove();
21306         });
21307         
21308     },
21309     
21310     renderEvents: function()
21311     {   
21312         var _this = this;
21313         
21314         this.cells.each(function(c) {
21315             
21316             if(c.row < 5){
21317                 return;
21318             }
21319             
21320             var ev = c.events;
21321             
21322             var r = 4;
21323             if(c.row != c.events.length){
21324                 r = 4 - (4 - (c.row - c.events.length));
21325             }
21326             
21327             c.events = ev.slice(0, r);
21328             c.more = ev.slice(r);
21329             
21330             if(c.more.length && c.more.length == 1){
21331                 c.events.push(c.more.pop());
21332             }
21333             
21334             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21335             
21336         });
21337             
21338         this.cells.each(function(c) {
21339             
21340             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21341             
21342             
21343             for (var e = 0; e < c.events.length; e++){
21344                 var ev = c.events[e];
21345                 var rows = ev.rows;
21346                 
21347                 for(var i = 0; i < rows.length; i++) {
21348                 
21349                     // how many rows should it span..
21350
21351                     var  cfg = {
21352                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21353                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21354
21355                         unselectable : "on",
21356                         cn : [
21357                             {
21358                                 cls: 'fc-event-inner',
21359                                 cn : [
21360     //                                {
21361     //                                  tag:'span',
21362     //                                  cls: 'fc-event-time',
21363     //                                  html : cells.length > 1 ? '' : ev.time
21364     //                                },
21365                                     {
21366                                       tag:'span',
21367                                       cls: 'fc-event-title',
21368                                       html : String.format('{0}', ev.title)
21369                                     }
21370
21371
21372                                 ]
21373                             },
21374                             {
21375                                 cls: 'ui-resizable-handle ui-resizable-e',
21376                                 html : '&nbsp;&nbsp;&nbsp'
21377                             }
21378
21379                         ]
21380                     };
21381
21382                     if (i == 0) {
21383                         cfg.cls += ' fc-event-start';
21384                     }
21385                     if ((i+1) == rows.length) {
21386                         cfg.cls += ' fc-event-end';
21387                     }
21388
21389                     var ctr = _this.el.select('.fc-event-container',true).first();
21390                     var cg = ctr.createChild(cfg);
21391
21392                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21393                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21394
21395                     var r = (c.more.length) ? 1 : 0;
21396                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21397                     cg.setWidth(ebox.right - sbox.x -2);
21398
21399                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21400                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21401                     cg.on('click', _this.onEventClick, _this, ev);
21402
21403                     ev.els.push(cg);
21404                     
21405                 }
21406                 
21407             }
21408             
21409             
21410             if(c.more.length){
21411                 var  cfg = {
21412                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21413                     style : 'position: absolute',
21414                     unselectable : "on",
21415                     cn : [
21416                         {
21417                             cls: 'fc-event-inner',
21418                             cn : [
21419                                 {
21420                                   tag:'span',
21421                                   cls: 'fc-event-title',
21422                                   html : 'More'
21423                                 }
21424
21425
21426                             ]
21427                         },
21428                         {
21429                             cls: 'ui-resizable-handle ui-resizable-e',
21430                             html : '&nbsp;&nbsp;&nbsp'
21431                         }
21432
21433                     ]
21434                 };
21435
21436                 var ctr = _this.el.select('.fc-event-container',true).first();
21437                 var cg = ctr.createChild(cfg);
21438
21439                 var sbox = c.select('.fc-day-content',true).first().getBox();
21440                 var ebox = c.select('.fc-day-content',true).first().getBox();
21441                 //Roo.log(cg);
21442                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21443                 cg.setWidth(ebox.right - sbox.x -2);
21444
21445                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21446                 
21447             }
21448             
21449         });
21450         
21451         
21452         
21453     },
21454     
21455     onEventEnter: function (e, el,event,d) {
21456         this.fireEvent('evententer', this, el, event);
21457     },
21458     
21459     onEventLeave: function (e, el,event,d) {
21460         this.fireEvent('eventleave', this, el, event);
21461     },
21462     
21463     onEventClick: function (e, el,event,d) {
21464         this.fireEvent('eventclick', this, el, event);
21465     },
21466     
21467     onMonthChange: function () {
21468         this.store.load();
21469     },
21470     
21471     onMoreEventClick: function(e, el, more)
21472     {
21473         var _this = this;
21474         
21475         this.calpopover.placement = 'right';
21476         this.calpopover.setTitle('More');
21477         
21478         this.calpopover.setContent('');
21479         
21480         var ctr = this.calpopover.el.select('.popover-content', true).first();
21481         
21482         Roo.each(more, function(m){
21483             var cfg = {
21484                 cls : 'fc-event-hori fc-event-draggable',
21485                 html : m.title
21486             };
21487             var cg = ctr.createChild(cfg);
21488             
21489             cg.on('click', _this.onEventClick, _this, m);
21490         });
21491         
21492         this.calpopover.show(el);
21493         
21494         
21495     },
21496     
21497     onLoad: function () 
21498     {   
21499         this.calevents = [];
21500         var cal = this;
21501         
21502         if(this.store.getCount() > 0){
21503             this.store.data.each(function(d){
21504                cal.addItem({
21505                     id : d.data.id,
21506                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21507                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21508                     time : d.data.start_time,
21509                     title : d.data.title,
21510                     description : d.data.description,
21511                     venue : d.data.venue
21512                 });
21513             });
21514         }
21515         
21516         this.renderEvents();
21517         
21518         if(this.calevents.length && this.loadMask){
21519             this.maskEl.hide();
21520         }
21521     },
21522     
21523     onBeforeLoad: function()
21524     {
21525         this.clearEvents();
21526         if(this.loadMask){
21527             this.maskEl.show();
21528         }
21529     }
21530 });
21531
21532  
21533  /*
21534  * - LGPL
21535  *
21536  * element
21537  * 
21538  */
21539
21540 /**
21541  * @class Roo.bootstrap.Popover
21542  * @extends Roo.bootstrap.Component
21543  * @parent none builder
21544  * @children Roo.bootstrap.Component
21545  * Bootstrap Popover class
21546  * @cfg {String} html contents of the popover   (or false to use children..)
21547  * @cfg {String} title of popover (or false to hide)
21548  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21549  * @cfg {String} trigger click || hover (or false to trigger manually)
21550  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21551  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21552  *      - if false and it has a 'parent' then it will be automatically added to that element
21553  *      - if string - Roo.get  will be called 
21554  * @cfg {Number} delay - delay before showing
21555  
21556  * @constructor
21557  * Create a new Popover
21558  * @param {Object} config The config object
21559  */
21560
21561 Roo.bootstrap.Popover = function(config){
21562     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21563     
21564     this.addEvents({
21565         // raw events
21566          /**
21567          * @event show
21568          * After the popover show
21569          * 
21570          * @param {Roo.bootstrap.Popover} this
21571          */
21572         "show" : true,
21573         /**
21574          * @event hide
21575          * After the popover hide
21576          * 
21577          * @param {Roo.bootstrap.Popover} this
21578          */
21579         "hide" : true
21580     });
21581 };
21582
21583 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21584     
21585     title: false,
21586     html: false,
21587     
21588     placement : 'right',
21589     trigger : 'hover', // hover
21590     modal : false,
21591     delay : 0,
21592     
21593     over: false,
21594     
21595     can_build_overlaid : false,
21596     
21597     maskEl : false, // the mask element
21598     headerEl : false,
21599     contentEl : false,
21600     alignEl : false, // when show is called with an element - this get's stored.
21601     
21602     getChildContainer : function()
21603     {
21604         return this.contentEl;
21605         
21606     },
21607     getPopoverHeader : function()
21608     {
21609         this.title = true; // flag not to hide it..
21610         this.headerEl.addClass('p-0');
21611         return this.headerEl
21612     },
21613     
21614     
21615     getAutoCreate : function(){
21616          
21617         var cfg = {
21618            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21619            style: 'display:block',
21620            cn : [
21621                 {
21622                     cls : 'arrow'
21623                 },
21624                 {
21625                     cls : 'popover-inner ',
21626                     cn : [
21627                         {
21628                             tag: 'h3',
21629                             cls: 'popover-title popover-header',
21630                             html : this.title === false ? '' : this.title
21631                         },
21632                         {
21633                             cls : 'popover-content popover-body '  + (this.cls || ''),
21634                             html : this.html || ''
21635                         }
21636                     ]
21637                     
21638                 }
21639            ]
21640         };
21641         
21642         return cfg;
21643     },
21644     /**
21645      * @param {string} the title
21646      */
21647     setTitle: function(str)
21648     {
21649         this.title = str;
21650         if (this.el) {
21651             this.headerEl.dom.innerHTML = str;
21652         }
21653         
21654     },
21655     /**
21656      * @param {string} the body content
21657      */
21658     setContent: function(str)
21659     {
21660         this.html = str;
21661         if (this.contentEl) {
21662             this.contentEl.dom.innerHTML = str;
21663         }
21664         
21665     },
21666     // as it get's added to the bottom of the page.
21667     onRender : function(ct, position)
21668     {
21669         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21670         
21671         
21672         
21673         if(!this.el){
21674             var cfg = Roo.apply({},  this.getAutoCreate());
21675             cfg.id = Roo.id();
21676             
21677             if (this.cls) {
21678                 cfg.cls += ' ' + this.cls;
21679             }
21680             if (this.style) {
21681                 cfg.style = this.style;
21682             }
21683             //Roo.log("adding to ");
21684             this.el = Roo.get(document.body).createChild(cfg, position);
21685 //            Roo.log(this.el);
21686         }
21687         
21688         this.contentEl = this.el.select('.popover-content',true).first();
21689         this.headerEl =  this.el.select('.popover-title',true).first();
21690         
21691         var nitems = [];
21692         if(typeof(this.items) != 'undefined'){
21693             var items = this.items;
21694             delete this.items;
21695
21696             for(var i =0;i < items.length;i++) {
21697                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21698             }
21699         }
21700
21701         this.items = nitems;
21702         
21703         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21704         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21705         
21706         
21707         
21708         this.initEvents();
21709     },
21710     
21711     resizeMask : function()
21712     {
21713         this.maskEl.setSize(
21714             Roo.lib.Dom.getViewWidth(true),
21715             Roo.lib.Dom.getViewHeight(true)
21716         );
21717     },
21718     
21719     initEvents : function()
21720     {
21721         
21722         if (!this.modal) { 
21723             Roo.bootstrap.Popover.register(this);
21724         }
21725          
21726         this.arrowEl = this.el.select('.arrow',true).first();
21727         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21728         this.el.enableDisplayMode('block');
21729         this.el.hide();
21730  
21731         
21732         if (this.over === false && !this.parent()) {
21733             return; 
21734         }
21735         if (this.triggers === false) {
21736             return;
21737         }
21738          
21739         // support parent
21740         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21741         var triggers = this.trigger ? this.trigger.split(' ') : [];
21742         Roo.each(triggers, function(trigger) {
21743         
21744             if (trigger == 'click') {
21745                 on_el.on('click', this.toggle, this);
21746             } else if (trigger != 'manual') {
21747                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21748                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21749       
21750                 on_el.on(eventIn  ,this.enter, this);
21751                 on_el.on(eventOut, this.leave, this);
21752             }
21753         }, this);
21754     },
21755     
21756     
21757     // private
21758     timeout : null,
21759     hoverState : null,
21760     
21761     toggle : function () {
21762         this.hoverState == 'in' ? this.leave() : this.enter();
21763     },
21764     
21765     enter : function () {
21766         
21767         clearTimeout(this.timeout);
21768     
21769         this.hoverState = 'in';
21770     
21771         if (!this.delay || !this.delay.show) {
21772             this.show();
21773             return;
21774         }
21775         var _t = this;
21776         this.timeout = setTimeout(function () {
21777             if (_t.hoverState == 'in') {
21778                 _t.show();
21779             }
21780         }, this.delay.show)
21781     },
21782     
21783     leave : function() {
21784         clearTimeout(this.timeout);
21785     
21786         this.hoverState = 'out';
21787     
21788         if (!this.delay || !this.delay.hide) {
21789             this.hide();
21790             return;
21791         }
21792         var _t = this;
21793         this.timeout = setTimeout(function () {
21794             if (_t.hoverState == 'out') {
21795                 _t.hide();
21796             }
21797         }, this.delay.hide)
21798     },
21799     
21800     /**
21801      * update the position of the dialog
21802      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21803      * 
21804      *
21805      */
21806     
21807     doAlign : function()
21808     {
21809         
21810         if (this.alignEl) {
21811             this.updatePosition(this.placement, true);
21812              
21813         } else {
21814             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21815             var es = this.el.getSize();
21816             var x = Roo.lib.Dom.getViewWidth()/2;
21817             var y = Roo.lib.Dom.getViewHeight()/2;
21818             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21819             
21820         }
21821
21822          
21823          
21824         
21825         
21826     },
21827     
21828     /**
21829      * Show the popover
21830      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21831      * @param {string} (left|right|top|bottom) position
21832      */
21833     show : function (on_el, placement)
21834     {
21835         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21836         on_el = on_el || false; // default to false
21837          
21838         if (!on_el) {
21839             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21840                 on_el = this.parent().el;
21841             } else if (this.over) {
21842                 on_el = Roo.get(this.over);
21843             }
21844             
21845         }
21846         
21847         this.alignEl = Roo.get( on_el );
21848
21849         if (!this.el) {
21850             this.render(document.body);
21851         }
21852         
21853         
21854          
21855         
21856         if (this.title === false) {
21857             this.headerEl.hide();
21858         }
21859         
21860        
21861         this.el.show();
21862         this.el.dom.style.display = 'block';
21863          
21864         this.doAlign();
21865         
21866         //var arrow = this.el.select('.arrow',true).first();
21867         //arrow.set(align[2], 
21868         
21869         this.el.addClass('in');
21870         
21871          
21872         
21873         this.hoverState = 'in';
21874         
21875         if (this.modal) {
21876             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21877             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21878             this.maskEl.dom.style.display = 'block';
21879             this.maskEl.addClass('show');
21880         }
21881         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21882  
21883         this.fireEvent('show', this);
21884         
21885     },
21886     /**
21887      * fire this manually after loading a grid in the table for example
21888      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21889      * @param {Boolean} try and move it if we cant get right position.
21890      */
21891     updatePosition : function(placement, try_move)
21892     {
21893         // allow for calling with no parameters
21894         placement = placement   ? placement :  this.placement;
21895         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21896         
21897         this.el.removeClass([
21898             'fade','top','bottom', 'left', 'right','in',
21899             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21900         ]);
21901         this.el.addClass(placement + ' bs-popover-' + placement);
21902         
21903         if (!this.alignEl ) {
21904             return false;
21905         }
21906         
21907         switch (placement) {
21908             case 'right':
21909                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21910                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21911                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21912                     //normal display... or moved up/down.
21913                     this.el.setXY(offset);
21914                     var xy = this.alignEl.getAnchorXY('tr', false);
21915                     xy[0]+=2;xy[1]+=5;
21916                     this.arrowEl.setXY(xy);
21917                     return true;
21918                 }
21919                 // continue through...
21920                 return this.updatePosition('left', false);
21921                 
21922             
21923             case 'left':
21924                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21925                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21926                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21927                     //normal display... or moved up/down.
21928                     this.el.setXY(offset);
21929                     var xy = this.alignEl.getAnchorXY('tl', false);
21930                     xy[0]-=10;xy[1]+=5; // << fix me
21931                     this.arrowEl.setXY(xy);
21932                     return true;
21933                 }
21934                 // call self...
21935                 return this.updatePosition('right', false);
21936             
21937             case 'top':
21938                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21939                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21940                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21941                     //normal display... or moved up/down.
21942                     this.el.setXY(offset);
21943                     var xy = this.alignEl.getAnchorXY('t', false);
21944                     xy[1]-=10; // << fix me
21945                     this.arrowEl.setXY(xy);
21946                     return true;
21947                 }
21948                 // fall through
21949                return this.updatePosition('bottom', false);
21950             
21951             case 'bottom':
21952                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21953                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21954                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21955                     //normal display... or moved up/down.
21956                     this.el.setXY(offset);
21957                     var xy = this.alignEl.getAnchorXY('b', false);
21958                      xy[1]+=2; // << fix me
21959                     this.arrowEl.setXY(xy);
21960                     return true;
21961                 }
21962                 // fall through
21963                 return this.updatePosition('top', false);
21964                 
21965             
21966         }
21967         
21968         
21969         return false;
21970     },
21971     
21972     hide : function()
21973     {
21974         this.el.setXY([0,0]);
21975         this.el.removeClass('in');
21976         this.el.hide();
21977         this.hoverState = null;
21978         this.maskEl.hide(); // always..
21979         this.fireEvent('hide', this);
21980     }
21981     
21982 });
21983
21984
21985 Roo.apply(Roo.bootstrap.Popover, {
21986
21987     alignment : {
21988         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21989         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21990         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21991         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21992     },
21993     
21994     zIndex : 20001,
21995
21996     clickHander : false,
21997     
21998     
21999
22000     onMouseDown : function(e)
22001     {
22002         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22003             /// what is nothing is showing..
22004             this.hideAll();
22005         }
22006          
22007     },
22008     
22009     
22010     popups : [],
22011     
22012     register : function(popup)
22013     {
22014         if (!Roo.bootstrap.Popover.clickHandler) {
22015             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22016         }
22017         // hide other popups.
22018         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22019         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22020         this.hideAll(); //<< why?
22021         //this.popups.push(popup);
22022     },
22023     hideAll : function()
22024     {
22025         this.popups.forEach(function(p) {
22026             p.hide();
22027         });
22028     },
22029     onShow : function() {
22030         Roo.bootstrap.Popover.popups.push(this);
22031     },
22032     onHide : function() {
22033         Roo.bootstrap.Popover.popups.remove(this);
22034     } 
22035
22036 });
22037 /**
22038  * @class Roo.bootstrap.PopoverNav
22039  * @extends Roo.bootstrap.nav.Simplebar
22040  * @parent Roo.bootstrap.Popover
22041  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22042  * @licence LGPL
22043  * Bootstrap Popover header navigation class
22044  * FIXME? should this go under nav?
22045  *
22046  * 
22047  * @constructor
22048  * Create a new Popover Header Navigation 
22049  * @param {Object} config The config object
22050  */
22051
22052 Roo.bootstrap.PopoverNav = function(config){
22053     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22054 };
22055
22056 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22057     
22058     
22059     container_method : 'getPopoverHeader' 
22060     
22061      
22062     
22063     
22064    
22065 });
22066
22067  
22068
22069  /*
22070  * - LGPL
22071  *
22072  * Progress
22073  * 
22074  */
22075
22076 /**
22077  * @class Roo.bootstrap.Progress
22078  * @extends Roo.bootstrap.Component
22079  * @children Roo.bootstrap.ProgressBar
22080  * Bootstrap Progress class
22081  * @cfg {Boolean} striped striped of the progress bar
22082  * @cfg {Boolean} active animated of the progress bar
22083  * 
22084  * 
22085  * @constructor
22086  * Create a new Progress
22087  * @param {Object} config The config object
22088  */
22089
22090 Roo.bootstrap.Progress = function(config){
22091     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22092 };
22093
22094 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22095     
22096     striped : false,
22097     active: false,
22098     
22099     getAutoCreate : function(){
22100         var cfg = {
22101             tag: 'div',
22102             cls: 'progress'
22103         };
22104         
22105         
22106         if(this.striped){
22107             cfg.cls += ' progress-striped';
22108         }
22109       
22110         if(this.active){
22111             cfg.cls += ' active';
22112         }
22113         
22114         
22115         return cfg;
22116     }
22117    
22118 });
22119
22120  
22121
22122  /*
22123  * - LGPL
22124  *
22125  * ProgressBar
22126  * 
22127  */
22128
22129 /**
22130  * @class Roo.bootstrap.ProgressBar
22131  * @extends Roo.bootstrap.Component
22132  * Bootstrap ProgressBar class
22133  * @cfg {Number} aria_valuenow aria-value now
22134  * @cfg {Number} aria_valuemin aria-value min
22135  * @cfg {Number} aria_valuemax aria-value max
22136  * @cfg {String} label label for the progress bar
22137  * @cfg {String} panel (success | info | warning | danger )
22138  * @cfg {String} role role of the progress bar
22139  * @cfg {String} sr_only text
22140  * 
22141  * 
22142  * @constructor
22143  * Create a new ProgressBar
22144  * @param {Object} config The config object
22145  */
22146
22147 Roo.bootstrap.ProgressBar = function(config){
22148     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22149 };
22150
22151 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22152     
22153     aria_valuenow : 0,
22154     aria_valuemin : 0,
22155     aria_valuemax : 100,
22156     label : false,
22157     panel : false,
22158     role : false,
22159     sr_only: false,
22160     
22161     getAutoCreate : function()
22162     {
22163         
22164         var cfg = {
22165             tag: 'div',
22166             cls: 'progress-bar',
22167             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22168         };
22169         
22170         if(this.sr_only){
22171             cfg.cn = {
22172                 tag: 'span',
22173                 cls: 'sr-only',
22174                 html: this.sr_only
22175             }
22176         }
22177         
22178         if(this.role){
22179             cfg.role = this.role;
22180         }
22181         
22182         if(this.aria_valuenow){
22183             cfg['aria-valuenow'] = this.aria_valuenow;
22184         }
22185         
22186         if(this.aria_valuemin){
22187             cfg['aria-valuemin'] = this.aria_valuemin;
22188         }
22189         
22190         if(this.aria_valuemax){
22191             cfg['aria-valuemax'] = this.aria_valuemax;
22192         }
22193         
22194         if(this.label && !this.sr_only){
22195             cfg.html = this.label;
22196         }
22197         
22198         if(this.panel){
22199             cfg.cls += ' progress-bar-' + this.panel;
22200         }
22201         
22202         return cfg;
22203     },
22204     
22205     update : function(aria_valuenow)
22206     {
22207         this.aria_valuenow = aria_valuenow;
22208         
22209         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22210     }
22211    
22212 });
22213
22214  
22215
22216  /**
22217  * @class Roo.bootstrap.TabGroup
22218  * @extends Roo.bootstrap.Column
22219  * @children Roo.bootstrap.TabPanel
22220  * Bootstrap Column class
22221  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22222  * @cfg {Boolean} carousel true to make the group behave like a carousel
22223  * @cfg {Boolean} bullets show bullets for the panels
22224  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22225  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22226  * @cfg {Boolean} showarrow (true|false) show arrow default true
22227  * 
22228  * @constructor
22229  * Create a new TabGroup
22230  * @param {Object} config The config object
22231  */
22232
22233 Roo.bootstrap.TabGroup = function(config){
22234     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22235     if (!this.navId) {
22236         this.navId = Roo.id();
22237     }
22238     this.tabs = [];
22239     Roo.bootstrap.TabGroup.register(this);
22240     
22241 };
22242
22243 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22244     
22245     carousel : false,
22246     transition : false,
22247     bullets : 0,
22248     timer : 0,
22249     autoslide : false,
22250     slideFn : false,
22251     slideOnTouch : false,
22252     showarrow : true,
22253     
22254     getAutoCreate : function()
22255     {
22256         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22257         
22258         cfg.cls += ' tab-content';
22259         
22260         if (this.carousel) {
22261             cfg.cls += ' carousel slide';
22262             
22263             cfg.cn = [{
22264                cls : 'carousel-inner',
22265                cn : []
22266             }];
22267         
22268             if(this.bullets  && !Roo.isTouch){
22269                 
22270                 var bullets = {
22271                     cls : 'carousel-bullets',
22272                     cn : []
22273                 };
22274                
22275                 if(this.bullets_cls){
22276                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22277                 }
22278                 
22279                 bullets.cn.push({
22280                     cls : 'clear'
22281                 });
22282                 
22283                 cfg.cn[0].cn.push(bullets);
22284             }
22285             
22286             if(this.showarrow){
22287                 cfg.cn[0].cn.push({
22288                     tag : 'div',
22289                     class : 'carousel-arrow',
22290                     cn : [
22291                         {
22292                             tag : 'div',
22293                             class : 'carousel-prev',
22294                             cn : [
22295                                 {
22296                                     tag : 'i',
22297                                     class : 'fa fa-chevron-left'
22298                                 }
22299                             ]
22300                         },
22301                         {
22302                             tag : 'div',
22303                             class : 'carousel-next',
22304                             cn : [
22305                                 {
22306                                     tag : 'i',
22307                                     class : 'fa fa-chevron-right'
22308                                 }
22309                             ]
22310                         }
22311                     ]
22312                 });
22313             }
22314             
22315         }
22316         
22317         return cfg;
22318     },
22319     
22320     initEvents:  function()
22321     {
22322 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22323 //            this.el.on("touchstart", this.onTouchStart, this);
22324 //        }
22325         
22326         if(this.autoslide){
22327             var _this = this;
22328             
22329             this.slideFn = window.setInterval(function() {
22330                 _this.showPanelNext();
22331             }, this.timer);
22332         }
22333         
22334         if(this.showarrow){
22335             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22336             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22337         }
22338         
22339         
22340     },
22341     
22342 //    onTouchStart : function(e, el, o)
22343 //    {
22344 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22345 //            return;
22346 //        }
22347 //        
22348 //        this.showPanelNext();
22349 //    },
22350     
22351     
22352     getChildContainer : function()
22353     {
22354         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22355     },
22356     
22357     /**
22358     * register a Navigation item
22359     * @param {Roo.bootstrap.nav.Item} the navitem to add
22360     */
22361     register : function(item)
22362     {
22363         this.tabs.push( item);
22364         item.navId = this.navId; // not really needed..
22365         this.addBullet();
22366     
22367     },
22368     
22369     getActivePanel : function()
22370     {
22371         var r = false;
22372         Roo.each(this.tabs, function(t) {
22373             if (t.active) {
22374                 r = t;
22375                 return false;
22376             }
22377             return null;
22378         });
22379         return r;
22380         
22381     },
22382     getPanelByName : function(n)
22383     {
22384         var r = false;
22385         Roo.each(this.tabs, function(t) {
22386             if (t.tabId == n) {
22387                 r = t;
22388                 return false;
22389             }
22390             return null;
22391         });
22392         return r;
22393     },
22394     indexOfPanel : function(p)
22395     {
22396         var r = false;
22397         Roo.each(this.tabs, function(t,i) {
22398             if (t.tabId == p.tabId) {
22399                 r = i;
22400                 return false;
22401             }
22402             return null;
22403         });
22404         return r;
22405     },
22406     /**
22407      * show a specific panel
22408      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22409      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22410      */
22411     showPanel : function (pan)
22412     {
22413         if(this.transition || typeof(pan) == 'undefined'){
22414             Roo.log("waiting for the transitionend");
22415             return false;
22416         }
22417         
22418         if (typeof(pan) == 'number') {
22419             pan = this.tabs[pan];
22420         }
22421         
22422         if (typeof(pan) == 'string') {
22423             pan = this.getPanelByName(pan);
22424         }
22425         
22426         var cur = this.getActivePanel();
22427         
22428         if(!pan || !cur){
22429             Roo.log('pan or acitve pan is undefined');
22430             return false;
22431         }
22432         
22433         if (pan.tabId == this.getActivePanel().tabId) {
22434             return true;
22435         }
22436         
22437         if (false === cur.fireEvent('beforedeactivate')) {
22438             return false;
22439         }
22440         
22441         if(this.bullets > 0 && !Roo.isTouch){
22442             this.setActiveBullet(this.indexOfPanel(pan));
22443         }
22444         
22445         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22446             
22447             //class="carousel-item carousel-item-next carousel-item-left"
22448             
22449             this.transition = true;
22450             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22451             var lr = dir == 'next' ? 'left' : 'right';
22452             pan.el.addClass(dir); // or prev
22453             pan.el.addClass('carousel-item-' + dir); // or prev
22454             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22455             cur.el.addClass(lr); // or right
22456             pan.el.addClass(lr);
22457             cur.el.addClass('carousel-item-' +lr); // or right
22458             pan.el.addClass('carousel-item-' +lr);
22459             
22460             
22461             var _this = this;
22462             cur.el.on('transitionend', function() {
22463                 Roo.log("trans end?");
22464                 
22465                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22466                 pan.setActive(true);
22467                 
22468                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22469                 cur.setActive(false);
22470                 
22471                 _this.transition = false;
22472                 
22473             }, this, { single:  true } );
22474             
22475             return true;
22476         }
22477         
22478         cur.setActive(false);
22479         pan.setActive(true);
22480         
22481         return true;
22482         
22483     },
22484     showPanelNext : function()
22485     {
22486         var i = this.indexOfPanel(this.getActivePanel());
22487         
22488         if (i >= this.tabs.length - 1 && !this.autoslide) {
22489             return;
22490         }
22491         
22492         if (i >= this.tabs.length - 1 && this.autoslide) {
22493             i = -1;
22494         }
22495         
22496         this.showPanel(this.tabs[i+1]);
22497     },
22498     
22499     showPanelPrev : function()
22500     {
22501         var i = this.indexOfPanel(this.getActivePanel());
22502         
22503         if (i  < 1 && !this.autoslide) {
22504             return;
22505         }
22506         
22507         if (i < 1 && this.autoslide) {
22508             i = this.tabs.length;
22509         }
22510         
22511         this.showPanel(this.tabs[i-1]);
22512     },
22513     
22514     
22515     addBullet: function()
22516     {
22517         if(!this.bullets || Roo.isTouch){
22518             return;
22519         }
22520         var ctr = this.el.select('.carousel-bullets',true).first();
22521         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22522         var bullet = ctr.createChild({
22523             cls : 'bullet bullet-' + i
22524         },ctr.dom.lastChild);
22525         
22526         
22527         var _this = this;
22528         
22529         bullet.on('click', (function(e, el, o, ii, t){
22530
22531             e.preventDefault();
22532
22533             this.showPanel(ii);
22534
22535             if(this.autoslide && this.slideFn){
22536                 clearInterval(this.slideFn);
22537                 this.slideFn = window.setInterval(function() {
22538                     _this.showPanelNext();
22539                 }, this.timer);
22540             }
22541
22542         }).createDelegate(this, [i, bullet], true));
22543                 
22544         
22545     },
22546      
22547     setActiveBullet : function(i)
22548     {
22549         if(Roo.isTouch){
22550             return;
22551         }
22552         
22553         Roo.each(this.el.select('.bullet', true).elements, function(el){
22554             el.removeClass('selected');
22555         });
22556
22557         var bullet = this.el.select('.bullet-' + i, true).first();
22558         
22559         if(!bullet){
22560             return;
22561         }
22562         
22563         bullet.addClass('selected');
22564     }
22565     
22566     
22567   
22568 });
22569
22570  
22571
22572  
22573  
22574 Roo.apply(Roo.bootstrap.TabGroup, {
22575     
22576     groups: {},
22577      /**
22578     * register a Navigation Group
22579     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22580     */
22581     register : function(navgrp)
22582     {
22583         this.groups[navgrp.navId] = navgrp;
22584         
22585     },
22586     /**
22587     * fetch a Navigation Group based on the navigation ID
22588     * if one does not exist , it will get created.
22589     * @param {string} the navgroup to add
22590     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22591     */
22592     get: function(navId) {
22593         if (typeof(this.groups[navId]) == 'undefined') {
22594             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22595         }
22596         return this.groups[navId] ;
22597     }
22598     
22599     
22600     
22601 });
22602
22603  /*
22604  * - LGPL
22605  *
22606  * TabPanel
22607  * 
22608  */
22609
22610 /**
22611  * @class Roo.bootstrap.TabPanel
22612  * @extends Roo.bootstrap.Component
22613  * @children Roo.bootstrap.Component
22614  * Bootstrap TabPanel class
22615  * @cfg {Boolean} active panel active
22616  * @cfg {String} html panel content
22617  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22618  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22619  * @cfg {String} href click to link..
22620  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22621  * 
22622  * 
22623  * @constructor
22624  * Create a new TabPanel
22625  * @param {Object} config The config object
22626  */
22627
22628 Roo.bootstrap.TabPanel = function(config){
22629     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22630     this.addEvents({
22631         /**
22632              * @event changed
22633              * Fires when the active status changes
22634              * @param {Roo.bootstrap.TabPanel} this
22635              * @param {Boolean} state the new state
22636             
22637          */
22638         'changed': true,
22639         /**
22640              * @event beforedeactivate
22641              * Fires before a tab is de-activated - can be used to do validation on a form.
22642              * @param {Roo.bootstrap.TabPanel} this
22643              * @return {Boolean} false if there is an error
22644             
22645          */
22646         'beforedeactivate': true
22647      });
22648     
22649     this.tabId = this.tabId || Roo.id();
22650   
22651 };
22652
22653 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22654     
22655     active: false,
22656     html: false,
22657     tabId: false,
22658     navId : false,
22659     href : '',
22660     touchSlide : false,
22661     getAutoCreate : function(){
22662         
22663         
22664         var cfg = {
22665             tag: 'div',
22666             // item is needed for carousel - not sure if it has any effect otherwise
22667             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22668             html: this.html || ''
22669         };
22670         
22671         if(this.active){
22672             cfg.cls += ' active';
22673         }
22674         
22675         if(this.tabId){
22676             cfg.tabId = this.tabId;
22677         }
22678         
22679         
22680         
22681         return cfg;
22682     },
22683     
22684     initEvents:  function()
22685     {
22686         var p = this.parent();
22687         
22688         this.navId = this.navId || p.navId;
22689         
22690         if (typeof(this.navId) != 'undefined') {
22691             // not really needed.. but just in case.. parent should be a NavGroup.
22692             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22693             
22694             tg.register(this);
22695             
22696             var i = tg.tabs.length - 1;
22697             
22698             if(this.active && tg.bullets > 0 && i < tg.bullets){
22699                 tg.setActiveBullet(i);
22700             }
22701         }
22702         
22703         this.el.on('click', this.onClick, this);
22704         
22705         if(Roo.isTouch && this.touchSlide){
22706             this.el.on("touchstart", this.onTouchStart, this);
22707             this.el.on("touchmove", this.onTouchMove, this);
22708             this.el.on("touchend", this.onTouchEnd, this);
22709         }
22710         
22711     },
22712     
22713     onRender : function(ct, position)
22714     {
22715         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22716     },
22717     
22718     setActive : function(state)
22719     {
22720         Roo.log("panel - set active " + this.tabId + "=" + state);
22721         
22722         this.active = state;
22723         if (!state) {
22724             this.el.removeClass('active');
22725             
22726         } else  if (!this.el.hasClass('active')) {
22727             this.el.addClass('active');
22728         }
22729         
22730         this.fireEvent('changed', this, state);
22731     },
22732     
22733     onClick : function(e)
22734     {
22735         e.preventDefault();
22736         
22737         if(!this.href.length){
22738             return;
22739         }
22740         
22741         window.location.href = this.href;
22742     },
22743     
22744     startX : 0,
22745     startY : 0,
22746     endX : 0,
22747     endY : 0,
22748     swiping : false,
22749     
22750     onTouchStart : function(e)
22751     {
22752         this.swiping = false;
22753         
22754         this.startX = e.browserEvent.touches[0].clientX;
22755         this.startY = e.browserEvent.touches[0].clientY;
22756     },
22757     
22758     onTouchMove : function(e)
22759     {
22760         this.swiping = true;
22761         
22762         this.endX = e.browserEvent.touches[0].clientX;
22763         this.endY = e.browserEvent.touches[0].clientY;
22764     },
22765     
22766     onTouchEnd : function(e)
22767     {
22768         if(!this.swiping){
22769             this.onClick(e);
22770             return;
22771         }
22772         
22773         var tabGroup = this.parent();
22774         
22775         if(this.endX > this.startX){ // swiping right
22776             tabGroup.showPanelPrev();
22777             return;
22778         }
22779         
22780         if(this.startX > this.endX){ // swiping left
22781             tabGroup.showPanelNext();
22782             return;
22783         }
22784     }
22785     
22786     
22787 });
22788  
22789
22790  
22791
22792  /*
22793  * - LGPL
22794  *
22795  * DateField
22796  * 
22797  */
22798
22799 /**
22800  * @class Roo.bootstrap.form.DateField
22801  * @extends Roo.bootstrap.form.Input
22802  * Bootstrap DateField class
22803  * @cfg {Number} weekStart default 0
22804  * @cfg {String} viewMode default empty, (months|years)
22805  * @cfg {String} minViewMode default empty, (months|years)
22806  * @cfg {Number} startDate default -Infinity
22807  * @cfg {Number} endDate default Infinity
22808  * @cfg {Boolean} todayHighlight default false
22809  * @cfg {Boolean} todayBtn default false
22810  * @cfg {Boolean} calendarWeeks default false
22811  * @cfg {Object} daysOfWeekDisabled default empty
22812  * @cfg {Boolean} singleMode default false (true | false)
22813  * 
22814  * @cfg {Boolean} keyboardNavigation default true
22815  * @cfg {String} language default en
22816  * 
22817  * @constructor
22818  * Create a new DateField
22819  * @param {Object} config The config object
22820  */
22821
22822 Roo.bootstrap.form.DateField = function(config){
22823     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22824      this.addEvents({
22825             /**
22826              * @event show
22827              * Fires when this field show.
22828              * @param {Roo.bootstrap.form.DateField} this
22829              * @param {Mixed} date The date value
22830              */
22831             show : true,
22832             /**
22833              * @event show
22834              * Fires when this field hide.
22835              * @param {Roo.bootstrap.form.DateField} this
22836              * @param {Mixed} date The date value
22837              */
22838             hide : true,
22839             /**
22840              * @event select
22841              * Fires when select a date.
22842              * @param {Roo.bootstrap.form.DateField} this
22843              * @param {Mixed} date The date value
22844              */
22845             select : true,
22846             /**
22847              * @event beforeselect
22848              * Fires when before select a date.
22849              * @param {Roo.bootstrap.form.DateField} this
22850              * @param {Mixed} date The date value
22851              */
22852             beforeselect : true
22853         });
22854 };
22855
22856 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22857     
22858     /**
22859      * @cfg {String} format
22860      * The default date format string which can be overriden for localization support.  The format must be
22861      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22862      */
22863     format : "m/d/y",
22864     /**
22865      * @cfg {String} altFormats
22866      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22867      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22868      */
22869     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22870     
22871     weekStart : 0,
22872     
22873     viewMode : '',
22874     
22875     minViewMode : '',
22876     
22877     todayHighlight : false,
22878     
22879     todayBtn: false,
22880     
22881     language: 'en',
22882     
22883     keyboardNavigation: true,
22884     
22885     calendarWeeks: false,
22886     
22887     startDate: -Infinity,
22888     
22889     endDate: Infinity,
22890     
22891     daysOfWeekDisabled: [],
22892     
22893     _events: [],
22894     
22895     singleMode : false,
22896     
22897     UTCDate: function()
22898     {
22899         return new Date(Date.UTC.apply(Date, arguments));
22900     },
22901     
22902     UTCToday: function()
22903     {
22904         var today = new Date();
22905         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22906     },
22907     
22908     getDate: function() {
22909             var d = this.getUTCDate();
22910             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22911     },
22912     
22913     getUTCDate: function() {
22914             return this.date;
22915     },
22916     
22917     setDate: function(d) {
22918             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22919     },
22920     
22921     setUTCDate: function(d) {
22922             this.date = d;
22923             this.setValue(this.formatDate(this.date));
22924     },
22925         
22926     onRender: function(ct, position)
22927     {
22928         
22929         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22930         
22931         this.language = this.language || 'en';
22932         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22933         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22934         
22935         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22936         this.format = this.format || 'm/d/y';
22937         this.isInline = false;
22938         this.isInput = true;
22939         this.component = this.el.select('.add-on', true).first() || false;
22940         this.component = (this.component && this.component.length === 0) ? false : this.component;
22941         this.hasInput = this.component && this.inputEl().length;
22942         
22943         if (typeof(this.minViewMode === 'string')) {
22944             switch (this.minViewMode) {
22945                 case 'months':
22946                     this.minViewMode = 1;
22947                     break;
22948                 case 'years':
22949                     this.minViewMode = 2;
22950                     break;
22951                 default:
22952                     this.minViewMode = 0;
22953                     break;
22954             }
22955         }
22956         
22957         if (typeof(this.viewMode === 'string')) {
22958             switch (this.viewMode) {
22959                 case 'months':
22960                     this.viewMode = 1;
22961                     break;
22962                 case 'years':
22963                     this.viewMode = 2;
22964                     break;
22965                 default:
22966                     this.viewMode = 0;
22967                     break;
22968             }
22969         }
22970                 
22971         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22972         
22973 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22974         
22975         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22976         
22977         this.picker().on('mousedown', this.onMousedown, this);
22978         this.picker().on('click', this.onClick, this);
22979         
22980         this.picker().addClass('datepicker-dropdown');
22981         
22982         this.startViewMode = this.viewMode;
22983         
22984         if(this.singleMode){
22985             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22986                 v.setVisibilityMode(Roo.Element.DISPLAY);
22987                 v.hide();
22988             });
22989             
22990             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22991                 v.setStyle('width', '189px');
22992             });
22993         }
22994         
22995         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22996             if(!this.calendarWeeks){
22997                 v.remove();
22998                 return;
22999             }
23000             
23001             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23002             v.attr('colspan', function(i, val){
23003                 return parseInt(val) + 1;
23004             });
23005         });
23006                         
23007         
23008         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23009         
23010         this.setStartDate(this.startDate);
23011         this.setEndDate(this.endDate);
23012         
23013         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23014         
23015         this.fillDow();
23016         this.fillMonths();
23017         this.update();
23018         this.showMode();
23019         
23020         if(this.isInline) {
23021             this.showPopup();
23022         }
23023     },
23024     
23025     picker : function()
23026     {
23027         return this.pickerEl;
23028 //        return this.el.select('.datepicker', true).first();
23029     },
23030     
23031     fillDow: function()
23032     {
23033         var dowCnt = this.weekStart;
23034         
23035         var dow = {
23036             tag: 'tr',
23037             cn: [
23038                 
23039             ]
23040         };
23041         
23042         if(this.calendarWeeks){
23043             dow.cn.push({
23044                 tag: 'th',
23045                 cls: 'cw',
23046                 html: '&nbsp;'
23047             })
23048         }
23049         
23050         while (dowCnt < this.weekStart + 7) {
23051             dow.cn.push({
23052                 tag: 'th',
23053                 cls: 'dow',
23054                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23055             });
23056         }
23057         
23058         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23059     },
23060     
23061     fillMonths: function()
23062     {    
23063         var i = 0;
23064         var months = this.picker().select('>.datepicker-months td', true).first();
23065         
23066         months.dom.innerHTML = '';
23067         
23068         while (i < 12) {
23069             var month = {
23070                 tag: 'span',
23071                 cls: 'month',
23072                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23073             };
23074             
23075             months.createChild(month);
23076         }
23077         
23078     },
23079     
23080     update: function()
23081     {
23082         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;
23083         
23084         if (this.date < this.startDate) {
23085             this.viewDate = new Date(this.startDate);
23086         } else if (this.date > this.endDate) {
23087             this.viewDate = new Date(this.endDate);
23088         } else {
23089             this.viewDate = new Date(this.date);
23090         }
23091         
23092         this.fill();
23093     },
23094     
23095     fill: function() 
23096     {
23097         var d = new Date(this.viewDate),
23098                 year = d.getUTCFullYear(),
23099                 month = d.getUTCMonth(),
23100                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23101                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23102                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23103                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23104                 currentDate = this.date && this.date.valueOf(),
23105                 today = this.UTCToday();
23106         
23107         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23108         
23109 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23110         
23111 //        this.picker.select('>tfoot th.today').
23112 //                                              .text(dates[this.language].today)
23113 //                                              .toggle(this.todayBtn !== false);
23114     
23115         this.updateNavArrows();
23116         this.fillMonths();
23117                                                 
23118         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23119         
23120         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23121          
23122         prevMonth.setUTCDate(day);
23123         
23124         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23125         
23126         var nextMonth = new Date(prevMonth);
23127         
23128         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23129         
23130         nextMonth = nextMonth.valueOf();
23131         
23132         var fillMonths = false;
23133         
23134         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23135         
23136         while(prevMonth.valueOf() <= nextMonth) {
23137             var clsName = '';
23138             
23139             if (prevMonth.getUTCDay() === this.weekStart) {
23140                 if(fillMonths){
23141                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23142                 }
23143                     
23144                 fillMonths = {
23145                     tag: 'tr',
23146                     cn: []
23147                 };
23148                 
23149                 if(this.calendarWeeks){
23150                     // ISO 8601: First week contains first thursday.
23151                     // ISO also states week starts on Monday, but we can be more abstract here.
23152                     var
23153                     // Start of current week: based on weekstart/current date
23154                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23155                     // Thursday of this week
23156                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23157                     // First Thursday of year, year from thursday
23158                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23159                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23160                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23161                     
23162                     fillMonths.cn.push({
23163                         tag: 'td',
23164                         cls: 'cw',
23165                         html: calWeek
23166                     });
23167                 }
23168             }
23169             
23170             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23171                 clsName += ' old';
23172             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23173                 clsName += ' new';
23174             }
23175             if (this.todayHighlight &&
23176                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23177                 prevMonth.getUTCMonth() == today.getMonth() &&
23178                 prevMonth.getUTCDate() == today.getDate()) {
23179                 clsName += ' today';
23180             }
23181             
23182             if (currentDate && prevMonth.valueOf() === currentDate) {
23183                 clsName += ' active';
23184             }
23185             
23186             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23187                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23188                     clsName += ' disabled';
23189             }
23190             
23191             fillMonths.cn.push({
23192                 tag: 'td',
23193                 cls: 'day ' + clsName,
23194                 html: prevMonth.getDate()
23195             });
23196             
23197             prevMonth.setDate(prevMonth.getDate()+1);
23198         }
23199           
23200         var currentYear = this.date && this.date.getUTCFullYear();
23201         var currentMonth = this.date && this.date.getUTCMonth();
23202         
23203         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23204         
23205         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23206             v.removeClass('active');
23207             
23208             if(currentYear === year && k === currentMonth){
23209                 v.addClass('active');
23210             }
23211             
23212             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23213                 v.addClass('disabled');
23214             }
23215             
23216         });
23217         
23218         
23219         year = parseInt(year/10, 10) * 10;
23220         
23221         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23222         
23223         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23224         
23225         year -= 1;
23226         for (var i = -1; i < 11; i++) {
23227             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23228                 tag: 'span',
23229                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23230                 html: year
23231             });
23232             
23233             year += 1;
23234         }
23235     },
23236     
23237     showMode: function(dir) 
23238     {
23239         if (dir) {
23240             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23241         }
23242         
23243         Roo.each(this.picker().select('>div',true).elements, function(v){
23244             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23245             v.hide();
23246         });
23247         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23248     },
23249     
23250     place: function()
23251     {
23252         if(this.isInline) {
23253             return;
23254         }
23255         
23256         this.picker().removeClass(['bottom', 'top']);
23257         
23258         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23259             /*
23260              * place to the top of element!
23261              *
23262              */
23263             
23264             this.picker().addClass('top');
23265             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23266             
23267             return;
23268         }
23269         
23270         this.picker().addClass('bottom');
23271         
23272         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23273     },
23274     
23275     parseDate : function(value)
23276     {
23277         if(!value || value instanceof Date){
23278             return value;
23279         }
23280         var v = Date.parseDate(value, this.format);
23281         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23282             v = Date.parseDate(value, 'Y-m-d');
23283         }
23284         if(!v && this.altFormats){
23285             if(!this.altFormatsArray){
23286                 this.altFormatsArray = this.altFormats.split("|");
23287             }
23288             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23289                 v = Date.parseDate(value, this.altFormatsArray[i]);
23290             }
23291         }
23292         return v;
23293     },
23294     
23295     formatDate : function(date, fmt)
23296     {   
23297         return (!date || !(date instanceof Date)) ?
23298         date : date.dateFormat(fmt || this.format);
23299     },
23300     
23301     onFocus : function()
23302     {
23303         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23304         this.showPopup();
23305     },
23306     
23307     onBlur : function()
23308     {
23309         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23310         
23311         var d = this.inputEl().getValue();
23312         
23313         this.setValue(d);
23314                 
23315         this.hidePopup();
23316     },
23317     
23318     showPopup : function()
23319     {
23320         this.picker().show();
23321         this.update();
23322         this.place();
23323         
23324         this.fireEvent('showpopup', this, this.date);
23325     },
23326     
23327     hidePopup : function()
23328     {
23329         if(this.isInline) {
23330             return;
23331         }
23332         this.picker().hide();
23333         this.viewMode = this.startViewMode;
23334         this.showMode();
23335         
23336         this.fireEvent('hidepopup', this, this.date);
23337         
23338     },
23339     
23340     onMousedown: function(e)
23341     {
23342         e.stopPropagation();
23343         e.preventDefault();
23344     },
23345     
23346     keyup: function(e)
23347     {
23348         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23349         this.update();
23350     },
23351
23352     setValue: function(v)
23353     {
23354         if(this.fireEvent('beforeselect', this, v) !== false){
23355             var d = new Date(this.parseDate(v) ).clearTime();
23356         
23357             if(isNaN(d.getTime())){
23358                 this.date = this.viewDate = '';
23359                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23360                 return;
23361             }
23362
23363             v = this.formatDate(d);
23364
23365             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23366
23367             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23368
23369             this.update();
23370
23371             this.fireEvent('select', this, this.date);
23372         }
23373     },
23374     
23375     getValue: function()
23376     {
23377         return this.formatDate(this.date);
23378     },
23379     
23380     fireKey: function(e)
23381     {
23382         if (!this.picker().isVisible()){
23383             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23384                 this.showPopup();
23385             }
23386             return;
23387         }
23388         
23389         var dateChanged = false,
23390         dir, day, month,
23391         newDate, newViewDate;
23392         
23393         switch(e.keyCode){
23394             case 27: // escape
23395                 this.hidePopup();
23396                 e.preventDefault();
23397                 break;
23398             case 37: // left
23399             case 39: // right
23400                 if (!this.keyboardNavigation) {
23401                     break;
23402                 }
23403                 dir = e.keyCode == 37 ? -1 : 1;
23404                 
23405                 if (e.ctrlKey){
23406                     newDate = this.moveYear(this.date, dir);
23407                     newViewDate = this.moveYear(this.viewDate, dir);
23408                 } else if (e.shiftKey){
23409                     newDate = this.moveMonth(this.date, dir);
23410                     newViewDate = this.moveMonth(this.viewDate, dir);
23411                 } else {
23412                     newDate = new Date(this.date);
23413                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23414                     newViewDate = new Date(this.viewDate);
23415                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23416                 }
23417                 if (this.dateWithinRange(newDate)){
23418                     this.date = newDate;
23419                     this.viewDate = newViewDate;
23420                     this.setValue(this.formatDate(this.date));
23421 //                    this.update();
23422                     e.preventDefault();
23423                     dateChanged = true;
23424                 }
23425                 break;
23426             case 38: // up
23427             case 40: // down
23428                 if (!this.keyboardNavigation) {
23429                     break;
23430                 }
23431                 dir = e.keyCode == 38 ? -1 : 1;
23432                 if (e.ctrlKey){
23433                     newDate = this.moveYear(this.date, dir);
23434                     newViewDate = this.moveYear(this.viewDate, dir);
23435                 } else if (e.shiftKey){
23436                     newDate = this.moveMonth(this.date, dir);
23437                     newViewDate = this.moveMonth(this.viewDate, dir);
23438                 } else {
23439                     newDate = new Date(this.date);
23440                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23441                     newViewDate = new Date(this.viewDate);
23442                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23443                 }
23444                 if (this.dateWithinRange(newDate)){
23445                     this.date = newDate;
23446                     this.viewDate = newViewDate;
23447                     this.setValue(this.formatDate(this.date));
23448 //                    this.update();
23449                     e.preventDefault();
23450                     dateChanged = true;
23451                 }
23452                 break;
23453             case 13: // enter
23454                 this.setValue(this.formatDate(this.date));
23455                 this.hidePopup();
23456                 e.preventDefault();
23457                 break;
23458             case 9: // tab
23459                 this.setValue(this.formatDate(this.date));
23460                 this.hidePopup();
23461                 break;
23462             case 16: // shift
23463             case 17: // ctrl
23464             case 18: // alt
23465                 break;
23466             default :
23467                 this.hidePopup();
23468                 
23469         }
23470     },
23471     
23472     
23473     onClick: function(e) 
23474     {
23475         e.stopPropagation();
23476         e.preventDefault();
23477         
23478         var target = e.getTarget();
23479         
23480         if(target.nodeName.toLowerCase() === 'i'){
23481             target = Roo.get(target).dom.parentNode;
23482         }
23483         
23484         var nodeName = target.nodeName;
23485         var className = target.className;
23486         var html = target.innerHTML;
23487         //Roo.log(nodeName);
23488         
23489         switch(nodeName.toLowerCase()) {
23490             case 'th':
23491                 switch(className) {
23492                     case 'switch':
23493                         this.showMode(1);
23494                         break;
23495                     case 'prev':
23496                     case 'next':
23497                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23498                         switch(this.viewMode){
23499                                 case 0:
23500                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23501                                         break;
23502                                 case 1:
23503                                 case 2:
23504                                         this.viewDate = this.moveYear(this.viewDate, dir);
23505                                         break;
23506                         }
23507                         this.fill();
23508                         break;
23509                     case 'today':
23510                         var date = new Date();
23511                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23512 //                        this.fill()
23513                         this.setValue(this.formatDate(this.date));
23514                         
23515                         this.hidePopup();
23516                         break;
23517                 }
23518                 break;
23519             case 'span':
23520                 if (className.indexOf('disabled') < 0) {
23521                 if (!this.viewDate) {
23522                     this.viewDate = new Date();
23523                 }
23524                 this.viewDate.setUTCDate(1);
23525                     if (className.indexOf('month') > -1) {
23526                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23527                     } else {
23528                         var year = parseInt(html, 10) || 0;
23529                         this.viewDate.setUTCFullYear(year);
23530                         
23531                     }
23532                     
23533                     if(this.singleMode){
23534                         this.setValue(this.formatDate(this.viewDate));
23535                         this.hidePopup();
23536                         return;
23537                     }
23538                     
23539                     this.showMode(-1);
23540                     this.fill();
23541                 }
23542                 break;
23543                 
23544             case 'td':
23545                 //Roo.log(className);
23546                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23547                     var day = parseInt(html, 10) || 1;
23548                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23549                         month = (this.viewDate || new Date()).getUTCMonth();
23550
23551                     if (className.indexOf('old') > -1) {
23552                         if(month === 0 ){
23553                             month = 11;
23554                             year -= 1;
23555                         }else{
23556                             month -= 1;
23557                         }
23558                     } else if (className.indexOf('new') > -1) {
23559                         if (month == 11) {
23560                             month = 0;
23561                             year += 1;
23562                         } else {
23563                             month += 1;
23564                         }
23565                     }
23566                     //Roo.log([year,month,day]);
23567                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23568                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23569 //                    this.fill();
23570                     //Roo.log(this.formatDate(this.date));
23571                     this.setValue(this.formatDate(this.date));
23572                     this.hidePopup();
23573                 }
23574                 break;
23575         }
23576     },
23577     
23578     setStartDate: function(startDate)
23579     {
23580         this.startDate = startDate || -Infinity;
23581         if (this.startDate !== -Infinity) {
23582             this.startDate = this.parseDate(this.startDate);
23583         }
23584         this.update();
23585         this.updateNavArrows();
23586     },
23587
23588     setEndDate: function(endDate)
23589     {
23590         this.endDate = endDate || Infinity;
23591         if (this.endDate !== Infinity) {
23592             this.endDate = this.parseDate(this.endDate);
23593         }
23594         this.update();
23595         this.updateNavArrows();
23596     },
23597     
23598     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23599     {
23600         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23601         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23602             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23603         }
23604         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23605             return parseInt(d, 10);
23606         });
23607         this.update();
23608         this.updateNavArrows();
23609     },
23610     
23611     updateNavArrows: function() 
23612     {
23613         if(this.singleMode){
23614             return;
23615         }
23616         
23617         var d = new Date(this.viewDate),
23618         year = d.getUTCFullYear(),
23619         month = d.getUTCMonth();
23620         
23621         Roo.each(this.picker().select('.prev', true).elements, function(v){
23622             v.show();
23623             switch (this.viewMode) {
23624                 case 0:
23625
23626                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23627                         v.hide();
23628                     }
23629                     break;
23630                 case 1:
23631                 case 2:
23632                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23633                         v.hide();
23634                     }
23635                     break;
23636             }
23637         });
23638         
23639         Roo.each(this.picker().select('.next', true).elements, function(v){
23640             v.show();
23641             switch (this.viewMode) {
23642                 case 0:
23643
23644                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23645                         v.hide();
23646                     }
23647                     break;
23648                 case 1:
23649                 case 2:
23650                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23651                         v.hide();
23652                     }
23653                     break;
23654             }
23655         })
23656     },
23657     
23658     moveMonth: function(date, dir)
23659     {
23660         if (!dir) {
23661             return date;
23662         }
23663         var new_date = new Date(date.valueOf()),
23664         day = new_date.getUTCDate(),
23665         month = new_date.getUTCMonth(),
23666         mag = Math.abs(dir),
23667         new_month, test;
23668         dir = dir > 0 ? 1 : -1;
23669         if (mag == 1){
23670             test = dir == -1
23671             // If going back one month, make sure month is not current month
23672             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23673             ? function(){
23674                 return new_date.getUTCMonth() == month;
23675             }
23676             // If going forward one month, make sure month is as expected
23677             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23678             : function(){
23679                 return new_date.getUTCMonth() != new_month;
23680             };
23681             new_month = month + dir;
23682             new_date.setUTCMonth(new_month);
23683             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23684             if (new_month < 0 || new_month > 11) {
23685                 new_month = (new_month + 12) % 12;
23686             }
23687         } else {
23688             // For magnitudes >1, move one month at a time...
23689             for (var i=0; i<mag; i++) {
23690                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23691                 new_date = this.moveMonth(new_date, dir);
23692             }
23693             // ...then reset the day, keeping it in the new month
23694             new_month = new_date.getUTCMonth();
23695             new_date.setUTCDate(day);
23696             test = function(){
23697                 return new_month != new_date.getUTCMonth();
23698             };
23699         }
23700         // Common date-resetting loop -- if date is beyond end of month, make it
23701         // end of month
23702         while (test()){
23703             new_date.setUTCDate(--day);
23704             new_date.setUTCMonth(new_month);
23705         }
23706         return new_date;
23707     },
23708
23709     moveYear: function(date, dir)
23710     {
23711         return this.moveMonth(date, dir*12);
23712     },
23713
23714     dateWithinRange: function(date)
23715     {
23716         return date >= this.startDate && date <= this.endDate;
23717     },
23718
23719     
23720     remove: function() 
23721     {
23722         this.picker().remove();
23723     },
23724     
23725     validateValue : function(value)
23726     {
23727         if(this.getVisibilityEl().hasClass('hidden')){
23728             return true;
23729         }
23730         
23731         if(value.length < 1)  {
23732             if(this.allowBlank){
23733                 return true;
23734             }
23735             return false;
23736         }
23737         
23738         if(value.length < this.minLength){
23739             return false;
23740         }
23741         if(value.length > this.maxLength){
23742             return false;
23743         }
23744         if(this.vtype){
23745             var vt = Roo.form.VTypes;
23746             if(!vt[this.vtype](value, this)){
23747                 return false;
23748             }
23749         }
23750         if(typeof this.validator == "function"){
23751             var msg = this.validator(value);
23752             if(msg !== true){
23753                 return false;
23754             }
23755         }
23756         
23757         if(this.regex && !this.regex.test(value)){
23758             return false;
23759         }
23760         
23761         if(typeof(this.parseDate(value)) == 'undefined'){
23762             return false;
23763         }
23764         
23765         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23766             return false;
23767         }      
23768         
23769         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23770             return false;
23771         } 
23772         
23773         
23774         return true;
23775     },
23776     
23777     reset : function()
23778     {
23779         this.date = this.viewDate = '';
23780         
23781         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23782     }
23783    
23784 });
23785
23786 Roo.apply(Roo.bootstrap.form.DateField,  {
23787     
23788     head : {
23789         tag: 'thead',
23790         cn: [
23791         {
23792             tag: 'tr',
23793             cn: [
23794             {
23795                 tag: 'th',
23796                 cls: 'prev',
23797                 html: '<i class="fa fa-arrow-left"/>'
23798             },
23799             {
23800                 tag: 'th',
23801                 cls: 'switch',
23802                 colspan: '5'
23803             },
23804             {
23805                 tag: 'th',
23806                 cls: 'next',
23807                 html: '<i class="fa fa-arrow-right"/>'
23808             }
23809
23810             ]
23811         }
23812         ]
23813     },
23814     
23815     content : {
23816         tag: 'tbody',
23817         cn: [
23818         {
23819             tag: 'tr',
23820             cn: [
23821             {
23822                 tag: 'td',
23823                 colspan: '7'
23824             }
23825             ]
23826         }
23827         ]
23828     },
23829     
23830     footer : {
23831         tag: 'tfoot',
23832         cn: [
23833         {
23834             tag: 'tr',
23835             cn: [
23836             {
23837                 tag: 'th',
23838                 colspan: '7',
23839                 cls: 'today'
23840             }
23841                     
23842             ]
23843         }
23844         ]
23845     },
23846     
23847     dates:{
23848         en: {
23849             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23850             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23851             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23852             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23853             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23854             today: "Today"
23855         }
23856     },
23857     
23858     modes: [
23859     {
23860         clsName: 'days',
23861         navFnc: 'Month',
23862         navStep: 1
23863     },
23864     {
23865         clsName: 'months',
23866         navFnc: 'FullYear',
23867         navStep: 1
23868     },
23869     {
23870         clsName: 'years',
23871         navFnc: 'FullYear',
23872         navStep: 10
23873     }]
23874 });
23875
23876 Roo.apply(Roo.bootstrap.form.DateField,  {
23877   
23878     template : {
23879         tag: 'div',
23880         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23881         cn: [
23882         {
23883             tag: 'div',
23884             cls: 'datepicker-days',
23885             cn: [
23886             {
23887                 tag: 'table',
23888                 cls: 'table-condensed',
23889                 cn:[
23890                 Roo.bootstrap.form.DateField.head,
23891                 {
23892                     tag: 'tbody'
23893                 },
23894                 Roo.bootstrap.form.DateField.footer
23895                 ]
23896             }
23897             ]
23898         },
23899         {
23900             tag: 'div',
23901             cls: 'datepicker-months',
23902             cn: [
23903             {
23904                 tag: 'table',
23905                 cls: 'table-condensed',
23906                 cn:[
23907                 Roo.bootstrap.form.DateField.head,
23908                 Roo.bootstrap.form.DateField.content,
23909                 Roo.bootstrap.form.DateField.footer
23910                 ]
23911             }
23912             ]
23913         },
23914         {
23915             tag: 'div',
23916             cls: 'datepicker-years',
23917             cn: [
23918             {
23919                 tag: 'table',
23920                 cls: 'table-condensed',
23921                 cn:[
23922                 Roo.bootstrap.form.DateField.head,
23923                 Roo.bootstrap.form.DateField.content,
23924                 Roo.bootstrap.form.DateField.footer
23925                 ]
23926             }
23927             ]
23928         }
23929         ]
23930     }
23931 });
23932
23933  
23934
23935  /*
23936  * - LGPL
23937  *
23938  * TimeField
23939  * 
23940  */
23941
23942 /**
23943  * @class Roo.bootstrap.form.TimeField
23944  * @extends Roo.bootstrap.form.Input
23945  * Bootstrap DateField class
23946  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23947  * 
23948  * 
23949  * @constructor
23950  * Create a new TimeField
23951  * @param {Object} config The config object
23952  */
23953
23954 Roo.bootstrap.form.TimeField = function(config){
23955     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23956     this.addEvents({
23957             /**
23958              * @event show
23959              * Fires when this field show.
23960              * @param {Roo.bootstrap.form.DateField} thisthis
23961              * @param {Mixed} date The date value
23962              */
23963             show : true,
23964             /**
23965              * @event show
23966              * Fires when this field hide.
23967              * @param {Roo.bootstrap.form.DateField} this
23968              * @param {Mixed} date The date value
23969              */
23970             hide : true,
23971             /**
23972              * @event select
23973              * Fires when select a date.
23974              * @param {Roo.bootstrap.form.DateField} this
23975              * @param {Mixed} date The date value
23976              */
23977             select : true
23978         });
23979 };
23980
23981 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23982     
23983     /**
23984      * @cfg {String} format
23985      * The default time format string which can be overriden for localization support.  The format must be
23986      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23987      */
23988     format : "H:i",
23989     minuteStep : 1,
23990
23991     getAutoCreate : function()
23992     {
23993         this.after = '<i class="fa far fa-clock"></i>';
23994         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23995         
23996          
23997     },
23998     onRender: function(ct, position)
23999     {
24000         
24001         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24002                 
24003         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24004         
24005         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.pop = this.picker().select('>.datepicker-time',true).first();
24008         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24009         
24010         this.picker().on('mousedown', this.onMousedown, this);
24011         this.picker().on('click', this.onClick, this);
24012         
24013         this.picker().addClass('datepicker-dropdown');
24014     
24015         this.fillTime();
24016         this.update();
24017             
24018         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24019         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24020         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24021         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24022         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24023         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24024
24025     },
24026     
24027     fireKey: function(e){
24028         if (!this.picker().isVisible()){
24029             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24030                 this.show();
24031             }
24032             return;
24033         }
24034
24035         e.preventDefault();
24036         
24037         switch(e.keyCode){
24038             case 27: // escape
24039                 this.hide();
24040                 break;
24041             case 37: // left
24042             case 39: // right
24043                 this.onTogglePeriod();
24044                 break;
24045             case 38: // up
24046                 this.onIncrementMinutes();
24047                 break;
24048             case 40: // down
24049                 this.onDecrementMinutes();
24050                 break;
24051             case 13: // enter
24052             case 9: // tab
24053                 this.setTime();
24054                 break;
24055         }
24056     },
24057     
24058     onClick: function(e) {
24059         e.stopPropagation();
24060         e.preventDefault();
24061     },
24062     
24063     picker : function()
24064     {
24065         return this.pickerEl;
24066     },
24067     
24068     fillTime: function()
24069     {    
24070         var time = this.pop.select('tbody', true).first();
24071         
24072         time.dom.innerHTML = '';
24073         
24074         time.createChild({
24075             tag: 'tr',
24076             cn: [
24077                 {
24078                     tag: 'td',
24079                     cn: [
24080                         {
24081                             tag: 'a',
24082                             href: '#',
24083                             cls: 'btn',
24084                             cn: [
24085                                 {
24086                                     tag: 'i',
24087                                     cls: 'hours-up fa fas fa-chevron-up'
24088                                 }
24089                             ]
24090                         } 
24091                     ]
24092                 },
24093                 {
24094                     tag: 'td',
24095                     cls: 'separator'
24096                 },
24097                 {
24098                     tag: 'td',
24099                     cn: [
24100                         {
24101                             tag: 'a',
24102                             href: '#',
24103                             cls: 'btn',
24104                             cn: [
24105                                 {
24106                                     tag: 'i',
24107                                     cls: 'minutes-up fa fas fa-chevron-up'
24108                                 }
24109                             ]
24110                         }
24111                     ]
24112                 },
24113                 {
24114                     tag: 'td',
24115                     cls: 'separator'
24116                 }
24117             ]
24118         });
24119         
24120         time.createChild({
24121             tag: 'tr',
24122             cn: [
24123                 {
24124                     tag: 'td',
24125                     cn: [
24126                         {
24127                             tag: 'span',
24128                             cls: 'timepicker-hour',
24129                             html: '00'
24130                         }  
24131                     ]
24132                 },
24133                 {
24134                     tag: 'td',
24135                     cls: 'separator',
24136                     html: ':'
24137                 },
24138                 {
24139                     tag: 'td',
24140                     cn: [
24141                         {
24142                             tag: 'span',
24143                             cls: 'timepicker-minute',
24144                             html: '00'
24145                         }  
24146                     ]
24147                 },
24148                 {
24149                     tag: 'td',
24150                     cls: 'separator'
24151                 },
24152                 {
24153                     tag: 'td',
24154                     cn: [
24155                         {
24156                             tag: 'button',
24157                             type: 'button',
24158                             cls: 'btn btn-primary period',
24159                             html: 'AM'
24160                             
24161                         }
24162                     ]
24163                 }
24164             ]
24165         });
24166         
24167         time.createChild({
24168             tag: 'tr',
24169             cn: [
24170                 {
24171                     tag: 'td',
24172                     cn: [
24173                         {
24174                             tag: 'a',
24175                             href: '#',
24176                             cls: 'btn',
24177                             cn: [
24178                                 {
24179                                     tag: 'span',
24180                                     cls: 'hours-down fa fas fa-chevron-down'
24181                                 }
24182                             ]
24183                         }
24184                     ]
24185                 },
24186                 {
24187                     tag: 'td',
24188                     cls: 'separator'
24189                 },
24190                 {
24191                     tag: 'td',
24192                     cn: [
24193                         {
24194                             tag: 'a',
24195                             href: '#',
24196                             cls: 'btn',
24197                             cn: [
24198                                 {
24199                                     tag: 'span',
24200                                     cls: 'minutes-down fa fas fa-chevron-down'
24201                                 }
24202                             ]
24203                         }
24204                     ]
24205                 },
24206                 {
24207                     tag: 'td',
24208                     cls: 'separator'
24209                 }
24210             ]
24211         });
24212         
24213     },
24214     
24215     update: function()
24216     {
24217         // default minute is a multiple of minuteStep
24218         if(typeof(this.time) === 'undefined') {
24219             this.time = new Date();
24220             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24221         }
24222         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24223         
24224         this.fill();
24225     },
24226     
24227     fill: function() 
24228     {
24229         var hours = this.time.getHours();
24230         var minutes = this.time.getMinutes();
24231         var period = 'AM';
24232         
24233         if(hours > 11){
24234             period = 'PM';
24235         }
24236         
24237         if(hours == 0){
24238             hours = 12;
24239         }
24240         
24241         
24242         if(hours > 12){
24243             hours = hours - 12;
24244         }
24245         
24246         if(hours < 10){
24247             hours = '0' + hours;
24248         }
24249         
24250         if(minutes < 10){
24251             minutes = '0' + minutes;
24252         }
24253         
24254         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24255         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24256         this.pop.select('button', true).first().dom.innerHTML = period;
24257         
24258     },
24259     
24260     place: function()
24261     {   
24262         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24263         
24264         var cls = ['bottom'];
24265         
24266         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24267             cls.pop();
24268             cls.push('top');
24269         }
24270         
24271         cls.push('right');
24272         
24273         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24274             cls.pop();
24275             cls.push('left');
24276         }
24277         //this.picker().setXY(20000,20000);
24278         this.picker().addClass(cls.join('-'));
24279         
24280         var _this = this;
24281         
24282         Roo.each(cls, function(c){
24283             if(c == 'bottom'){
24284                 (function() {
24285                  //  
24286                 }).defer(200);
24287                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24288                 //_this.picker().setTop(_this.inputEl().getHeight());
24289                 return;
24290             }
24291             if(c == 'top'){
24292                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24293                 
24294                 //_this.picker().setTop(0 - _this.picker().getHeight());
24295                 return;
24296             }
24297             /*
24298             if(c == 'left'){
24299                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24300                 return;
24301             }
24302             if(c == 'right'){
24303                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24304                 return;
24305             }
24306             */
24307         });
24308         
24309     },
24310   
24311     onFocus : function()
24312     {
24313         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24314         this.show();
24315     },
24316     
24317     onBlur : function()
24318     {
24319         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24320         this.hide();
24321     },
24322     
24323     show : function()
24324     {
24325         this.picker().show();
24326         this.pop.show();
24327         this.update();
24328         this.place();
24329         
24330         this.fireEvent('show', this, this.date);
24331     },
24332     
24333     hide : function()
24334     {
24335         this.picker().hide();
24336         this.pop.hide();
24337         
24338         this.fireEvent('hide', this, this.date);
24339     },
24340     
24341     setTime : function()
24342     {
24343         this.hide();
24344         this.setValue(this.time.format(this.format));
24345         
24346         this.fireEvent('select', this, this.date);
24347         
24348         
24349     },
24350     
24351     onMousedown: function(e){
24352         e.stopPropagation();
24353         e.preventDefault();
24354     },
24355     
24356     onIncrementHours: function()
24357     {
24358         Roo.log('onIncrementHours');
24359         this.time = this.time.add(Date.HOUR, 1);
24360         this.update();
24361         
24362     },
24363     
24364     onDecrementHours: function()
24365     {
24366         Roo.log('onDecrementHours');
24367         this.time = this.time.add(Date.HOUR, -1);
24368         this.update();
24369     },
24370     
24371     onIncrementMinutes: function()
24372     {
24373         Roo.log('onIncrementMinutes');
24374         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24375         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24376         this.update();
24377     },
24378     
24379     onDecrementMinutes: function()
24380     {
24381         Roo.log('onDecrementMinutes');
24382         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24383         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24384         this.update();
24385     },
24386     
24387     onTogglePeriod: function()
24388     {
24389         Roo.log('onTogglePeriod');
24390         this.time = this.time.add(Date.HOUR, 12);
24391         this.update();
24392     }
24393     
24394    
24395 });
24396  
24397
24398 Roo.apply(Roo.bootstrap.form.TimeField,  {
24399   
24400     template : {
24401         tag: 'div',
24402         cls: 'datepicker dropdown-menu',
24403         cn: [
24404             {
24405                 tag: 'div',
24406                 cls: 'datepicker-time',
24407                 cn: [
24408                 {
24409                     tag: 'table',
24410                     cls: 'table-condensed',
24411                     cn:[
24412                         {
24413                             tag: 'tbody',
24414                             cn: [
24415                                 {
24416                                     tag: 'tr',
24417                                     cn: [
24418                                     {
24419                                         tag: 'td',
24420                                         colspan: '7'
24421                                     }
24422                                     ]
24423                                 }
24424                             ]
24425                         },
24426                         {
24427                             tag: 'tfoot',
24428                             cn: [
24429                                 {
24430                                     tag: 'tr',
24431                                     cn: [
24432                                     {
24433                                         tag: 'th',
24434                                         colspan: '7',
24435                                         cls: '',
24436                                         cn: [
24437                                             {
24438                                                 tag: 'button',
24439                                                 cls: 'btn btn-info ok',
24440                                                 html: 'OK'
24441                                             }
24442                                         ]
24443                                     }
24444                     
24445                                     ]
24446                                 }
24447                             ]
24448                         }
24449                     ]
24450                 }
24451                 ]
24452             }
24453         ]
24454     }
24455 });
24456
24457  
24458
24459  /*
24460  * - LGPL
24461  *
24462  * MonthField
24463  * 
24464  */
24465
24466 /**
24467  * @class Roo.bootstrap.form.MonthField
24468  * @extends Roo.bootstrap.form.Input
24469  * Bootstrap MonthField class
24470  * 
24471  * @cfg {String} language default en
24472  * 
24473  * @constructor
24474  * Create a new MonthField
24475  * @param {Object} config The config object
24476  */
24477
24478 Roo.bootstrap.form.MonthField = function(config){
24479     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24480     
24481     this.addEvents({
24482         /**
24483          * @event show
24484          * Fires when this field show.
24485          * @param {Roo.bootstrap.form.MonthField} this
24486          * @param {Mixed} date The date value
24487          */
24488         show : true,
24489         /**
24490          * @event show
24491          * Fires when this field hide.
24492          * @param {Roo.bootstrap.form.MonthField} this
24493          * @param {Mixed} date The date value
24494          */
24495         hide : true,
24496         /**
24497          * @event select
24498          * Fires when select a date.
24499          * @param {Roo.bootstrap.form.MonthField} this
24500          * @param {String} oldvalue The old value
24501          * @param {String} newvalue The new value
24502          */
24503         select : true
24504     });
24505 };
24506
24507 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24508     
24509     onRender: function(ct, position)
24510     {
24511         
24512         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24513         
24514         this.language = this.language || 'en';
24515         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24516         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24517         
24518         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24519         this.isInline = false;
24520         this.isInput = true;
24521         this.component = this.el.select('.add-on', true).first() || false;
24522         this.component = (this.component && this.component.length === 0) ? false : this.component;
24523         this.hasInput = this.component && this.inputEL().length;
24524         
24525         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24526         
24527         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24528         
24529         this.picker().on('mousedown', this.onMousedown, this);
24530         this.picker().on('click', this.onClick, this);
24531         
24532         this.picker().addClass('datepicker-dropdown');
24533         
24534         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24535             v.setStyle('width', '189px');
24536         });
24537         
24538         this.fillMonths();
24539         
24540         this.update();
24541         
24542         if(this.isInline) {
24543             this.show();
24544         }
24545         
24546     },
24547     
24548     setValue: function(v, suppressEvent)
24549     {   
24550         var o = this.getValue();
24551         
24552         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24553         
24554         this.update();
24555
24556         if(suppressEvent !== true){
24557             this.fireEvent('select', this, o, v);
24558         }
24559         
24560     },
24561     
24562     getValue: function()
24563     {
24564         return this.value;
24565     },
24566     
24567     onClick: function(e) 
24568     {
24569         e.stopPropagation();
24570         e.preventDefault();
24571         
24572         var target = e.getTarget();
24573         
24574         if(target.nodeName.toLowerCase() === 'i'){
24575             target = Roo.get(target).dom.parentNode;
24576         }
24577         
24578         var nodeName = target.nodeName;
24579         var className = target.className;
24580         var html = target.innerHTML;
24581         
24582         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24583             return;
24584         }
24585         
24586         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24587         
24588         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24589         
24590         this.hide();
24591                         
24592     },
24593     
24594     picker : function()
24595     {
24596         return this.pickerEl;
24597     },
24598     
24599     fillMonths: function()
24600     {    
24601         var i = 0;
24602         var months = this.picker().select('>.datepicker-months td', true).first();
24603         
24604         months.dom.innerHTML = '';
24605         
24606         while (i < 12) {
24607             var month = {
24608                 tag: 'span',
24609                 cls: 'month',
24610                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24611             };
24612             
24613             months.createChild(month);
24614         }
24615         
24616     },
24617     
24618     update: function()
24619     {
24620         var _this = this;
24621         
24622         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24623             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24624         }
24625         
24626         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24627             e.removeClass('active');
24628             
24629             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24630                 e.addClass('active');
24631             }
24632         })
24633     },
24634     
24635     place: function()
24636     {
24637         if(this.isInline) {
24638             return;
24639         }
24640         
24641         this.picker().removeClass(['bottom', 'top']);
24642         
24643         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24644             /*
24645              * place to the top of element!
24646              *
24647              */
24648             
24649             this.picker().addClass('top');
24650             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24651             
24652             return;
24653         }
24654         
24655         this.picker().addClass('bottom');
24656         
24657         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24658     },
24659     
24660     onFocus : function()
24661     {
24662         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24663         this.show();
24664     },
24665     
24666     onBlur : function()
24667     {
24668         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24669         
24670         var d = this.inputEl().getValue();
24671         
24672         this.setValue(d);
24673                 
24674         this.hide();
24675     },
24676     
24677     show : function()
24678     {
24679         this.picker().show();
24680         this.picker().select('>.datepicker-months', true).first().show();
24681         this.update();
24682         this.place();
24683         
24684         this.fireEvent('show', this, this.date);
24685     },
24686     
24687     hide : function()
24688     {
24689         if(this.isInline) {
24690             return;
24691         }
24692         this.picker().hide();
24693         this.fireEvent('hide', this, this.date);
24694         
24695     },
24696     
24697     onMousedown: function(e)
24698     {
24699         e.stopPropagation();
24700         e.preventDefault();
24701     },
24702     
24703     keyup: function(e)
24704     {
24705         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24706         this.update();
24707     },
24708
24709     fireKey: function(e)
24710     {
24711         if (!this.picker().isVisible()){
24712             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24713                 this.show();
24714             }
24715             return;
24716         }
24717         
24718         var dir;
24719         
24720         switch(e.keyCode){
24721             case 27: // escape
24722                 this.hide();
24723                 e.preventDefault();
24724                 break;
24725             case 37: // left
24726             case 39: // right
24727                 dir = e.keyCode == 37 ? -1 : 1;
24728                 
24729                 this.vIndex = this.vIndex + dir;
24730                 
24731                 if(this.vIndex < 0){
24732                     this.vIndex = 0;
24733                 }
24734                 
24735                 if(this.vIndex > 11){
24736                     this.vIndex = 11;
24737                 }
24738                 
24739                 if(isNaN(this.vIndex)){
24740                     this.vIndex = 0;
24741                 }
24742                 
24743                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24744                 
24745                 break;
24746             case 38: // up
24747             case 40: // down
24748                 
24749                 dir = e.keyCode == 38 ? -1 : 1;
24750                 
24751                 this.vIndex = this.vIndex + dir * 4;
24752                 
24753                 if(this.vIndex < 0){
24754                     this.vIndex = 0;
24755                 }
24756                 
24757                 if(this.vIndex > 11){
24758                     this.vIndex = 11;
24759                 }
24760                 
24761                 if(isNaN(this.vIndex)){
24762                     this.vIndex = 0;
24763                 }
24764                 
24765                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24766                 break;
24767                 
24768             case 13: // enter
24769                 
24770                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24771                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24772                 }
24773                 
24774                 this.hide();
24775                 e.preventDefault();
24776                 break;
24777             case 9: // tab
24778                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24779                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24780                 }
24781                 this.hide();
24782                 break;
24783             case 16: // shift
24784             case 17: // ctrl
24785             case 18: // alt
24786                 break;
24787             default :
24788                 this.hide();
24789                 
24790         }
24791     },
24792     
24793     remove: function() 
24794     {
24795         this.picker().remove();
24796     }
24797    
24798 });
24799
24800 Roo.apply(Roo.bootstrap.form.MonthField,  {
24801     
24802     content : {
24803         tag: 'tbody',
24804         cn: [
24805         {
24806             tag: 'tr',
24807             cn: [
24808             {
24809                 tag: 'td',
24810                 colspan: '7'
24811             }
24812             ]
24813         }
24814         ]
24815     },
24816     
24817     dates:{
24818         en: {
24819             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24820             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24821         }
24822     }
24823 });
24824
24825 Roo.apply(Roo.bootstrap.form.MonthField,  {
24826   
24827     template : {
24828         tag: 'div',
24829         cls: 'datepicker dropdown-menu roo-dynamic',
24830         cn: [
24831             {
24832                 tag: 'div',
24833                 cls: 'datepicker-months',
24834                 cn: [
24835                 {
24836                     tag: 'table',
24837                     cls: 'table-condensed',
24838                     cn:[
24839                         Roo.bootstrap.form.DateField.content
24840                     ]
24841                 }
24842                 ]
24843             }
24844         ]
24845     }
24846 });
24847
24848  
24849
24850  
24851  /*
24852  * - LGPL
24853  *
24854  * CheckBox
24855  * 
24856  */
24857
24858 /**
24859  * @class Roo.bootstrap.form.CheckBox
24860  * @extends Roo.bootstrap.form.Input
24861  * Bootstrap CheckBox class
24862  * 
24863  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24864  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24865  * @cfg {String} boxLabel The text that appears beside the checkbox
24866  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24867  * @cfg {Boolean} checked initnal the element
24868  * @cfg {Boolean} inline inline the element (default false)
24869  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24870  * @cfg {String} tooltip label tooltip
24871  * 
24872  * @constructor
24873  * Create a new CheckBox
24874  * @param {Object} config The config object
24875  */
24876
24877 Roo.bootstrap.form.CheckBox = function(config){
24878     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24879    
24880     this.addEvents({
24881         /**
24882         * @event check
24883         * Fires when the element is checked or unchecked.
24884         * @param {Roo.bootstrap.form.CheckBox} this This input
24885         * @param {Boolean} checked The new checked value
24886         */
24887        check : true,
24888        /**
24889         * @event click
24890         * Fires when the element is click.
24891         * @param {Roo.bootstrap.form.CheckBox} this This input
24892         */
24893        click : true
24894     });
24895     
24896 };
24897
24898 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24899   
24900     inputType: 'checkbox',
24901     inputValue: 1,
24902     valueOff: 0,
24903     boxLabel: false,
24904     checked: false,
24905     weight : false,
24906     inline: false,
24907     tooltip : '',
24908     
24909     // checkbox success does not make any sense really.. 
24910     invalidClass : "",
24911     validClass : "",
24912     
24913     
24914     getAutoCreate : function()
24915     {
24916         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24917         
24918         var id = Roo.id();
24919         
24920         var cfg = {};
24921         
24922         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24923         
24924         if(this.inline){
24925             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24926         }
24927         
24928         var input =  {
24929             tag: 'input',
24930             id : id,
24931             type : this.inputType,
24932             value : this.inputValue,
24933             cls : 'roo-' + this.inputType, //'form-box',
24934             placeholder : this.placeholder || ''
24935             
24936         };
24937         
24938         if(this.inputType != 'radio'){
24939             var hidden =  {
24940                 tag: 'input',
24941                 type : 'hidden',
24942                 cls : 'roo-hidden-value',
24943                 value : this.checked ? this.inputValue : this.valueOff
24944             };
24945         }
24946         
24947             
24948         if (this.weight) { // Validity check?
24949             cfg.cls += " " + this.inputType + "-" + this.weight;
24950         }
24951         
24952         if (this.disabled) {
24953             input.disabled=true;
24954         }
24955         
24956         if(this.checked){
24957             input.checked = this.checked;
24958         }
24959         
24960         if (this.name) {
24961             
24962             input.name = this.name;
24963             
24964             if(this.inputType != 'radio'){
24965                 hidden.name = this.name;
24966                 input.name = '_hidden_' + this.name;
24967             }
24968         }
24969         
24970         if (this.size) {
24971             input.cls += ' input-' + this.size;
24972         }
24973         
24974         var settings=this;
24975         
24976         ['xs','sm','md','lg'].map(function(size){
24977             if (settings[size]) {
24978                 cfg.cls += ' col-' + size + '-' + settings[size];
24979             }
24980         });
24981         
24982         var inputblock = input;
24983          
24984         if (this.before || this.after) {
24985             
24986             inputblock = {
24987                 cls : 'input-group',
24988                 cn :  [] 
24989             };
24990             
24991             if (this.before) {
24992                 inputblock.cn.push({
24993                     tag :'span',
24994                     cls : 'input-group-addon',
24995                     html : this.before
24996                 });
24997             }
24998             
24999             inputblock.cn.push(input);
25000             
25001             if(this.inputType != 'radio'){
25002                 inputblock.cn.push(hidden);
25003             }
25004             
25005             if (this.after) {
25006                 inputblock.cn.push({
25007                     tag :'span',
25008                     cls : 'input-group-addon',
25009                     html : this.after
25010                 });
25011             }
25012             
25013         }
25014         var boxLabelCfg = false;
25015         
25016         if(this.boxLabel){
25017            
25018             boxLabelCfg = {
25019                 tag: 'label',
25020                 //'for': id, // box label is handled by onclick - so no for...
25021                 cls: 'box-label',
25022                 html: this.boxLabel
25023             };
25024             if(this.tooltip){
25025                 boxLabelCfg.tooltip = this.tooltip;
25026             }
25027              
25028         }
25029         
25030         
25031         if (align ==='left' && this.fieldLabel.length) {
25032 //                Roo.log("left and has label");
25033             cfg.cn = [
25034                 {
25035                     tag: 'label',
25036                     'for' :  id,
25037                     cls : 'control-label',
25038                     html : this.fieldLabel
25039                 },
25040                 {
25041                     cls : "", 
25042                     cn: [
25043                         inputblock
25044                     ]
25045                 }
25046             ];
25047             
25048             if (boxLabelCfg) {
25049                 cfg.cn[1].cn.push(boxLabelCfg);
25050             }
25051             
25052             if(this.labelWidth > 12){
25053                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25054             }
25055             
25056             if(this.labelWidth < 13 && this.labelmd == 0){
25057                 this.labelmd = this.labelWidth;
25058             }
25059             
25060             if(this.labellg > 0){
25061                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25062                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25063             }
25064             
25065             if(this.labelmd > 0){
25066                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25067                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25068             }
25069             
25070             if(this.labelsm > 0){
25071                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25072                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25073             }
25074             
25075             if(this.labelxs > 0){
25076                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25077                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25078             }
25079             
25080         } else if ( this.fieldLabel.length) {
25081 //                Roo.log(" label");
25082                 cfg.cn = [
25083                    
25084                     {
25085                         tag: this.boxLabel ? 'span' : 'label',
25086                         'for': id,
25087                         cls: 'control-label box-input-label',
25088                         //cls : 'input-group-addon',
25089                         html : this.fieldLabel
25090                     },
25091                     
25092                     inputblock
25093                     
25094                 ];
25095                 if (boxLabelCfg) {
25096                     cfg.cn.push(boxLabelCfg);
25097                 }
25098
25099         } else {
25100             
25101 //                Roo.log(" no label && no align");
25102                 cfg.cn = [  inputblock ] ;
25103                 if (boxLabelCfg) {
25104                     cfg.cn.push(boxLabelCfg);
25105                 }
25106
25107                 
25108         }
25109         
25110        
25111         
25112         if(this.inputType != 'radio'){
25113             cfg.cn.push(hidden);
25114         }
25115         
25116         return cfg;
25117         
25118     },
25119     
25120     /**
25121      * return the real input element.
25122      */
25123     inputEl: function ()
25124     {
25125         return this.el.select('input.roo-' + this.inputType,true).first();
25126     },
25127     hiddenEl: function ()
25128     {
25129         return this.el.select('input.roo-hidden-value',true).first();
25130     },
25131     
25132     labelEl: function()
25133     {
25134         return this.el.select('label.control-label',true).first();
25135     },
25136     /* depricated... */
25137     
25138     label: function()
25139     {
25140         return this.labelEl();
25141     },
25142     
25143     boxLabelEl: function()
25144     {
25145         return this.el.select('label.box-label',true).first();
25146     },
25147     
25148     initEvents : function()
25149     {
25150 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25151         
25152         this.inputEl().on('click', this.onClick,  this);
25153         
25154         if (this.boxLabel) { 
25155             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25156         }
25157         
25158         this.startValue = this.getValue();
25159         
25160         if(this.groupId){
25161             Roo.bootstrap.form.CheckBox.register(this);
25162         }
25163     },
25164     
25165     onClick : function(e)
25166     {   
25167         if(this.fireEvent('click', this, e) !== false){
25168             this.setChecked(!this.checked);
25169         }
25170         
25171     },
25172     
25173     setChecked : function(state,suppressEvent)
25174     {
25175         this.startValue = this.getValue();
25176
25177         if(this.inputType == 'radio'){
25178             
25179             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25180                 e.dom.checked = false;
25181             });
25182             
25183             this.inputEl().dom.checked = true;
25184             
25185             this.inputEl().dom.value = this.inputValue;
25186             
25187             if(suppressEvent !== true){
25188                 this.fireEvent('check', this, true);
25189             }
25190             
25191             this.validate();
25192             
25193             return;
25194         }
25195         
25196         this.checked = state;
25197         
25198         this.inputEl().dom.checked = state;
25199         
25200         
25201         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25202         
25203         if(suppressEvent !== true){
25204             this.fireEvent('check', this, state);
25205         }
25206         
25207         this.validate();
25208     },
25209     
25210     getValue : function()
25211     {
25212         if(this.inputType == 'radio'){
25213             return this.getGroupValue();
25214         }
25215         
25216         return this.hiddenEl().dom.value;
25217         
25218     },
25219     
25220     getGroupValue : function()
25221     {
25222         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25223             return '';
25224         }
25225         
25226         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25227     },
25228     
25229     setValue : function(v,suppressEvent)
25230     {
25231         if(this.inputType == 'radio'){
25232             this.setGroupValue(v, suppressEvent);
25233             return;
25234         }
25235         
25236         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25237         
25238         this.validate();
25239     },
25240     
25241     setGroupValue : function(v, suppressEvent)
25242     {
25243         this.startValue = this.getValue();
25244         
25245         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25246             e.dom.checked = false;
25247             
25248             if(e.dom.value == v){
25249                 e.dom.checked = true;
25250             }
25251         });
25252         
25253         if(suppressEvent !== true){
25254             this.fireEvent('check', this, true);
25255         }
25256
25257         this.validate();
25258         
25259         return;
25260     },
25261     
25262     validate : function()
25263     {
25264         if(this.getVisibilityEl().hasClass('hidden')){
25265             return true;
25266         }
25267         
25268         if(
25269                 this.disabled || 
25270                 (this.inputType == 'radio' && this.validateRadio()) ||
25271                 (this.inputType == 'checkbox' && this.validateCheckbox())
25272         ){
25273             this.markValid();
25274             return true;
25275         }
25276         
25277         this.markInvalid();
25278         return false;
25279     },
25280     
25281     validateRadio : function()
25282     {
25283         if(this.getVisibilityEl().hasClass('hidden')){
25284             return true;
25285         }
25286         
25287         if(this.allowBlank){
25288             return true;
25289         }
25290         
25291         var valid = false;
25292         
25293         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25294             if(!e.dom.checked){
25295                 return;
25296             }
25297             
25298             valid = true;
25299             
25300             return false;
25301         });
25302         
25303         return valid;
25304     },
25305     
25306     validateCheckbox : function()
25307     {
25308         if(!this.groupId){
25309             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25310             //return (this.getValue() == this.inputValue) ? true : false;
25311         }
25312         
25313         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25314         
25315         if(!group){
25316             return false;
25317         }
25318         
25319         var r = false;
25320         
25321         for(var i in group){
25322             if(group[i].el.isVisible(true)){
25323                 r = false;
25324                 break;
25325             }
25326             
25327             r = true;
25328         }
25329         
25330         for(var i in group){
25331             if(r){
25332                 break;
25333             }
25334             
25335             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25336         }
25337         
25338         return r;
25339     },
25340     
25341     /**
25342      * Mark this field as valid
25343      */
25344     markValid : function()
25345     {
25346         var _this = this;
25347         
25348         this.fireEvent('valid', this);
25349         
25350         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25351         
25352         if(this.groupId){
25353             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25354         }
25355         
25356         if(label){
25357             label.markValid();
25358         }
25359
25360         if(this.inputType == 'radio'){
25361             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25362                 var fg = e.findParent('.form-group', false, true);
25363                 if (Roo.bootstrap.version == 3) {
25364                     fg.removeClass([_this.invalidClass, _this.validClass]);
25365                     fg.addClass(_this.validClass);
25366                 } else {
25367                     fg.removeClass(['is-valid', 'is-invalid']);
25368                     fg.addClass('is-valid');
25369                 }
25370             });
25371             
25372             return;
25373         }
25374
25375         if(!this.groupId){
25376             var fg = this.el.findParent('.form-group', false, true);
25377             if (Roo.bootstrap.version == 3) {
25378                 fg.removeClass([this.invalidClass, this.validClass]);
25379                 fg.addClass(this.validClass);
25380             } else {
25381                 fg.removeClass(['is-valid', 'is-invalid']);
25382                 fg.addClass('is-valid');
25383             }
25384             return;
25385         }
25386         
25387         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25388         
25389         if(!group){
25390             return;
25391         }
25392         
25393         for(var i in group){
25394             var fg = group[i].el.findParent('.form-group', false, true);
25395             if (Roo.bootstrap.version == 3) {
25396                 fg.removeClass([this.invalidClass, this.validClass]);
25397                 fg.addClass(this.validClass);
25398             } else {
25399                 fg.removeClass(['is-valid', 'is-invalid']);
25400                 fg.addClass('is-valid');
25401             }
25402         }
25403     },
25404     
25405      /**
25406      * Mark this field as invalid
25407      * @param {String} msg The validation message
25408      */
25409     markInvalid : function(msg)
25410     {
25411         if(this.allowBlank){
25412             return;
25413         }
25414         
25415         var _this = this;
25416         
25417         this.fireEvent('invalid', this, msg);
25418         
25419         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25420         
25421         if(this.groupId){
25422             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25423         }
25424         
25425         if(label){
25426             label.markInvalid();
25427         }
25428             
25429         if(this.inputType == 'radio'){
25430             
25431             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25432                 var fg = e.findParent('.form-group', false, true);
25433                 if (Roo.bootstrap.version == 3) {
25434                     fg.removeClass([_this.invalidClass, _this.validClass]);
25435                     fg.addClass(_this.invalidClass);
25436                 } else {
25437                     fg.removeClass(['is-invalid', 'is-valid']);
25438                     fg.addClass('is-invalid');
25439                 }
25440             });
25441             
25442             return;
25443         }
25444         
25445         if(!this.groupId){
25446             var fg = this.el.findParent('.form-group', false, true);
25447             if (Roo.bootstrap.version == 3) {
25448                 fg.removeClass([_this.invalidClass, _this.validClass]);
25449                 fg.addClass(_this.invalidClass);
25450             } else {
25451                 fg.removeClass(['is-invalid', 'is-valid']);
25452                 fg.addClass('is-invalid');
25453             }
25454             return;
25455         }
25456         
25457         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25458         
25459         if(!group){
25460             return;
25461         }
25462         
25463         for(var i in group){
25464             var fg = group[i].el.findParent('.form-group', false, true);
25465             if (Roo.bootstrap.version == 3) {
25466                 fg.removeClass([_this.invalidClass, _this.validClass]);
25467                 fg.addClass(_this.invalidClass);
25468             } else {
25469                 fg.removeClass(['is-invalid', 'is-valid']);
25470                 fg.addClass('is-invalid');
25471             }
25472         }
25473         
25474     },
25475     
25476     clearInvalid : function()
25477     {
25478         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25479         
25480         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25481         
25482         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25483         
25484         if (label && label.iconEl) {
25485             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25486             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25487         }
25488     },
25489     
25490     disable : function()
25491     {
25492         if(this.inputType != 'radio'){
25493             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25494             return;
25495         }
25496         
25497         var _this = this;
25498         
25499         if(this.rendered){
25500             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25501                 _this.getActionEl().addClass(this.disabledClass);
25502                 e.dom.disabled = true;
25503             });
25504         }
25505         
25506         this.disabled = true;
25507         this.fireEvent("disable", this);
25508         return this;
25509     },
25510
25511     enable : function()
25512     {
25513         if(this.inputType != 'radio'){
25514             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25515             return;
25516         }
25517         
25518         var _this = this;
25519         
25520         if(this.rendered){
25521             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25522                 _this.getActionEl().removeClass(this.disabledClass);
25523                 e.dom.disabled = false;
25524             });
25525         }
25526         
25527         this.disabled = false;
25528         this.fireEvent("enable", this);
25529         return this;
25530     },
25531     
25532     setBoxLabel : function(v)
25533     {
25534         this.boxLabel = v;
25535         
25536         if(this.rendered){
25537             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25538         }
25539     }
25540
25541 });
25542
25543 Roo.apply(Roo.bootstrap.form.CheckBox, {
25544     
25545     groups: {},
25546     
25547      /**
25548     * register a CheckBox Group
25549     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25550     */
25551     register : function(checkbox)
25552     {
25553         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25554             this.groups[checkbox.groupId] = {};
25555         }
25556         
25557         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25558             return;
25559         }
25560         
25561         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25562         
25563     },
25564     /**
25565     * fetch a CheckBox Group based on the group ID
25566     * @param {string} the group ID
25567     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25568     */
25569     get: function(groupId) {
25570         if (typeof(this.groups[groupId]) == 'undefined') {
25571             return false;
25572         }
25573         
25574         return this.groups[groupId] ;
25575     }
25576     
25577     
25578 });
25579 /*
25580  * - LGPL
25581  *
25582  * RadioItem
25583  * 
25584  */
25585
25586 /**
25587  * @class Roo.bootstrap.form.Radio
25588  * @extends Roo.bootstrap.Component
25589  * Bootstrap Radio class
25590  * @cfg {String} boxLabel - the label associated
25591  * @cfg {String} value - the value of radio
25592  * 
25593  * @constructor
25594  * Create a new Radio
25595  * @param {Object} config The config object
25596  */
25597 Roo.bootstrap.form.Radio = function(config){
25598     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25599     
25600 };
25601
25602 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25603     
25604     boxLabel : '',
25605     
25606     value : '',
25607     
25608     getAutoCreate : function()
25609     {
25610         var cfg = {
25611             tag : 'div',
25612             cls : 'form-group radio',
25613             cn : [
25614                 {
25615                     tag : 'label',
25616                     cls : 'box-label',
25617                     html : this.boxLabel
25618                 }
25619             ]
25620         };
25621         
25622         return cfg;
25623     },
25624     
25625     initEvents : function() 
25626     {
25627         this.parent().register(this);
25628         
25629         this.el.on('click', this.onClick, this);
25630         
25631     },
25632     
25633     onClick : function(e)
25634     {
25635         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25636             this.setChecked(true);
25637         }
25638     },
25639     
25640     setChecked : function(state, suppressEvent)
25641     {
25642         this.parent().setValue(this.value, suppressEvent);
25643         
25644     },
25645     
25646     setBoxLabel : function(v)
25647     {
25648         this.boxLabel = v;
25649         
25650         if(this.rendered){
25651             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25652         }
25653     }
25654     
25655 });
25656  
25657
25658  /*
25659  * - LGPL
25660  *
25661  * Input
25662  * 
25663  */
25664
25665 /**
25666  * @class Roo.bootstrap.form.SecurePass
25667  * @extends Roo.bootstrap.form.Input
25668  * Bootstrap SecurePass class
25669  *
25670  * 
25671  * @constructor
25672  * Create a new SecurePass
25673  * @param {Object} config The config object
25674  */
25675  
25676 Roo.bootstrap.form.SecurePass = function (config) {
25677     // these go here, so the translation tool can replace them..
25678     this.errors = {
25679         PwdEmpty: "Please type a password, and then retype it to confirm.",
25680         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25681         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25682         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25683         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25684         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25685         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25686         TooWeak: "Your password is Too Weak."
25687     },
25688     this.meterLabel = "Password strength:";
25689     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25690     this.meterClass = [
25691         "roo-password-meter-tooweak", 
25692         "roo-password-meter-weak", 
25693         "roo-password-meter-medium", 
25694         "roo-password-meter-strong", 
25695         "roo-password-meter-grey"
25696     ];
25697     
25698     this.errors = {};
25699     
25700     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25701 }
25702
25703 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25704     /**
25705      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25706      * {
25707      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25708      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25709      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25710      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25711      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25712      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25713      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25714      * })
25715      */
25716     // private
25717     
25718     meterWidth: 300,
25719     errorMsg :'',    
25720     errors: false,
25721     imageRoot: '/',
25722     /**
25723      * @cfg {String/Object} Label for the strength meter (defaults to
25724      * 'Password strength:')
25725      */
25726     // private
25727     meterLabel: '',
25728     /**
25729      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25730      * ['Weak', 'Medium', 'Strong'])
25731      */
25732     // private    
25733     pwdStrengths: false,    
25734     // private
25735     strength: 0,
25736     // private
25737     _lastPwd: null,
25738     // private
25739     kCapitalLetter: 0,
25740     kSmallLetter: 1,
25741     kDigit: 2,
25742     kPunctuation: 3,
25743     
25744     insecure: false,
25745     // private
25746     initEvents: function ()
25747     {
25748         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25749
25750         if (this.el.is('input[type=password]') && Roo.isSafari) {
25751             this.el.on('keydown', this.SafariOnKeyDown, this);
25752         }
25753
25754         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25755     },
25756     // private
25757     onRender: function (ct, position)
25758     {
25759         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25760         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25761         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25762
25763         this.trigger.createChild({
25764                    cn: [
25765                     {
25766                     //id: 'PwdMeter',
25767                     tag: 'div',
25768                     cls: 'roo-password-meter-grey col-xs-12',
25769                     style: {
25770                         //width: 0,
25771                         //width: this.meterWidth + 'px'                                                
25772                         }
25773                     },
25774                     {                            
25775                          cls: 'roo-password-meter-text'                          
25776                     }
25777                 ]            
25778         });
25779
25780          
25781         if (this.hideTrigger) {
25782             this.trigger.setDisplayed(false);
25783         }
25784         this.setSize(this.width || '', this.height || '');
25785     },
25786     // private
25787     onDestroy: function ()
25788     {
25789         if (this.trigger) {
25790             this.trigger.removeAllListeners();
25791             this.trigger.remove();
25792         }
25793         if (this.wrap) {
25794             this.wrap.remove();
25795         }
25796         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25797     },
25798     // private
25799     checkStrength: function ()
25800     {
25801         var pwd = this.inputEl().getValue();
25802         if (pwd == this._lastPwd) {
25803             return;
25804         }
25805
25806         var strength;
25807         if (this.ClientSideStrongPassword(pwd)) {
25808             strength = 3;
25809         } else if (this.ClientSideMediumPassword(pwd)) {
25810             strength = 2;
25811         } else if (this.ClientSideWeakPassword(pwd)) {
25812             strength = 1;
25813         } else {
25814             strength = 0;
25815         }
25816         
25817         Roo.log('strength1: ' + strength);
25818         
25819         //var pm = this.trigger.child('div/div/div').dom;
25820         var pm = this.trigger.child('div/div');
25821         pm.removeClass(this.meterClass);
25822         pm.addClass(this.meterClass[strength]);
25823                 
25824         
25825         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25826                 
25827         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25828         
25829         this._lastPwd = pwd;
25830     },
25831     reset: function ()
25832     {
25833         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25834         
25835         this._lastPwd = '';
25836         
25837         var pm = this.trigger.child('div/div');
25838         pm.removeClass(this.meterClass);
25839         pm.addClass('roo-password-meter-grey');        
25840         
25841         
25842         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25843         
25844         pt.innerHTML = '';
25845         this.inputEl().dom.type='password';
25846     },
25847     // private
25848     validateValue: function (value)
25849     {
25850         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25851             return false;
25852         }
25853         if (value.length == 0) {
25854             if (this.allowBlank) {
25855                 this.clearInvalid();
25856                 return true;
25857             }
25858
25859             this.markInvalid(this.errors.PwdEmpty);
25860             this.errorMsg = this.errors.PwdEmpty;
25861             return false;
25862         }
25863         
25864         if(this.insecure){
25865             return true;
25866         }
25867         
25868         if (!value.match(/[\x21-\x7e]+/)) {
25869             this.markInvalid(this.errors.PwdBadChar);
25870             this.errorMsg = this.errors.PwdBadChar;
25871             return false;
25872         }
25873         if (value.length < 6) {
25874             this.markInvalid(this.errors.PwdShort);
25875             this.errorMsg = this.errors.PwdShort;
25876             return false;
25877         }
25878         if (value.length > 16) {
25879             this.markInvalid(this.errors.PwdLong);
25880             this.errorMsg = this.errors.PwdLong;
25881             return false;
25882         }
25883         var strength;
25884         if (this.ClientSideStrongPassword(value)) {
25885             strength = 3;
25886         } else if (this.ClientSideMediumPassword(value)) {
25887             strength = 2;
25888         } else if (this.ClientSideWeakPassword(value)) {
25889             strength = 1;
25890         } else {
25891             strength = 0;
25892         }
25893
25894         
25895         if (strength < 2) {
25896             //this.markInvalid(this.errors.TooWeak);
25897             this.errorMsg = this.errors.TooWeak;
25898             //return false;
25899         }
25900         
25901         
25902         console.log('strength2: ' + strength);
25903         
25904         //var pm = this.trigger.child('div/div/div').dom;
25905         
25906         var pm = this.trigger.child('div/div');
25907         pm.removeClass(this.meterClass);
25908         pm.addClass(this.meterClass[strength]);
25909                 
25910         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25911                 
25912         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25913         
25914         this.errorMsg = ''; 
25915         return true;
25916     },
25917     // private
25918     CharacterSetChecks: function (type)
25919     {
25920         this.type = type;
25921         this.fResult = false;
25922     },
25923     // private
25924     isctype: function (character, type)
25925     {
25926         switch (type) {  
25927             case this.kCapitalLetter:
25928                 if (character >= 'A' && character <= 'Z') {
25929                     return true;
25930                 }
25931                 break;
25932             
25933             case this.kSmallLetter:
25934                 if (character >= 'a' && character <= 'z') {
25935                     return true;
25936                 }
25937                 break;
25938             
25939             case this.kDigit:
25940                 if (character >= '0' && character <= '9') {
25941                     return true;
25942                 }
25943                 break;
25944             
25945             case this.kPunctuation:
25946                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25947                     return true;
25948                 }
25949                 break;
25950             
25951             default:
25952                 return false;
25953         }
25954
25955     },
25956     // private
25957     IsLongEnough: function (pwd, size)
25958     {
25959         return !(pwd == null || isNaN(size) || pwd.length < size);
25960     },
25961     // private
25962     SpansEnoughCharacterSets: function (word, nb)
25963     {
25964         if (!this.IsLongEnough(word, nb))
25965         {
25966             return false;
25967         }
25968
25969         var characterSetChecks = new Array(
25970             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25971             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25972         );
25973         
25974         for (var index = 0; index < word.length; ++index) {
25975             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25977                     characterSetChecks[nCharSet].fResult = true;
25978                     break;
25979                 }
25980             }
25981         }
25982
25983         var nCharSets = 0;
25984         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25985             if (characterSetChecks[nCharSet].fResult) {
25986                 ++nCharSets;
25987             }
25988         }
25989
25990         if (nCharSets < nb) {
25991             return false;
25992         }
25993         return true;
25994     },
25995     // private
25996     ClientSideStrongPassword: function (pwd)
25997     {
25998         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25999     },
26000     // private
26001     ClientSideMediumPassword: function (pwd)
26002     {
26003         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26004     },
26005     // private
26006     ClientSideWeakPassword: function (pwd)
26007     {
26008         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26009     }
26010           
26011 });Roo.rtf = {}; // namespace
26012 Roo.rtf.Hex = function(hex)
26013 {
26014     this.hexstr = hex;
26015 };
26016 Roo.rtf.Paragraph = function(opts)
26017 {
26018     this.content = []; ///??? is that used?
26019 };Roo.rtf.Span = function(opts)
26020 {
26021     this.value = opts.value;
26022 };
26023
26024 Roo.rtf.Group = function(parent)
26025 {
26026     // we dont want to acutally store parent - it will make debug a nightmare..
26027     this.content = [];
26028     this.cn  = [];
26029      
26030        
26031     
26032 };
26033
26034 Roo.rtf.Group.prototype = {
26035     ignorable : false,
26036     content: false,
26037     cn: false,
26038     addContent : function(node) {
26039         // could set styles...
26040         this.content.push(node);
26041     },
26042     addChild : function(cn)
26043     {
26044         this.cn.push(cn);
26045     },
26046     // only for images really...
26047     toDataURL : function()
26048     {
26049         var mimetype = false;
26050         switch(true) {
26051             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26052                 mimetype = "image/png";
26053                 break;
26054              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26055                 mimetype = "image/jpeg";
26056                 break;
26057             default :
26058                 return 'about:blank'; // ?? error?
26059         }
26060         
26061         
26062         var hexstring = this.content[this.content.length-1].value;
26063         
26064         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26065             return String.fromCharCode(parseInt(a, 16));
26066         }).join(""));
26067     }
26068     
26069 };
26070 // this looks like it's normally the {rtf{ .... }}
26071 Roo.rtf.Document = function()
26072 {
26073     // we dont want to acutally store parent - it will make debug a nightmare..
26074     this.rtlch  = [];
26075     this.content = [];
26076     this.cn = [];
26077     
26078 };
26079 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26080     addChild : function(cn)
26081     {
26082         this.cn.push(cn);
26083         switch(cn.type) {
26084             case 'rtlch': // most content seems to be inside this??
26085             case 'listtext':
26086             case 'shpinst':
26087                 this.rtlch.push(cn);
26088                 return;
26089             default:
26090                 this[cn.type] = cn;
26091         }
26092         
26093     },
26094     
26095     getElementsByType : function(type)
26096     {
26097         var ret =  [];
26098         this._getElementsByType(type, ret, this.cn, 'rtf');
26099         return ret;
26100     },
26101     _getElementsByType : function (type, ret, search_array, path)
26102     {
26103         search_array.forEach(function(n,i) {
26104             if (n.type == type) {
26105                 n.path = path + '/' + n.type + ':' + i;
26106                 ret.push(n);
26107             }
26108             if (n.cn.length > 0) {
26109                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26110             }
26111         },this);
26112     }
26113     
26114 });
26115  
26116 Roo.rtf.Ctrl = function(opts)
26117 {
26118     this.value = opts.value;
26119     this.param = opts.param;
26120 };
26121 /**
26122  *
26123  *
26124  * based on this https://github.com/iarna/rtf-parser
26125  * it's really only designed to extract pict from pasted RTF 
26126  *
26127  * usage:
26128  *
26129  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26130  *  
26131  *
26132  */
26133
26134  
26135
26136
26137
26138 Roo.rtf.Parser = function(text) {
26139     //super({objectMode: true})
26140     this.text = '';
26141     this.parserState = this.parseText;
26142     
26143     // these are for interpeter...
26144     this.doc = {};
26145     ///this.parserState = this.parseTop
26146     this.groupStack = [];
26147     this.hexStore = [];
26148     this.doc = false;
26149     
26150     this.groups = []; // where we put the return.
26151     
26152     for (var ii = 0; ii < text.length; ++ii) {
26153         ++this.cpos;
26154         
26155         if (text[ii] === '\n') {
26156             ++this.row;
26157             this.col = 1;
26158         } else {
26159             ++this.col;
26160         }
26161         this.parserState(text[ii]);
26162     }
26163     
26164     
26165     
26166 };
26167 Roo.rtf.Parser.prototype = {
26168     text : '', // string being parsed..
26169     controlWord : '',
26170     controlWordParam :  '',
26171     hexChar : '',
26172     doc : false,
26173     group: false,
26174     groupStack : false,
26175     hexStore : false,
26176     
26177     
26178     cpos : 0, 
26179     row : 1, // reportin?
26180     col : 1, //
26181
26182      
26183     push : function (el)
26184     {
26185         var m = 'cmd'+ el.type;
26186         if (typeof(this[m]) == 'undefined') {
26187             Roo.log('invalid cmd:' + el.type);
26188             return;
26189         }
26190         this[m](el);
26191         //Roo.log(el);
26192     },
26193     flushHexStore : function()
26194     {
26195         if (this.hexStore.length < 1) {
26196             return;
26197         }
26198         var hexstr = this.hexStore.map(
26199             function(cmd) {
26200                 return cmd.value;
26201         }).join('');
26202         
26203         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26204               
26205             
26206         this.hexStore.splice(0)
26207         
26208     },
26209     
26210     cmdgroupstart : function()
26211     {
26212         this.flushHexStore();
26213         if (this.group) {
26214             this.groupStack.push(this.group);
26215         }
26216          // parent..
26217         if (this.doc === false) {
26218             this.group = this.doc = new Roo.rtf.Document();
26219             return;
26220             
26221         }
26222         this.group = new Roo.rtf.Group(this.group);
26223     },
26224     cmdignorable : function()
26225     {
26226         this.flushHexStore();
26227         this.group.ignorable = true;
26228     },
26229     cmdendparagraph : function()
26230     {
26231         this.flushHexStore();
26232         this.group.addContent(new Roo.rtf.Paragraph());
26233     },
26234     cmdgroupend : function ()
26235     {
26236         this.flushHexStore();
26237         var endingGroup = this.group;
26238         
26239         
26240         this.group = this.groupStack.pop();
26241         if (this.group) {
26242             this.group.addChild(endingGroup);
26243         }
26244         
26245         
26246         
26247         var doc = this.group || this.doc;
26248         //if (endingGroup instanceof FontTable) {
26249         //  doc.fonts = endingGroup.table
26250         //} else if (endingGroup instanceof ColorTable) {
26251         //  doc.colors = endingGroup.table
26252         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26253         if (endingGroup.ignorable === false) {
26254             //code
26255             this.groups.push(endingGroup);
26256            // Roo.log( endingGroup );
26257         }
26258             //Roo.each(endingGroup.content, function(item)) {
26259             //    doc.addContent(item);
26260             //}
26261             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26262         //}
26263     },
26264     cmdtext : function (cmd)
26265     {
26266         this.flushHexStore();
26267         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26268             //this.group = this.doc
26269             return;  // we really don't care about stray text...
26270         }
26271         this.group.addContent(new Roo.rtf.Span(cmd));
26272     },
26273     cmdcontrolword : function (cmd)
26274     {
26275         this.flushHexStore();
26276         if (!this.group.type) {
26277             this.group.type = cmd.value;
26278             return;
26279         }
26280         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26281         // we actually don't care about ctrl words...
26282         return ;
26283         /*
26284         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26285         if (this[method]) {
26286             this[method](cmd.param)
26287         } else {
26288             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26289         }
26290         */
26291     },
26292     cmdhexchar : function(cmd) {
26293         this.hexStore.push(cmd);
26294     },
26295     cmderror : function(cmd) {
26296         throw cmd.value;
26297     },
26298     
26299     /*
26300       _flush (done) {
26301         if (this.text !== '\u0000') this.emitText()
26302         done()
26303       }
26304       */
26305       
26306       
26307     parseText : function(c)
26308     {
26309         if (c === '\\') {
26310             this.parserState = this.parseEscapes;
26311         } else if (c === '{') {
26312             this.emitStartGroup();
26313         } else if (c === '}') {
26314             this.emitEndGroup();
26315         } else if (c === '\x0A' || c === '\x0D') {
26316             // cr/lf are noise chars
26317         } else {
26318             this.text += c;
26319         }
26320     },
26321     
26322     parseEscapes: function (c)
26323     {
26324         if (c === '\\' || c === '{' || c === '}') {
26325             this.text += c;
26326             this.parserState = this.parseText;
26327         } else {
26328             this.parserState = this.parseControlSymbol;
26329             this.parseControlSymbol(c);
26330         }
26331     },
26332     parseControlSymbol: function(c)
26333     {
26334         if (c === '~') {
26335             this.text += '\u00a0'; // nbsp
26336             this.parserState = this.parseText
26337         } else if (c === '-') {
26338              this.text += '\u00ad'; // soft hyphen
26339         } else if (c === '_') {
26340             this.text += '\u2011'; // non-breaking hyphen
26341         } else if (c === '*') {
26342             this.emitIgnorable();
26343             this.parserState = this.parseText;
26344         } else if (c === "'") {
26345             this.parserState = this.parseHexChar;
26346         } else if (c === '|') { // formula cacter
26347             this.emitFormula();
26348             this.parserState = this.parseText;
26349         } else if (c === ':') { // subentry in an index entry
26350             this.emitIndexSubEntry();
26351             this.parserState = this.parseText;
26352         } else if (c === '\x0a') {
26353             this.emitEndParagraph();
26354             this.parserState = this.parseText;
26355         } else if (c === '\x0d') {
26356             this.emitEndParagraph();
26357             this.parserState = this.parseText;
26358         } else {
26359             this.parserState = this.parseControlWord;
26360             this.parseControlWord(c);
26361         }
26362     },
26363     parseHexChar: function (c)
26364     {
26365         if (/^[A-Fa-f0-9]$/.test(c)) {
26366             this.hexChar += c;
26367             if (this.hexChar.length >= 2) {
26368               this.emitHexChar();
26369               this.parserState = this.parseText;
26370             }
26371             return;
26372         }
26373         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26374         this.parserState = this.parseText;
26375         
26376     },
26377     parseControlWord : function(c)
26378     {
26379         if (c === ' ') {
26380             this.emitControlWord();
26381             this.parserState = this.parseText;
26382         } else if (/^[-\d]$/.test(c)) {
26383             this.parserState = this.parseControlWordParam;
26384             this.controlWordParam += c;
26385         } else if (/^[A-Za-z]$/.test(c)) {
26386           this.controlWord += c;
26387         } else {
26388           this.emitControlWord();
26389           this.parserState = this.parseText;
26390           this.parseText(c);
26391         }
26392     },
26393     parseControlWordParam : function (c) {
26394         if (/^\d$/.test(c)) {
26395           this.controlWordParam += c;
26396         } else if (c === ' ') {
26397           this.emitControlWord();
26398           this.parserState = this.parseText;
26399         } else {
26400           this.emitControlWord();
26401           this.parserState = this.parseText;
26402           this.parseText(c);
26403         }
26404     },
26405     
26406     
26407     
26408     
26409     emitText : function () {
26410         if (this.text === '') {
26411             return;
26412         }
26413         this.push({
26414             type: 'text',
26415             value: this.text,
26416             pos: this.cpos,
26417             row: this.row,
26418             col: this.col
26419         });
26420         this.text = ''
26421     },
26422     emitControlWord : function ()
26423     {
26424         this.emitText();
26425         if (this.controlWord === '') {
26426             // do we want to track this - it seems just to cause problems.
26427             //this.emitError('empty control word');
26428         } else {
26429             this.push({
26430                   type: 'controlword',
26431                   value: this.controlWord,
26432                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26433                   pos: this.cpos,
26434                   row: this.row,
26435                   col: this.col
26436             });
26437         }
26438         this.controlWord = '';
26439         this.controlWordParam = '';
26440     },
26441     emitStartGroup : function ()
26442     {
26443         this.emitText();
26444         this.push({
26445             type: 'groupstart',
26446             pos: this.cpos,
26447             row: this.row,
26448             col: this.col
26449         });
26450     },
26451     emitEndGroup : function ()
26452     {
26453         this.emitText();
26454         this.push({
26455             type: 'groupend',
26456             pos: this.cpos,
26457             row: this.row,
26458             col: this.col
26459         });
26460     },
26461     emitIgnorable : function ()
26462     {
26463         this.emitText();
26464         this.push({
26465             type: 'ignorable',
26466             pos: this.cpos,
26467             row: this.row,
26468             col: this.col
26469         });
26470     },
26471     emitHexChar : function ()
26472     {
26473         this.emitText();
26474         this.push({
26475             type: 'hexchar',
26476             value: this.hexChar,
26477             pos: this.cpos,
26478             row: this.row,
26479             col: this.col
26480         });
26481         this.hexChar = ''
26482     },
26483     emitError : function (message)
26484     {
26485       this.emitText();
26486       this.push({
26487             type: 'error',
26488             value: message,
26489             row: this.row,
26490             col: this.col,
26491             char: this.cpos //,
26492             //stack: new Error().stack
26493         });
26494     },
26495     emitEndParagraph : function () {
26496         this.emitText();
26497         this.push({
26498             type: 'endparagraph',
26499             pos: this.cpos,
26500             row: this.row,
26501             col: this.col
26502         });
26503     }
26504      
26505 } ; 
26506 /**
26507  * @class Roo.htmleditor.Filter
26508  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26509  * @cfg {DomElement} node The node to iterate and filter
26510  * @cfg {boolean|String|Array} tag Tags to replace 
26511  * @constructor
26512  * Create a new Filter.
26513  * @param {Object} config Configuration options
26514  */
26515
26516
26517
26518 Roo.htmleditor.Filter = function(cfg) {
26519     Roo.apply(this.cfg);
26520     // this does not actually call walk as it's really just a abstract class
26521 }
26522
26523
26524 Roo.htmleditor.Filter.prototype = {
26525     
26526     node: false,
26527     
26528     tag: false,
26529
26530     // overrride to do replace comments.
26531     replaceComment : false,
26532     
26533     // overrride to do replace or do stuff with tags..
26534     replaceTag : false,
26535     
26536     walk : function(dom)
26537     {
26538         Roo.each( Array.from(dom.childNodes), function( e ) {
26539             switch(true) {
26540                 
26541                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26542                     this.replaceComment(e);
26543                     return;
26544                 
26545                 case e.nodeType != 1: //not a node.
26546                     return;
26547                 
26548                 case this.tag === true: // everything
26549                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26550                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26551                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26552                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26553                     if (this.replaceTag && false === this.replaceTag(e)) {
26554                         return;
26555                     }
26556                     if (e.hasChildNodes()) {
26557                         this.walk(e);
26558                     }
26559                     return;
26560                 
26561                 default:    // tags .. that do not match.
26562                     if (e.hasChildNodes()) {
26563                         this.walk(e);
26564                     }
26565             }
26566             
26567         }, this);
26568         
26569     },
26570     
26571     
26572     removeNodeKeepChildren : function( node)
26573     {
26574     
26575         ar = Array.from(node.childNodes);
26576         for (var i = 0; i < ar.length; i++) {
26577          
26578             node.removeChild(ar[i]);
26579             // what if we need to walk these???
26580             node.parentNode.insertBefore(ar[i], node);
26581            
26582         }
26583         node.parentNode.removeChild(node);
26584     }
26585 }; 
26586
26587 /**
26588  * @class Roo.htmleditor.FilterAttributes
26589  * clean attributes and  styles including http:// etc.. in attribute
26590  * @constructor
26591 * Run a new Attribute Filter
26592 * @param {Object} config Configuration options
26593  */
26594 Roo.htmleditor.FilterAttributes = function(cfg)
26595 {
26596     Roo.apply(this, cfg);
26597     this.attrib_black = this.attrib_black || [];
26598     this.attrib_white = this.attrib_white || [];
26599
26600     this.attrib_clean = this.attrib_clean || [];
26601     this.style_white = this.style_white || [];
26602     this.style_black = this.style_black || [];
26603     this.walk(cfg.node);
26604 }
26605
26606 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26607 {
26608     tag: true, // all tags
26609     
26610     attrib_black : false, // array
26611     attrib_clean : false,
26612     attrib_white : false,
26613
26614     style_white : false,
26615     style_black : false,
26616      
26617      
26618     replaceTag : function(node)
26619     {
26620         if (!node.attributes || !node.attributes.length) {
26621             return true;
26622         }
26623         
26624         for (var i = node.attributes.length-1; i > -1 ; i--) {
26625             var a = node.attributes[i];
26626             //console.log(a);
26627             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26628                 node.removeAttribute(a.name);
26629                 continue;
26630             }
26631             
26632             
26633             
26634             if (a.name.toLowerCase().substr(0,2)=='on')  {
26635                 node.removeAttribute(a.name);
26636                 continue;
26637             }
26638             
26639             
26640             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26641                 node.removeAttribute(a.name);
26642                 continue;
26643             }
26644             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26645                 this.cleanAttr(node,a.name,a.value); // fixme..
26646                 continue;
26647             }
26648             if (a.name == 'style') {
26649                 this.cleanStyle(node,a.name,a.value);
26650                 continue;
26651             }
26652             /// clean up MS crap..
26653             // tecnically this should be a list of valid class'es..
26654             
26655             
26656             if (a.name == 'class') {
26657                 if (a.value.match(/^Mso/)) {
26658                     node.removeAttribute('class');
26659                 }
26660                 
26661                 if (a.value.match(/^body$/)) {
26662                     node.removeAttribute('class');
26663                 }
26664                 continue;
26665             }
26666             
26667             
26668             // style cleanup!?
26669             // class cleanup?
26670             
26671         }
26672         return true; // clean children
26673     },
26674         
26675     cleanAttr: function(node, n,v)
26676     {
26677         
26678         if (v.match(/^\./) || v.match(/^\//)) {
26679             return;
26680         }
26681         if (v.match(/^(http|https):\/\//)
26682             || v.match(/^mailto:/) 
26683             || v.match(/^ftp:/)
26684             || v.match(/^data:/)
26685             ) {
26686             return;
26687         }
26688         if (v.match(/^#/)) {
26689             return;
26690         }
26691         if (v.match(/^\{/)) { // allow template editing.
26692             return;
26693         }
26694 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26695         node.removeAttribute(n);
26696         
26697     },
26698     cleanStyle : function(node,  n,v)
26699     {
26700         if (v.match(/expression/)) { //XSS?? should we even bother..
26701             node.removeAttribute(n);
26702             return;
26703         }
26704         
26705         var parts = v.split(/;/);
26706         var clean = [];
26707         
26708         Roo.each(parts, function(p) {
26709             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26710             if (!p.length) {
26711                 return true;
26712             }
26713             var l = p.split(':').shift().replace(/\s+/g,'');
26714             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26715             
26716             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26717                 return true;
26718             }
26719             //Roo.log()
26720             // only allow 'c whitelisted system attributes'
26721             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26722                 return true;
26723             }
26724             
26725             
26726             clean.push(p);
26727             return true;
26728         },this);
26729         if (clean.length) { 
26730             node.setAttribute(n, clean.join(';'));
26731         } else {
26732             node.removeAttribute(n);
26733         }
26734         
26735     }
26736         
26737         
26738         
26739     
26740 });/**
26741  * @class Roo.htmleditor.FilterBlack
26742  * remove blacklisted elements.
26743  * @constructor
26744  * Run a new Blacklisted Filter
26745  * @param {Object} config Configuration options
26746  */
26747
26748 Roo.htmleditor.FilterBlack = function(cfg)
26749 {
26750     Roo.apply(this, cfg);
26751     this.walk(cfg.node);
26752 }
26753
26754 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26755 {
26756     tag : true, // all elements.
26757    
26758     replaceTag : function(n)
26759     {
26760         n.parentNode.removeChild(n);
26761     }
26762 });
26763 /**
26764  * @class Roo.htmleditor.FilterComment
26765  * remove comments.
26766  * @constructor
26767 * Run a new Comments Filter
26768 * @param {Object} config Configuration options
26769  */
26770 Roo.htmleditor.FilterComment = function(cfg)
26771 {
26772     this.walk(cfg.node);
26773 }
26774
26775 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26776 {
26777   
26778     replaceComment : function(n)
26779     {
26780         n.parentNode.removeChild(n);
26781     }
26782 });/**
26783  * @class Roo.htmleditor.FilterKeepChildren
26784  * remove tags but keep children
26785  * @constructor
26786  * Run a new Keep Children Filter
26787  * @param {Object} config Configuration options
26788  */
26789
26790 Roo.htmleditor.FilterKeepChildren = function(cfg)
26791 {
26792     Roo.apply(this, cfg);
26793     if (this.tag === false) {
26794         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26795     }
26796     // hacky?
26797     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26798         this.cleanNamespace = true;
26799     }
26800         
26801     this.walk(cfg.node);
26802 }
26803
26804 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26805 {
26806     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26807   
26808     replaceTag : function(node)
26809     {
26810         // walk children...
26811         //Roo.log(node.tagName);
26812         var ar = Array.from(node.childNodes);
26813         //remove first..
26814         
26815         for (var i = 0; i < ar.length; i++) {
26816             var e = ar[i];
26817             if (e.nodeType == 1) {
26818                 if (
26819                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26820                     || // array and it matches
26821                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26822                     ||
26823                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26824                     ||
26825                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26826                 ) {
26827                     this.replaceTag(ar[i]); // child is blacklisted as well...
26828                     continue;
26829                 }
26830             }
26831         }  
26832         ar = Array.from(node.childNodes);
26833         for (var i = 0; i < ar.length; i++) {
26834          
26835             node.removeChild(ar[i]);
26836             // what if we need to walk these???
26837             node.parentNode.insertBefore(ar[i], node);
26838             if (this.tag !== false) {
26839                 this.walk(ar[i]);
26840                 
26841             }
26842         }
26843         //Roo.log("REMOVE:" + node.tagName);
26844         node.parentNode.removeChild(node);
26845         return false; // don't walk children
26846         
26847         
26848     }
26849 });/**
26850  * @class Roo.htmleditor.FilterParagraph
26851  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26852  * like on 'push' to remove the <p> tags and replace them with line breaks.
26853  * @constructor
26854  * Run a new Paragraph Filter
26855  * @param {Object} config Configuration options
26856  */
26857
26858 Roo.htmleditor.FilterParagraph = function(cfg)
26859 {
26860     // no need to apply config.
26861     this.walk(cfg.node);
26862 }
26863
26864 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26865 {
26866     
26867      
26868     tag : 'P',
26869     
26870      
26871     replaceTag : function(node)
26872     {
26873         
26874         if (node.childNodes.length == 1 &&
26875             node.childNodes[0].nodeType == 3 &&
26876             node.childNodes[0].textContent.trim().length < 1
26877             ) {
26878             // remove and replace with '<BR>';
26879             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26880             return false; // no need to walk..
26881         }
26882         var ar = Array.from(node.childNodes);
26883         for (var i = 0; i < ar.length; i++) {
26884             node.removeChild(ar[i]);
26885             // what if we need to walk these???
26886             node.parentNode.insertBefore(ar[i], node);
26887         }
26888         // now what about this?
26889         // <p> &nbsp; </p>
26890         
26891         // double BR.
26892         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26893         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26894         node.parentNode.removeChild(node);
26895         
26896         return false;
26897
26898     }
26899     
26900 });/**
26901  * @class Roo.htmleditor.FilterSpan
26902  * filter span's with no attributes out..
26903  * @constructor
26904  * Run a new Span Filter
26905  * @param {Object} config Configuration options
26906  */
26907
26908 Roo.htmleditor.FilterSpan = function(cfg)
26909 {
26910     // no need to apply config.
26911     this.walk(cfg.node);
26912 }
26913
26914 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26915 {
26916      
26917     tag : 'SPAN',
26918      
26919  
26920     replaceTag : function(node)
26921     {
26922         if (node.attributes && node.attributes.length > 0) {
26923             return true; // walk if there are any.
26924         }
26925         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26926         return false;
26927      
26928     }
26929     
26930 });/**
26931  * @class Roo.htmleditor.FilterTableWidth
26932   try and remove table width data - as that frequently messes up other stuff.
26933  * 
26934  *      was cleanTableWidths.
26935  *
26936  * Quite often pasting from word etc.. results in tables with column and widths.
26937  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26938  *
26939  * @constructor
26940  * Run a new Table Filter
26941  * @param {Object} config Configuration options
26942  */
26943
26944 Roo.htmleditor.FilterTableWidth = function(cfg)
26945 {
26946     // no need to apply config.
26947     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26948     this.walk(cfg.node);
26949 }
26950
26951 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26952 {
26953      
26954      
26955     
26956     replaceTag: function(node) {
26957         
26958         
26959       
26960         if (node.hasAttribute('width')) {
26961             node.removeAttribute('width');
26962         }
26963         
26964          
26965         if (node.hasAttribute("style")) {
26966             // pretty basic...
26967             
26968             var styles = node.getAttribute("style").split(";");
26969             var nstyle = [];
26970             Roo.each(styles, function(s) {
26971                 if (!s.match(/:/)) {
26972                     return;
26973                 }
26974                 var kv = s.split(":");
26975                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26976                     return;
26977                 }
26978                 // what ever is left... we allow.
26979                 nstyle.push(s);
26980             });
26981             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26982             if (!nstyle.length) {
26983                 node.removeAttribute('style');
26984             }
26985         }
26986         
26987         return true; // continue doing children..
26988     }
26989 });/**
26990  * @class Roo.htmleditor.FilterWord
26991  * try and clean up all the mess that Word generates.
26992  * 
26993  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26994  
26995  * @constructor
26996  * Run a new Span Filter
26997  * @param {Object} config Configuration options
26998  */
26999
27000 Roo.htmleditor.FilterWord = function(cfg)
27001 {
27002     // no need to apply config.
27003     this.replaceDocBullets(cfg.node);
27004     
27005     this.replaceAname(cfg.node);
27006     // this is disabled as the removal is done by other filters;
27007    // this.walk(cfg.node);
27008     this.replaceImageTable(cfg.node);
27009     
27010 }
27011
27012 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27013 {
27014     tag: true,
27015      
27016     
27017     /**
27018      * Clean up MS wordisms...
27019      */
27020     replaceTag : function(node)
27021     {
27022          
27023         // no idea what this does - span with text, replaceds with just text.
27024         if(
27025                 node.nodeName == 'SPAN' &&
27026                 !node.hasAttributes() &&
27027                 node.childNodes.length == 1 &&
27028                 node.firstChild.nodeName == "#text"  
27029         ) {
27030             var textNode = node.firstChild;
27031             node.removeChild(textNode);
27032             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27033                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27034             }
27035             node.parentNode.insertBefore(textNode, node);
27036             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27037                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27038             }
27039             
27040             node.parentNode.removeChild(node);
27041             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27042         }
27043         
27044    
27045         
27046         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27047             node.parentNode.removeChild(node);
27048             return false; // dont do chidlren
27049         }
27050         //Roo.log(node.tagName);
27051         // remove - but keep children..
27052         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27053             //Roo.log('-- removed');
27054             while (node.childNodes.length) {
27055                 var cn = node.childNodes[0];
27056                 node.removeChild(cn);
27057                 node.parentNode.insertBefore(cn, node);
27058                 // move node to parent - and clean it..
27059                 if (cn.nodeType == 1) {
27060                     this.replaceTag(cn);
27061                 }
27062                 
27063             }
27064             node.parentNode.removeChild(node);
27065             /// no need to iterate chidlren = it's got none..
27066             //this.iterateChildren(node, this.cleanWord);
27067             return false; // no need to iterate children.
27068         }
27069         // clean styles
27070         if (node.className.length) {
27071             
27072             var cn = node.className.split(/\W+/);
27073             var cna = [];
27074             Roo.each(cn, function(cls) {
27075                 if (cls.match(/Mso[a-zA-Z]+/)) {
27076                     return;
27077                 }
27078                 cna.push(cls);
27079             });
27080             node.className = cna.length ? cna.join(' ') : '';
27081             if (!cna.length) {
27082                 node.removeAttribute("class");
27083             }
27084         }
27085         
27086         if (node.hasAttribute("lang")) {
27087             node.removeAttribute("lang");
27088         }
27089         
27090         if (node.hasAttribute("style")) {
27091             
27092             var styles = node.getAttribute("style").split(";");
27093             var nstyle = [];
27094             Roo.each(styles, function(s) {
27095                 if (!s.match(/:/)) {
27096                     return;
27097                 }
27098                 var kv = s.split(":");
27099                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27100                     return;
27101                 }
27102                 // what ever is left... we allow.
27103                 nstyle.push(s);
27104             });
27105             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27106             if (!nstyle.length) {
27107                 node.removeAttribute('style');
27108             }
27109         }
27110         return true; // do children
27111         
27112         
27113         
27114     },
27115     
27116     styleToObject: function(node)
27117     {
27118         var styles = (node.getAttribute("style") || '').split(";");
27119         var ret = {};
27120         Roo.each(styles, function(s) {
27121             if (!s.match(/:/)) {
27122                 return;
27123             }
27124             var kv = s.split(":");
27125              
27126             // what ever is left... we allow.
27127             ret[kv[0].trim()] = kv[1];
27128         });
27129         return ret;
27130     },
27131     
27132     
27133     replaceAname : function (doc)
27134     {
27135         // replace all the a/name without..
27136         var aa = Array.from(doc.getElementsByTagName('a'));
27137         for (var i = 0; i  < aa.length; i++) {
27138             var a = aa[i];
27139             if (a.hasAttribute("name")) {
27140                 a.removeAttribute("name");
27141             }
27142             if (a.hasAttribute("href")) {
27143                 continue;
27144             }
27145             // reparent children.
27146             this.removeNodeKeepChildren(a);
27147             
27148         }
27149         
27150         
27151         
27152     },
27153
27154     
27155     
27156     replaceDocBullets : function(doc)
27157     {
27158         // this is a bit odd - but it appears some indents use ql-indent-1
27159          //Roo.log(doc.innerHTML);
27160         
27161         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27162         for( var i = 0; i < listpara.length; i ++) {
27163             listpara[i].className = "MsoListParagraph";
27164         }
27165         
27166         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27167         for( var i = 0; i < listpara.length; i ++) {
27168             listpara[i].className = "MsoListParagraph";
27169         }
27170         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27171         for( var i = 0; i < listpara.length; i ++) {
27172             listpara[i].className = "MsoListParagraph";
27173         }
27174         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27175         for( var i = 0; i < listpara.length; i ++) {
27176             listpara[i].className = "MsoListParagraph";
27177         }
27178         
27179         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27180         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27181         for( var i = 0; i < htwo.length; i ++) {
27182             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27183                 htwo[i].className = "MsoListParagraph";
27184             }
27185         }
27186         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27187         for( var i = 0; i < listpara.length; i ++) {
27188             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27189                 listpara[i].className = "MsoListParagraph";
27190             } else {
27191                 listpara[i].className = "MsoNormalx";
27192             }
27193         }
27194        
27195         listpara = doc.getElementsByClassName('MsoListParagraph');
27196         // Roo.log(doc.innerHTML);
27197         
27198         
27199         
27200         while(listpara.length) {
27201             
27202             this.replaceDocBullet(listpara.item(0));
27203         }
27204       
27205     },
27206     
27207      
27208     
27209     replaceDocBullet : function(p)
27210     {
27211         // gather all the siblings.
27212         var ns = p,
27213             parent = p.parentNode,
27214             doc = parent.ownerDocument,
27215             items = [];
27216          
27217         //Roo.log("Parsing: " + p.innerText)    ;
27218         var listtype = 'ul';   
27219         while (ns) {
27220             if (ns.nodeType != 1) {
27221                 ns = ns.nextSibling;
27222                 continue;
27223             }
27224             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27225                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27226                 break;
27227             }
27228             var spans = ns.getElementsByTagName('span');
27229             
27230             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27231                 items.push(ns);
27232                 ns = ns.nextSibling;
27233                 has_list = true;
27234                 if (!spans.length) {
27235                     continue;
27236                 }
27237                 var ff = '';
27238                 var se = spans[0];
27239                 for (var i = 0; i < spans.length;i++) {
27240                     se = spans[i];
27241                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27242                         ff = se.style.fontFamily;
27243                         break;
27244                     }
27245                 }
27246                  
27247                     
27248                 //Roo.log("got font family: " + ff);
27249                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27250                     listtype = 'ol';
27251                 }
27252                 
27253                 continue;
27254             }
27255             //Roo.log("no mso-list?");
27256             
27257             var spans = ns.getElementsByTagName('span');
27258             if (!spans.length) {
27259                 break;
27260             }
27261             var has_list  = false;
27262             for(var i = 0; i < spans.length; i++) {
27263                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27264                     has_list = true;
27265                     break;
27266                 }
27267             }
27268             if (!has_list) {
27269                 break;
27270             }
27271             items.push(ns);
27272             ns = ns.nextSibling;
27273             
27274             
27275         }
27276         if (!items.length) {
27277             ns.className = "";
27278             return;
27279         }
27280         
27281         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27282         parent.insertBefore(ul, p);
27283         var lvl = 0;
27284         var stack = [ ul ];
27285         var last_li = false;
27286         
27287         var margin_to_depth = {};
27288         max_margins = -1;
27289         
27290         items.forEach(function(n, ipos) {
27291             //Roo.log("got innertHMLT=" + n.innerHTML);
27292             
27293             var spans = n.getElementsByTagName('span');
27294             if (!spans.length) {
27295                 //Roo.log("No spans found");
27296                  
27297                 parent.removeChild(n);
27298                 
27299                 
27300                 return; // skip it...
27301             }
27302            
27303                 
27304             var num = 1;
27305             var style = {};
27306             for(var i = 0; i < spans.length; i++) {
27307             
27308                 style = this.styleToObject(spans[i]);
27309                 if (typeof(style['mso-list']) == 'undefined') {
27310                     continue;
27311                 }
27312                 if (listtype == 'ol') {
27313                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27314                 }
27315                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27316                 break;
27317             }
27318             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27319             style = this.styleToObject(n); // mo-list is from the parent node.
27320             if (typeof(style['mso-list']) == 'undefined') {
27321                 //Roo.log("parent is missing level");
27322                   
27323                 parent.removeChild(n);
27324                  
27325                 return;
27326             }
27327             
27328             var margin = style['margin-left'];
27329             if (typeof(margin_to_depth[margin]) == 'undefined') {
27330                 max_margins++;
27331                 margin_to_depth[margin] = max_margins;
27332             }
27333             nlvl = margin_to_depth[margin] ;
27334              
27335             if (nlvl > lvl) {
27336                 //new indent
27337                 var nul = doc.createElement(listtype); // what about number lists...
27338                 if (!last_li) {
27339                     last_li = doc.createElement('li');
27340                     stack[lvl].appendChild(last_li);
27341                 }
27342                 last_li.appendChild(nul);
27343                 stack[nlvl] = nul;
27344                 
27345             }
27346             lvl = nlvl;
27347             
27348             // not starting at 1..
27349             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27350                 stack[nlvl].setAttribute("start", num);
27351             }
27352             
27353             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27354             last_li = nli;
27355             nli.innerHTML = n.innerHTML;
27356             //Roo.log("innerHTML = " + n.innerHTML);
27357             parent.removeChild(n);
27358             
27359              
27360              
27361             
27362         },this);
27363         
27364         
27365         
27366         
27367     },
27368     
27369     replaceImageTable : function(doc)
27370     {
27371          /*
27372           <table cellpadding=0 cellspacing=0 align=left>
27373   <tr>
27374    <td width=423 height=0></td>
27375   </tr>
27376   <tr>
27377    <td></td>
27378    <td><img width=601 height=401
27379    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27380    v:shapes="Picture_x0020_2"></td>
27381   </tr>
27382  </table>
27383  */
27384         var imgs = Array.from(doc.getElementsByTagName('img'));
27385         Roo.each(imgs, function(img) {
27386             var td = img.parentNode;
27387             if (td.nodeName !=  'TD') {
27388                 return;
27389             }
27390             var tr = td.parentNode;
27391             if (tr.nodeName !=  'TR') {
27392                 return;
27393             }
27394             var tbody = tr.parentNode;
27395             if (tbody.nodeName !=  'TBODY') {
27396                 return;
27397             }
27398             var table = tbody.parentNode;
27399             if (table.nodeName !=  'TABLE') {
27400                 return;
27401             }
27402             // first row..
27403             
27404             if (table.getElementsByTagName('tr').length != 2) {
27405                 return;
27406             }
27407             if (table.getElementsByTagName('td').length != 3) {
27408                 return;
27409             }
27410             if (table.innerText.trim() != '') {
27411                 return;
27412             }
27413             var p = table.parentNode;
27414             img.parentNode.removeChild(img);
27415             p.insertBefore(img, table);
27416             p.removeChild(table);
27417             
27418             
27419             
27420         });
27421         
27422       
27423     }
27424     
27425 });
27426 /**
27427  * @class Roo.htmleditor.FilterStyleToTag
27428  * part of the word stuff... - certain 'styles' should be converted to tags.
27429  * eg.
27430  *   font-weight: bold -> bold
27431  *   ?? super / subscrit etc..
27432  * 
27433  * @constructor
27434 * Run a new style to tag filter.
27435 * @param {Object} config Configuration options
27436  */
27437 Roo.htmleditor.FilterStyleToTag = function(cfg)
27438 {
27439     
27440     this.tags = {
27441         B  : [ 'fontWeight' , 'bold'],
27442         I :  [ 'fontStyle' , 'italic'],
27443         //pre :  [ 'font-style' , 'italic'],
27444         // h1.. h6 ?? font-size?
27445         SUP : [ 'verticalAlign' , 'super' ],
27446         SUB : [ 'verticalAlign' , 'sub' ]
27447         
27448         
27449     };
27450     
27451     Roo.apply(this, cfg);
27452      
27453     
27454     this.walk(cfg.node);
27455     
27456     
27457     
27458 }
27459
27460
27461 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27462 {
27463     tag: true, // all tags
27464     
27465     tags : false,
27466     
27467     
27468     replaceTag : function(node)
27469     {
27470         
27471         
27472         if (node.getAttribute("style") === null) {
27473             return true;
27474         }
27475         var inject = [];
27476         for (var k in this.tags) {
27477             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27478                 inject.push(k);
27479                 node.style.removeProperty(this.tags[k][0]);
27480             }
27481         }
27482         if (!inject.length) {
27483             return true; 
27484         }
27485         var cn = Array.from(node.childNodes);
27486         var nn = node;
27487         Roo.each(inject, function(t) {
27488             var nc = node.ownerDocument.createElement(t);
27489             nn.appendChild(nc);
27490             nn = nc;
27491         });
27492         for(var i = 0;i < cn.length;cn++) {
27493             node.removeChild(cn[i]);
27494             nn.appendChild(cn[i]);
27495         }
27496         return true /// iterate thru
27497     }
27498     
27499 })/**
27500  * @class Roo.htmleditor.FilterLongBr
27501  * BR/BR/BR - keep a maximum of 2...
27502  * @constructor
27503  * Run a new Long BR Filter
27504  * @param {Object} config Configuration options
27505  */
27506
27507 Roo.htmleditor.FilterLongBr = function(cfg)
27508 {
27509     // no need to apply config.
27510     this.walk(cfg.node);
27511 }
27512
27513 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27514 {
27515     
27516      
27517     tag : 'BR',
27518     
27519      
27520     replaceTag : function(node)
27521     {
27522         
27523         var ps = node.nextSibling;
27524         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27525             ps = ps.nextSibling;
27526         }
27527         
27528         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27529             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27530             return false;
27531         }
27532         
27533         if (!ps || ps.nodeType != 1) {
27534             return false;
27535         }
27536         
27537         if (!ps || ps.tagName != 'BR') {
27538            
27539             return false;
27540         }
27541         
27542         
27543         
27544         
27545         
27546         if (!node.previousSibling) {
27547             return false;
27548         }
27549         var ps = node.previousSibling;
27550         
27551         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27552             ps = ps.previousSibling;
27553         }
27554         if (!ps || ps.nodeType != 1) {
27555             return false;
27556         }
27557         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27558         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27559             return false;
27560         }
27561         
27562         node.parentNode.removeChild(node); // remove me...
27563         
27564         return false; // no need to do children
27565
27566     }
27567     
27568 }); 
27569
27570 /**
27571  * @class Roo.htmleditor.FilterBlock
27572  * removes id / data-block and contenteditable that are associated with blocks
27573  * usage should be done on a cloned copy of the dom
27574  * @constructor
27575 * Run a new Attribute Filter { node : xxxx }}
27576 * @param {Object} config Configuration options
27577  */
27578 Roo.htmleditor.FilterBlock = function(cfg)
27579 {
27580     Roo.apply(this, cfg);
27581     var qa = cfg.node.querySelectorAll;
27582     this.removeAttributes('data-block');
27583     this.removeAttributes('contenteditable');
27584     this.removeAttributes('id');
27585     
27586 }
27587
27588 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27589 {
27590     node: true, // all tags
27591      
27592      
27593     removeAttributes : function(attr)
27594     {
27595         var ar = this.node.querySelectorAll('*[' + attr + ']');
27596         for (var i =0;i<ar.length;i++) {
27597             ar[i].removeAttribute(attr);
27598         }
27599     }
27600         
27601         
27602         
27603     
27604 });
27605 /***
27606  * This is based loosely on tinymce 
27607  * @class Roo.htmleditor.TidySerializer
27608  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27609  * @constructor
27610  * @method Serializer
27611  * @param {Object} settings Name/value settings object.
27612  */
27613
27614
27615 Roo.htmleditor.TidySerializer = function(settings)
27616 {
27617     Roo.apply(this, settings);
27618     
27619     this.writer = new Roo.htmleditor.TidyWriter(settings);
27620     
27621     
27622
27623 };
27624 Roo.htmleditor.TidySerializer.prototype = {
27625     
27626     /**
27627      * @param {boolean} inner do the inner of the node.
27628      */
27629     inner : false,
27630     
27631     writer : false,
27632     
27633     /**
27634     * Serializes the specified node into a string.
27635     *
27636     * @example
27637     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27638     * @method serialize
27639     * @param {DomElement} node Node instance to serialize.
27640     * @return {String} String with HTML based on DOM tree.
27641     */
27642     serialize : function(node) {
27643         
27644         // = settings.validate;
27645         var writer = this.writer;
27646         var self  = this;
27647         this.handlers = {
27648             // #text
27649             3: function(node) {
27650                 
27651                 writer.text(node.nodeValue, node);
27652             },
27653             // #comment
27654             8: function(node) {
27655                 writer.comment(node.nodeValue);
27656             },
27657             // Processing instruction
27658             7: function(node) {
27659                 writer.pi(node.name, node.nodeValue);
27660             },
27661             // Doctype
27662             10: function(node) {
27663                 writer.doctype(node.nodeValue);
27664             },
27665             // CDATA
27666             4: function(node) {
27667                 writer.cdata(node.nodeValue);
27668             },
27669             // Document fragment
27670             11: function(node) {
27671                 node = node.firstChild;
27672                 if (!node) {
27673                     return;
27674                 }
27675                 while(node) {
27676                     self.walk(node);
27677                     node = node.nextSibling
27678                 }
27679             }
27680         };
27681         writer.reset();
27682         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27683         return writer.getContent();
27684     },
27685
27686     walk: function(node)
27687     {
27688         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27689             handler = this.handlers[node.nodeType];
27690             
27691         if (handler) {
27692             handler(node);
27693             return;
27694         }
27695     
27696         var name = node.nodeName;
27697         var isEmpty = node.childNodes.length < 1;
27698       
27699         var writer = this.writer;
27700         var attrs = node.attributes;
27701         // Sort attributes
27702         
27703         writer.start(node.nodeName, attrs, isEmpty, node);
27704         if (isEmpty) {
27705             return;
27706         }
27707         node = node.firstChild;
27708         if (!node) {
27709             writer.end(name);
27710             return;
27711         }
27712         while (node) {
27713             this.walk(node);
27714             node = node.nextSibling;
27715         }
27716         writer.end(name);
27717         
27718     
27719     }
27720     // Serialize element and treat all non elements as fragments
27721    
27722 }; 
27723
27724 /***
27725  * This is based loosely on tinymce 
27726  * @class Roo.htmleditor.TidyWriter
27727  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27728  *
27729  * Known issues?
27730  * - not tested much with 'PRE' formated elements.
27731  * 
27732  *
27733  *
27734  */
27735
27736 Roo.htmleditor.TidyWriter = function(settings)
27737 {
27738     
27739     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27740     Roo.apply(this, settings);
27741     this.html = [];
27742     this.state = [];
27743      
27744     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27745   
27746 }
27747 Roo.htmleditor.TidyWriter.prototype = {
27748
27749  
27750     state : false,
27751     
27752     indent :  '  ',
27753     
27754     // part of state...
27755     indentstr : '',
27756     in_pre: false,
27757     in_inline : false,
27758     last_inline : false,
27759     encode : false,
27760      
27761     
27762             /**
27763     * Writes the a start element such as <p id="a">.
27764     *
27765     * @method start
27766     * @param {String} name Name of the element.
27767     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27768     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27769     */
27770     start: function(name, attrs, empty, node)
27771     {
27772         var i, l, attr, value;
27773         
27774         // there are some situations where adding line break && indentation will not work. will not work.
27775         // <span / b / i ... formating?
27776         
27777         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27778         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27779         
27780         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27781         
27782         var add_lb = name == 'BR' ? false : in_inline;
27783         
27784         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27785             i_inline = false;
27786         }
27787
27788         var indentstr =  this.indentstr;
27789         
27790         // e_inline = elements that can be inline, but still allow \n before and after?
27791         // only 'BR' ??? any others?
27792         
27793         // ADD LINE BEFORE tage
27794         if (!this.in_pre) {
27795             if (in_inline) {
27796                 //code
27797                 if (name == 'BR') {
27798                     this.addLine();
27799                 } else if (this.lastElementEndsWS()) {
27800                     this.addLine();
27801                 } else{
27802                     // otherwise - no new line. (and dont indent.)
27803                     indentstr = '';
27804                 }
27805                 
27806             } else {
27807                 this.addLine();
27808             }
27809         } else {
27810             indentstr = '';
27811         }
27812         
27813         this.html.push(indentstr + '<', name.toLowerCase());
27814         
27815         if (attrs) {
27816             for (i = 0, l = attrs.length; i < l; i++) {
27817                 attr = attrs[i];
27818                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27819             }
27820         }
27821      
27822         if (empty) {
27823             if (is_short) {
27824                 this.html[this.html.length] = '/>';
27825             } else {
27826                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27827             }
27828             var e_inline = name == 'BR' ? false : this.in_inline;
27829             
27830             if (!e_inline && !this.in_pre) {
27831                 this.addLine();
27832             }
27833             return;
27834         
27835         }
27836         // not empty..
27837         this.html[this.html.length] = '>';
27838         
27839         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27840         /*
27841         if (!in_inline && !in_pre) {
27842             var cn = node.firstChild;
27843             while(cn) {
27844                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27845                     in_inline = true
27846                     break;
27847                 }
27848                 cn = cn.nextSibling;
27849             }
27850              
27851         }
27852         */
27853         
27854         
27855         this.pushState({
27856             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27857             in_pre : in_pre,
27858             in_inline :  in_inline
27859         });
27860         // add a line after if we are not in a
27861         
27862         if (!in_inline && !in_pre) {
27863             this.addLine();
27864         }
27865         
27866             
27867          
27868         
27869     },
27870     
27871     lastElementEndsWS : function()
27872     {
27873         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27874         if (value === false) {
27875             return true;
27876         }
27877         return value.match(/\s+$/);
27878         
27879     },
27880     
27881     /**
27882      * Writes the a end element such as </p>.
27883      *
27884      * @method end
27885      * @param {String} name Name of the element.
27886      */
27887     end: function(name) {
27888         var value;
27889         this.popState();
27890         var indentstr = '';
27891         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27892         
27893         if (!this.in_pre && !in_inline) {
27894             this.addLine();
27895             indentstr  = this.indentstr;
27896         }
27897         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27898         this.last_inline = in_inline;
27899         
27900         // pop the indent state..
27901     },
27902     /**
27903      * Writes a text node.
27904      *
27905      * In pre - we should not mess with the contents.
27906      * 
27907      *
27908      * @method text
27909      * @param {String} text String to write out.
27910      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27911      */
27912     text: function(in_text, node)
27913     {
27914         // if not in whitespace critical
27915         if (in_text.length < 1) {
27916             return;
27917         }
27918         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27919         
27920         if (this.in_pre) {
27921             this.html[this.html.length] =  text;
27922             return;   
27923         }
27924         
27925         if (this.in_inline) {
27926             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27927             if (text != ' ') {
27928                 text = text.replace(/\s+/,' ');  // all white space to single white space
27929                 
27930                     
27931                 // if next tag is '<BR>', then we can trim right..
27932                 if (node.nextSibling &&
27933                     node.nextSibling.nodeType == 1 &&
27934                     node.nextSibling.nodeName == 'BR' )
27935                 {
27936                     text = text.replace(/\s+$/g,'');
27937                 }
27938                 // if previous tag was a BR, we can also trim..
27939                 if (node.previousSibling &&
27940                     node.previousSibling.nodeType == 1 &&
27941                     node.previousSibling.nodeName == 'BR' )
27942                 {
27943                     text = this.indentstr +  text.replace(/^\s+/g,'');
27944                 }
27945                 if (text.match(/\n/)) {
27946                     text = text.replace(
27947                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27948                     );
27949                     // remoeve the last whitespace / line break.
27950                     text = text.replace(/\n\s+$/,'');
27951                 }
27952                 // repace long lines
27953                 
27954             }
27955              
27956             this.html[this.html.length] =  text;
27957             return;   
27958         }
27959         // see if previous element was a inline element.
27960         var indentstr = this.indentstr;
27961    
27962         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27963         
27964         // should trim left?
27965         if (node.previousSibling &&
27966             node.previousSibling.nodeType == 1 &&
27967             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27968         {
27969             indentstr = '';
27970             
27971         } else {
27972             this.addLine();
27973             text = text.replace(/^\s+/,''); // trim left
27974           
27975         }
27976         // should trim right?
27977         if (node.nextSibling &&
27978             node.nextSibling.nodeType == 1 &&
27979             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27980         {
27981           // noop
27982             
27983         }  else {
27984             text = text.replace(/\s+$/,''); // trim right
27985         }
27986          
27987               
27988         
27989         
27990         
27991         if (text.length < 1) {
27992             return;
27993         }
27994         if (!text.match(/\n/)) {
27995             this.html.push(indentstr + text);
27996             return;
27997         }
27998         
27999         text = this.indentstr + text.replace(
28000             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28001         );
28002         // remoeve the last whitespace / line break.
28003         text = text.replace(/\s+$/,''); 
28004         
28005         this.html.push(text);
28006         
28007         // split and indent..
28008         
28009         
28010     },
28011     /**
28012      * Writes a cdata node such as <![CDATA[data]]>.
28013      *
28014      * @method cdata
28015      * @param {String} text String to write out inside the cdata.
28016      */
28017     cdata: function(text) {
28018         this.html.push('<![CDATA[', text, ']]>');
28019     },
28020     /**
28021     * Writes a comment node such as <!-- Comment -->.
28022     *
28023     * @method cdata
28024     * @param {String} text String to write out inside the comment.
28025     */
28026    comment: function(text) {
28027        this.html.push('<!--', text, '-->');
28028    },
28029     /**
28030      * Writes a PI node such as <?xml attr="value" ?>.
28031      *
28032      * @method pi
28033      * @param {String} name Name of the pi.
28034      * @param {String} text String to write out inside the pi.
28035      */
28036     pi: function(name, text) {
28037         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28038         this.indent != '' && this.html.push('\n');
28039     },
28040     /**
28041      * Writes a doctype node such as <!DOCTYPE data>.
28042      *
28043      * @method doctype
28044      * @param {String} text String to write out inside the doctype.
28045      */
28046     doctype: function(text) {
28047         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28048     },
28049     /**
28050      * Resets the internal buffer if one wants to reuse the writer.
28051      *
28052      * @method reset
28053      */
28054     reset: function() {
28055         this.html.length = 0;
28056         this.state = [];
28057         this.pushState({
28058             indentstr : '',
28059             in_pre : false, 
28060             in_inline : false
28061         })
28062     },
28063     /**
28064      * Returns the contents that got serialized.
28065      *
28066      * @method getContent
28067      * @return {String} HTML contents that got written down.
28068      */
28069     getContent: function() {
28070         return this.html.join('').replace(/\n$/, '');
28071     },
28072     
28073     pushState : function(cfg)
28074     {
28075         this.state.push(cfg);
28076         Roo.apply(this, cfg);
28077     },
28078     
28079     popState : function()
28080     {
28081         if (this.state.length < 1) {
28082             return; // nothing to push
28083         }
28084         var cfg = {
28085             in_pre: false,
28086             indentstr : ''
28087         };
28088         this.state.pop();
28089         if (this.state.length > 0) {
28090             cfg = this.state[this.state.length-1]; 
28091         }
28092         Roo.apply(this, cfg);
28093     },
28094     
28095     addLine: function()
28096     {
28097         if (this.html.length < 1) {
28098             return;
28099         }
28100         
28101         
28102         var value = this.html[this.html.length - 1];
28103         if (value.length > 0 && '\n' !== value) {
28104             this.html.push('\n');
28105         }
28106     }
28107     
28108     
28109 //'pre script noscript style textarea video audio iframe object code'
28110 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28111 // inline 
28112 };
28113
28114 Roo.htmleditor.TidyWriter.inline_elements = [
28115         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28116         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28117 ];
28118 Roo.htmleditor.TidyWriter.shortend_elements = [
28119     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28120     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28121 ];
28122
28123 Roo.htmleditor.TidyWriter.whitespace_elements = [
28124     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28125 ];/***
28126  * This is based loosely on tinymce 
28127  * @class Roo.htmleditor.TidyEntities
28128  * @static
28129  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28130  *
28131  * Not 100% sure this is actually used or needed.
28132  */
28133
28134 Roo.htmleditor.TidyEntities = {
28135     
28136     /**
28137      * initialize data..
28138      */
28139     init : function (){
28140      
28141         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28142        
28143     },
28144
28145
28146     buildEntitiesLookup: function(items, radix) {
28147         var i, chr, entity, lookup = {};
28148         if (!items) {
28149             return {};
28150         }
28151         items = typeof(items) == 'string' ? items.split(',') : items;
28152         radix = radix || 10;
28153         // Build entities lookup table
28154         for (i = 0; i < items.length; i += 2) {
28155             chr = String.fromCharCode(parseInt(items[i], radix));
28156             // Only add non base entities
28157             if (!this.baseEntities[chr]) {
28158                 entity = '&' + items[i + 1] + ';';
28159                 lookup[chr] = entity;
28160                 lookup[entity] = chr;
28161             }
28162         }
28163         return lookup;
28164         
28165     },
28166     
28167     asciiMap : {
28168             128: '€',
28169             130: '‚',
28170             131: 'ƒ',
28171             132: '„',
28172             133: '…',
28173             134: '†',
28174             135: '‡',
28175             136: 'ˆ',
28176             137: '‰',
28177             138: 'Š',
28178             139: '‹',
28179             140: 'Œ',
28180             142: 'Ž',
28181             145: '‘',
28182             146: '’',
28183             147: '“',
28184             148: '”',
28185             149: '•',
28186             150: '–',
28187             151: '—',
28188             152: '˜',
28189             153: '™',
28190             154: 'š',
28191             155: '›',
28192             156: 'œ',
28193             158: 'ž',
28194             159: 'Ÿ'
28195     },
28196     // Raw entities
28197     baseEntities : {
28198         '"': '&quot;',
28199         // Needs to be escaped since the YUI compressor would otherwise break the code
28200         '\'': '&#39;',
28201         '<': '&lt;',
28202         '>': '&gt;',
28203         '&': '&amp;',
28204         '`': '&#96;'
28205     },
28206     // Reverse lookup table for raw entities
28207     reverseEntities : {
28208         '&lt;': '<',
28209         '&gt;': '>',
28210         '&amp;': '&',
28211         '&quot;': '"',
28212         '&apos;': '\''
28213     },
28214     
28215     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28216     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28217     rawCharsRegExp : /[<>&\"\']/g,
28218     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28219     namedEntities  : false,
28220     namedEntitiesData : [ 
28221         '50',
28222         'nbsp',
28223         '51',
28224         'iexcl',
28225         '52',
28226         'cent',
28227         '53',
28228         'pound',
28229         '54',
28230         'curren',
28231         '55',
28232         'yen',
28233         '56',
28234         'brvbar',
28235         '57',
28236         'sect',
28237         '58',
28238         'uml',
28239         '59',
28240         'copy',
28241         '5a',
28242         'ordf',
28243         '5b',
28244         'laquo',
28245         '5c',
28246         'not',
28247         '5d',
28248         'shy',
28249         '5e',
28250         'reg',
28251         '5f',
28252         'macr',
28253         '5g',
28254         'deg',
28255         '5h',
28256         'plusmn',
28257         '5i',
28258         'sup2',
28259         '5j',
28260         'sup3',
28261         '5k',
28262         'acute',
28263         '5l',
28264         'micro',
28265         '5m',
28266         'para',
28267         '5n',
28268         'middot',
28269         '5o',
28270         'cedil',
28271         '5p',
28272         'sup1',
28273         '5q',
28274         'ordm',
28275         '5r',
28276         'raquo',
28277         '5s',
28278         'frac14',
28279         '5t',
28280         'frac12',
28281         '5u',
28282         'frac34',
28283         '5v',
28284         'iquest',
28285         '60',
28286         'Agrave',
28287         '61',
28288         'Aacute',
28289         '62',
28290         'Acirc',
28291         '63',
28292         'Atilde',
28293         '64',
28294         'Auml',
28295         '65',
28296         'Aring',
28297         '66',
28298         'AElig',
28299         '67',
28300         'Ccedil',
28301         '68',
28302         'Egrave',
28303         '69',
28304         'Eacute',
28305         '6a',
28306         'Ecirc',
28307         '6b',
28308         'Euml',
28309         '6c',
28310         'Igrave',
28311         '6d',
28312         'Iacute',
28313         '6e',
28314         'Icirc',
28315         '6f',
28316         'Iuml',
28317         '6g',
28318         'ETH',
28319         '6h',
28320         'Ntilde',
28321         '6i',
28322         'Ograve',
28323         '6j',
28324         'Oacute',
28325         '6k',
28326         'Ocirc',
28327         '6l',
28328         'Otilde',
28329         '6m',
28330         'Ouml',
28331         '6n',
28332         'times',
28333         '6o',
28334         'Oslash',
28335         '6p',
28336         'Ugrave',
28337         '6q',
28338         'Uacute',
28339         '6r',
28340         'Ucirc',
28341         '6s',
28342         'Uuml',
28343         '6t',
28344         'Yacute',
28345         '6u',
28346         'THORN',
28347         '6v',
28348         'szlig',
28349         '70',
28350         'agrave',
28351         '71',
28352         'aacute',
28353         '72',
28354         'acirc',
28355         '73',
28356         'atilde',
28357         '74',
28358         'auml',
28359         '75',
28360         'aring',
28361         '76',
28362         'aelig',
28363         '77',
28364         'ccedil',
28365         '78',
28366         'egrave',
28367         '79',
28368         'eacute',
28369         '7a',
28370         'ecirc',
28371         '7b',
28372         'euml',
28373         '7c',
28374         'igrave',
28375         '7d',
28376         'iacute',
28377         '7e',
28378         'icirc',
28379         '7f',
28380         'iuml',
28381         '7g',
28382         'eth',
28383         '7h',
28384         'ntilde',
28385         '7i',
28386         'ograve',
28387         '7j',
28388         'oacute',
28389         '7k',
28390         'ocirc',
28391         '7l',
28392         'otilde',
28393         '7m',
28394         'ouml',
28395         '7n',
28396         'divide',
28397         '7o',
28398         'oslash',
28399         '7p',
28400         'ugrave',
28401         '7q',
28402         'uacute',
28403         '7r',
28404         'ucirc',
28405         '7s',
28406         'uuml',
28407         '7t',
28408         'yacute',
28409         '7u',
28410         'thorn',
28411         '7v',
28412         'yuml',
28413         'ci',
28414         'fnof',
28415         'sh',
28416         'Alpha',
28417         'si',
28418         'Beta',
28419         'sj',
28420         'Gamma',
28421         'sk',
28422         'Delta',
28423         'sl',
28424         'Epsilon',
28425         'sm',
28426         'Zeta',
28427         'sn',
28428         'Eta',
28429         'so',
28430         'Theta',
28431         'sp',
28432         'Iota',
28433         'sq',
28434         'Kappa',
28435         'sr',
28436         'Lambda',
28437         'ss',
28438         'Mu',
28439         'st',
28440         'Nu',
28441         'su',
28442         'Xi',
28443         'sv',
28444         'Omicron',
28445         't0',
28446         'Pi',
28447         't1',
28448         'Rho',
28449         't3',
28450         'Sigma',
28451         't4',
28452         'Tau',
28453         't5',
28454         'Upsilon',
28455         't6',
28456         'Phi',
28457         't7',
28458         'Chi',
28459         't8',
28460         'Psi',
28461         't9',
28462         'Omega',
28463         'th',
28464         'alpha',
28465         'ti',
28466         'beta',
28467         'tj',
28468         'gamma',
28469         'tk',
28470         'delta',
28471         'tl',
28472         'epsilon',
28473         'tm',
28474         'zeta',
28475         'tn',
28476         'eta',
28477         'to',
28478         'theta',
28479         'tp',
28480         'iota',
28481         'tq',
28482         'kappa',
28483         'tr',
28484         'lambda',
28485         'ts',
28486         'mu',
28487         'tt',
28488         'nu',
28489         'tu',
28490         'xi',
28491         'tv',
28492         'omicron',
28493         'u0',
28494         'pi',
28495         'u1',
28496         'rho',
28497         'u2',
28498         'sigmaf',
28499         'u3',
28500         'sigma',
28501         'u4',
28502         'tau',
28503         'u5',
28504         'upsilon',
28505         'u6',
28506         'phi',
28507         'u7',
28508         'chi',
28509         'u8',
28510         'psi',
28511         'u9',
28512         'omega',
28513         'uh',
28514         'thetasym',
28515         'ui',
28516         'upsih',
28517         'um',
28518         'piv',
28519         '812',
28520         'bull',
28521         '816',
28522         'hellip',
28523         '81i',
28524         'prime',
28525         '81j',
28526         'Prime',
28527         '81u',
28528         'oline',
28529         '824',
28530         'frasl',
28531         '88o',
28532         'weierp',
28533         '88h',
28534         'image',
28535         '88s',
28536         'real',
28537         '892',
28538         'trade',
28539         '89l',
28540         'alefsym',
28541         '8cg',
28542         'larr',
28543         '8ch',
28544         'uarr',
28545         '8ci',
28546         'rarr',
28547         '8cj',
28548         'darr',
28549         '8ck',
28550         'harr',
28551         '8dl',
28552         'crarr',
28553         '8eg',
28554         'lArr',
28555         '8eh',
28556         'uArr',
28557         '8ei',
28558         'rArr',
28559         '8ej',
28560         'dArr',
28561         '8ek',
28562         'hArr',
28563         '8g0',
28564         'forall',
28565         '8g2',
28566         'part',
28567         '8g3',
28568         'exist',
28569         '8g5',
28570         'empty',
28571         '8g7',
28572         'nabla',
28573         '8g8',
28574         'isin',
28575         '8g9',
28576         'notin',
28577         '8gb',
28578         'ni',
28579         '8gf',
28580         'prod',
28581         '8gh',
28582         'sum',
28583         '8gi',
28584         'minus',
28585         '8gn',
28586         'lowast',
28587         '8gq',
28588         'radic',
28589         '8gt',
28590         'prop',
28591         '8gu',
28592         'infin',
28593         '8h0',
28594         'ang',
28595         '8h7',
28596         'and',
28597         '8h8',
28598         'or',
28599         '8h9',
28600         'cap',
28601         '8ha',
28602         'cup',
28603         '8hb',
28604         'int',
28605         '8hk',
28606         'there4',
28607         '8hs',
28608         'sim',
28609         '8i5',
28610         'cong',
28611         '8i8',
28612         'asymp',
28613         '8j0',
28614         'ne',
28615         '8j1',
28616         'equiv',
28617         '8j4',
28618         'le',
28619         '8j5',
28620         'ge',
28621         '8k2',
28622         'sub',
28623         '8k3',
28624         'sup',
28625         '8k4',
28626         'nsub',
28627         '8k6',
28628         'sube',
28629         '8k7',
28630         'supe',
28631         '8kl',
28632         'oplus',
28633         '8kn',
28634         'otimes',
28635         '8l5',
28636         'perp',
28637         '8m5',
28638         'sdot',
28639         '8o8',
28640         'lceil',
28641         '8o9',
28642         'rceil',
28643         '8oa',
28644         'lfloor',
28645         '8ob',
28646         'rfloor',
28647         '8p9',
28648         'lang',
28649         '8pa',
28650         'rang',
28651         '9ea',
28652         'loz',
28653         '9j0',
28654         'spades',
28655         '9j3',
28656         'clubs',
28657         '9j5',
28658         'hearts',
28659         '9j6',
28660         'diams',
28661         'ai',
28662         'OElig',
28663         'aj',
28664         'oelig',
28665         'b0',
28666         'Scaron',
28667         'b1',
28668         'scaron',
28669         'bo',
28670         'Yuml',
28671         'm6',
28672         'circ',
28673         'ms',
28674         'tilde',
28675         '802',
28676         'ensp',
28677         '803',
28678         'emsp',
28679         '809',
28680         'thinsp',
28681         '80c',
28682         'zwnj',
28683         '80d',
28684         'zwj',
28685         '80e',
28686         'lrm',
28687         '80f',
28688         'rlm',
28689         '80j',
28690         'ndash',
28691         '80k',
28692         'mdash',
28693         '80o',
28694         'lsquo',
28695         '80p',
28696         'rsquo',
28697         '80q',
28698         'sbquo',
28699         '80s',
28700         'ldquo',
28701         '80t',
28702         'rdquo',
28703         '80u',
28704         'bdquo',
28705         '810',
28706         'dagger',
28707         '811',
28708         'Dagger',
28709         '81g',
28710         'permil',
28711         '81p',
28712         'lsaquo',
28713         '81q',
28714         'rsaquo',
28715         '85c',
28716         'euro'
28717     ],
28718
28719          
28720     /**
28721      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28722      *
28723      * @method encodeRaw
28724      * @param {String} text Text to encode.
28725      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28726      * @return {String} Entity encoded text.
28727      */
28728     encodeRaw: function(text, attr)
28729     {
28730         var t = this;
28731         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28732             return t.baseEntities[chr] || chr;
28733         });
28734     },
28735     /**
28736      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28737      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28738      * and is exposed as the DOMUtils.encode function.
28739      *
28740      * @method encodeAllRaw
28741      * @param {String} text Text to encode.
28742      * @return {String} Entity encoded text.
28743      */
28744     encodeAllRaw: function(text) {
28745         var t = this;
28746         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28747             return t.baseEntities[chr] || chr;
28748         });
28749     },
28750     /**
28751      * Encodes the specified string using numeric entities. The core entities will be
28752      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28753      *
28754      * @method encodeNumeric
28755      * @param {String} text Text to encode.
28756      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28757      * @return {String} Entity encoded text.
28758      */
28759     encodeNumeric: function(text, attr) {
28760         var t = this;
28761         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28762             // Multi byte sequence convert it to a single entity
28763             if (chr.length > 1) {
28764                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28765             }
28766             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28767         });
28768     },
28769     /**
28770      * Encodes the specified string using named entities. The core entities will be encoded
28771      * as named ones but all non lower ascii characters will be encoded into named entities.
28772      *
28773      * @method encodeNamed
28774      * @param {String} text Text to encode.
28775      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28776      * @param {Object} entities Optional parameter with entities to use.
28777      * @return {String} Entity encoded text.
28778      */
28779     encodeNamed: function(text, attr, entities) {
28780         var t = this;
28781         entities = entities || this.namedEntities;
28782         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28783             return t.baseEntities[chr] || entities[chr] || chr;
28784         });
28785     },
28786     /**
28787      * Returns an encode function based on the name(s) and it's optional entities.
28788      *
28789      * @method getEncodeFunc
28790      * @param {String} name Comma separated list of encoders for example named,numeric.
28791      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28792      * @return {function} Encode function to be used.
28793      */
28794     getEncodeFunc: function(name, entities) {
28795         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28796         var t = this;
28797         function encodeNamedAndNumeric(text, attr) {
28798             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28799                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28800             });
28801         }
28802
28803         function encodeCustomNamed(text, attr) {
28804             return t.encodeNamed(text, attr, entities);
28805         }
28806         // Replace + with , to be compatible with previous TinyMCE versions
28807         name = this.makeMap(name.replace(/\+/g, ','));
28808         // Named and numeric encoder
28809         if (name.named && name.numeric) {
28810             return this.encodeNamedAndNumeric;
28811         }
28812         // Named encoder
28813         if (name.named) {
28814             // Custom names
28815             if (entities) {
28816                 return encodeCustomNamed;
28817             }
28818             return this.encodeNamed;
28819         }
28820         // Numeric
28821         if (name.numeric) {
28822             return this.encodeNumeric;
28823         }
28824         // Raw encoder
28825         return this.encodeRaw;
28826     },
28827     /**
28828      * Decodes the specified string, this will replace entities with raw UTF characters.
28829      *
28830      * @method decode
28831      * @param {String} text Text to entity decode.
28832      * @return {String} Entity decoded string.
28833      */
28834     decode: function(text)
28835     {
28836         var  t = this;
28837         return text.replace(this.entityRegExp, function(all, numeric) {
28838             if (numeric) {
28839                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28840                 // Support upper UTF
28841                 if (numeric > 65535) {
28842                     numeric -= 65536;
28843                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28844                 }
28845                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28846             }
28847             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28848         });
28849     },
28850     nativeDecode : function (text) {
28851         return text;
28852     },
28853     makeMap : function (items, delim, map) {
28854                 var i;
28855                 items = items || [];
28856                 delim = delim || ',';
28857                 if (typeof items == "string") {
28858                         items = items.split(delim);
28859                 }
28860                 map = map || {};
28861                 i = items.length;
28862                 while (i--) {
28863                         map[items[i]] = {};
28864                 }
28865                 return map;
28866         }
28867 };
28868     
28869     
28870     
28871 Roo.htmleditor.TidyEntities.init();
28872 /**
28873  * @class Roo.htmleditor.KeyEnter
28874  * Handle Enter press..
28875  * @cfg {Roo.HtmlEditorCore} core the editor.
28876  * @constructor
28877  * Create a new Filter.
28878  * @param {Object} config Configuration options
28879  */
28880
28881
28882
28883
28884
28885 Roo.htmleditor.KeyEnter = function(cfg) {
28886     Roo.apply(this, cfg);
28887     // this does not actually call walk as it's really just a abstract class
28888  
28889     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28890 }
28891
28892 //Roo.htmleditor.KeyEnter.i = 0;
28893
28894
28895 Roo.htmleditor.KeyEnter.prototype = {
28896     
28897     core : false,
28898     
28899     keypress : function(e)
28900     {
28901         if (e.charCode != 13 && e.charCode != 10) {
28902             Roo.log([e.charCode,e]);
28903             return true;
28904         }
28905         e.preventDefault();
28906         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28907         var doc = this.core.doc;
28908           //add a new line
28909        
28910     
28911         var sel = this.core.getSelection();
28912         var range = sel.getRangeAt(0);
28913         var n = range.commonAncestorContainer;
28914         var pc = range.closest([ 'ol', 'ul']);
28915         var pli = range.closest('li');
28916         if (!pc || e.ctrlKey) {
28917             // on it list, or ctrl pressed.
28918             if (!e.ctrlKey) {
28919                 sel.insertNode('br', 'after'); 
28920             } else {
28921                 // only do this if we have ctrl key..
28922                 var br = doc.createElement('br');
28923                 br.className = 'clear';
28924                 br.setAttribute('style', 'clear: both');
28925                 sel.insertNode(br, 'after'); 
28926             }
28927             
28928          
28929             this.core.undoManager.addEvent();
28930             this.core.fireEditorEvent(e);
28931             return false;
28932         }
28933         
28934         // deal with <li> insetion
28935         if (pli.innerText.trim() == '' &&
28936             pli.previousSibling &&
28937             pli.previousSibling.nodeName == 'LI' &&
28938             pli.previousSibling.innerText.trim() ==  '') {
28939             pli.parentNode.removeChild(pli.previousSibling);
28940             sel.cursorAfter(pc);
28941             this.core.undoManager.addEvent();
28942             this.core.fireEditorEvent(e);
28943             return false;
28944         }
28945     
28946         var li = doc.createElement('LI');
28947         li.innerHTML = '&nbsp;';
28948         if (!pli || !pli.firstSibling) {
28949             pc.appendChild(li);
28950         } else {
28951             pli.parentNode.insertBefore(li, pli.firstSibling);
28952         }
28953         sel.cursorText (li.firstChild);
28954       
28955         this.core.undoManager.addEvent();
28956         this.core.fireEditorEvent(e);
28957
28958         return false;
28959         
28960     
28961         
28962         
28963          
28964     }
28965 };
28966      
28967 /**
28968  * @class Roo.htmleditor.Block
28969  * Base class for html editor blocks - do not use it directly .. extend it..
28970  * @cfg {DomElement} node The node to apply stuff to.
28971  * @cfg {String} friendly_name the name that appears in the context bar about this block
28972  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28973  
28974  * @constructor
28975  * Create a new Filter.
28976  * @param {Object} config Configuration options
28977  */
28978
28979 Roo.htmleditor.Block  = function(cfg)
28980 {
28981     // do nothing .. should not be called really.
28982 }
28983 /**
28984  * factory method to get the block from an element (using cache if necessary)
28985  * @static
28986  * @param {HtmlElement} the dom element
28987  */
28988 Roo.htmleditor.Block.factory = function(node)
28989 {
28990     var cc = Roo.htmleditor.Block.cache;
28991     var id = Roo.get(node).id;
28992     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28993         Roo.htmleditor.Block.cache[id].readElement(node);
28994         return Roo.htmleditor.Block.cache[id];
28995     }
28996     var db  = node.getAttribute('data-block');
28997     if (!db) {
28998         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28999     }
29000     var cls = Roo.htmleditor['Block' + db];
29001     if (typeof(cls) == 'undefined') {
29002         //Roo.log(node.getAttribute('data-block'));
29003         Roo.log("OOps missing block : " + 'Block' + db);
29004         return false;
29005     }
29006     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29007     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29008 };
29009
29010 /**
29011  * initalize all Elements from content that are 'blockable'
29012  * @static
29013  * @param the body element
29014  */
29015 Roo.htmleditor.Block.initAll = function(body, type)
29016 {
29017     if (typeof(type) == 'undefined') {
29018         var ia = Roo.htmleditor.Block.initAll;
29019         ia(body,'table');
29020         ia(body,'td');
29021         ia(body,'figure');
29022         return;
29023     }
29024     Roo.each(Roo.get(body).query(type), function(e) {
29025         Roo.htmleditor.Block.factory(e);    
29026     },this);
29027 };
29028 // question goes here... do we need to clear out this cache sometimes?
29029 // or show we make it relivant to the htmleditor.
29030 Roo.htmleditor.Block.cache = {};
29031
29032 Roo.htmleditor.Block.prototype = {
29033     
29034     node : false,
29035     
29036      // used by context menu
29037     friendly_name : 'Based Block',
29038     
29039     // text for button to delete this element
29040     deleteTitle : false,
29041     
29042     context : false,
29043     /**
29044      * Update a node with values from this object
29045      * @param {DomElement} node
29046      */
29047     updateElement : function(node)
29048     {
29049         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29050     },
29051      /**
29052      * convert to plain HTML for calling insertAtCursor..
29053      */
29054     toHTML : function()
29055     {
29056         return Roo.DomHelper.markup(this.toObject());
29057     },
29058     /**
29059      * used by readEleemnt to extract data from a node
29060      * may need improving as it's pretty basic
29061      
29062      * @param {DomElement} node
29063      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29064      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29065      * @param {String} style the style property - eg. text-align
29066      */
29067     getVal : function(node, tag, attr, style)
29068     {
29069         var n = node;
29070         if (tag !== true && n.tagName != tag.toUpperCase()) {
29071             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29072             // but kiss for now.
29073             n = node.getElementsByTagName(tag).item(0);
29074         }
29075         if (!n) {
29076             return '';
29077         }
29078         if (attr === false) {
29079             return n;
29080         }
29081         if (attr == 'html') {
29082             return n.innerHTML;
29083         }
29084         if (attr == 'style') {
29085             return n.style[style]; 
29086         }
29087         
29088         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29089             
29090     },
29091     /**
29092      * create a DomHelper friendly object - for use with 
29093      * Roo.DomHelper.markup / overwrite / etc..
29094      * (override this)
29095      */
29096     toObject : function()
29097     {
29098         return {};
29099     },
29100       /**
29101      * Read a node that has a 'data-block' property - and extract the values from it.
29102      * @param {DomElement} node - the node
29103      */
29104     readElement : function(node)
29105     {
29106         
29107     } 
29108     
29109     
29110 };
29111
29112  
29113
29114 /**
29115  * @class Roo.htmleditor.BlockFigure
29116  * Block that has an image and a figcaption
29117  * @cfg {String} image_src the url for the image
29118  * @cfg {String} align (left|right) alignment for the block default left
29119  * @cfg {String} caption the text to appear below  (and in the alt tag)
29120  * @cfg {String} caption_display (block|none) display or not the caption
29121  * @cfg {String|number} image_width the width of the image number or %?
29122  * @cfg {String|number} image_height the height of the image number or %?
29123  * 
29124  * @constructor
29125  * Create a new Filter.
29126  * @param {Object} config Configuration options
29127  */
29128
29129 Roo.htmleditor.BlockFigure = function(cfg)
29130 {
29131     if (cfg.node) {
29132         this.readElement(cfg.node);
29133         this.updateElement(cfg.node);
29134     }
29135     Roo.apply(this, cfg);
29136 }
29137 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29138  
29139     
29140     // setable values.
29141     image_src: '',
29142     align: 'center',
29143     caption : '',
29144     caption_display : 'block',
29145     width : '100%',
29146     cls : '',
29147     href: '',
29148     video_url : '',
29149     
29150     // margin: '2%', not used
29151     
29152     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29153
29154     
29155     // used by context menu
29156     friendly_name : 'Image with caption',
29157     deleteTitle : "Delete Image and Caption",
29158     
29159     contextMenu : function(toolbar)
29160     {
29161         
29162         var block = function() {
29163             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29164         };
29165         
29166         
29167         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29168         
29169         var syncValue = toolbar.editorcore.syncValue;
29170         
29171         var fields = {};
29172         
29173         return [
29174              {
29175                 xtype : 'TextItem',
29176                 text : "Source: ",
29177                 xns : rooui.Toolbar  //Boostrap?
29178             },
29179             {
29180                 xtype : 'Button',
29181                 text: 'Change Image URL',
29182                  
29183                 listeners : {
29184                     click: function (btn, state)
29185                     {
29186                         var b = block();
29187                         
29188                         Roo.MessageBox.show({
29189                             title : "Image Source URL",
29190                             msg : "Enter the url for the image",
29191                             buttons: Roo.MessageBox.OKCANCEL,
29192                             fn: function(btn, val){
29193                                 if (btn != 'ok') {
29194                                     return;
29195                                 }
29196                                 b.image_src = val;
29197                                 b.updateElement();
29198                                 syncValue();
29199                                 toolbar.editorcore.onEditorEvent();
29200                             },
29201                             minWidth:250,
29202                             prompt:true,
29203                             //multiline: multiline,
29204                             modal : true,
29205                             value : b.image_src
29206                         });
29207                     }
29208                 },
29209                 xns : rooui.Toolbar
29210             },
29211          
29212             {
29213                 xtype : 'Button',
29214                 text: 'Change Link URL',
29215                  
29216                 listeners : {
29217                     click: function (btn, state)
29218                     {
29219                         var b = block();
29220                         
29221                         Roo.MessageBox.show({
29222                             title : "Link URL",
29223                             msg : "Enter the url for the link - leave blank to have no link",
29224                             buttons: Roo.MessageBox.OKCANCEL,
29225                             fn: function(btn, val){
29226                                 if (btn != 'ok') {
29227                                     return;
29228                                 }
29229                                 b.href = val;
29230                                 b.updateElement();
29231                                 syncValue();
29232                                 toolbar.editorcore.onEditorEvent();
29233                             },
29234                             minWidth:250,
29235                             prompt:true,
29236                             //multiline: multiline,
29237                             modal : true,
29238                             value : b.href
29239                         });
29240                     }
29241                 },
29242                 xns : rooui.Toolbar
29243             },
29244             {
29245                 xtype : 'Button',
29246                 text: 'Show Video URL',
29247                  
29248                 listeners : {
29249                     click: function (btn, state)
29250                     {
29251                         Roo.MessageBox.alert("Video URL",
29252                             block().video_url == '' ? 'This image is not linked ot a video' :
29253                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29254                     }
29255                 },
29256                 xns : rooui.Toolbar
29257             },
29258             
29259             
29260             {
29261                 xtype : 'TextItem',
29262                 text : "Width: ",
29263                 xns : rooui.Toolbar  //Boostrap?
29264             },
29265             {
29266                 xtype : 'ComboBox',
29267                 allowBlank : false,
29268                 displayField : 'val',
29269                 editable : true,
29270                 listWidth : 100,
29271                 triggerAction : 'all',
29272                 typeAhead : true,
29273                 valueField : 'val',
29274                 width : 70,
29275                 name : 'width',
29276                 listeners : {
29277                     select : function (combo, r, index)
29278                     {
29279                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29280                         var b = block();
29281                         b.width = r.get('val');
29282                         b.updateElement();
29283                         syncValue();
29284                         toolbar.editorcore.onEditorEvent();
29285                     }
29286                 },
29287                 xns : rooui.form,
29288                 store : {
29289                     xtype : 'SimpleStore',
29290                     data : [
29291                         ['100%'],
29292                         ['80%'],
29293                         ['50%'],
29294                         ['20%'],
29295                         ['10%']
29296                     ],
29297                     fields : [ 'val'],
29298                     xns : Roo.data
29299                 }
29300             },
29301             {
29302                 xtype : 'TextItem',
29303                 text : "Align: ",
29304                 xns : rooui.Toolbar  //Boostrap?
29305             },
29306             {
29307                 xtype : 'ComboBox',
29308                 allowBlank : false,
29309                 displayField : 'val',
29310                 editable : true,
29311                 listWidth : 100,
29312                 triggerAction : 'all',
29313                 typeAhead : true,
29314                 valueField : 'val',
29315                 width : 70,
29316                 name : 'align',
29317                 listeners : {
29318                     select : function (combo, r, index)
29319                     {
29320                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29321                         var b = block();
29322                         b.align = r.get('val');
29323                         b.updateElement();
29324                         syncValue();
29325                         toolbar.editorcore.onEditorEvent();
29326                     }
29327                 },
29328                 xns : rooui.form,
29329                 store : {
29330                     xtype : 'SimpleStore',
29331                     data : [
29332                         ['left'],
29333                         ['right'],
29334                         ['center']
29335                     ],
29336                     fields : [ 'val'],
29337                     xns : Roo.data
29338                 }
29339             },
29340             
29341               
29342             {
29343                 xtype : 'Button',
29344                 text: 'Hide Caption',
29345                 name : 'caption_display',
29346                 pressed : false,
29347                 enableToggle : true,
29348                 setValue : function(v) {
29349                     // this trigger toggle.
29350                      
29351                     this.setText(v ? "Hide Caption" : "Show Caption");
29352                     this.setPressed(v != 'block');
29353                 },
29354                 listeners : {
29355                     toggle: function (btn, state)
29356                     {
29357                         var b  = block();
29358                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29359                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29360                         b.updateElement();
29361                         syncValue();
29362                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29363                         toolbar.editorcore.onEditorEvent();
29364                     }
29365                 },
29366                 xns : rooui.Toolbar
29367             }
29368         ];
29369         
29370     },
29371     /**
29372      * create a DomHelper friendly object - for use with
29373      * Roo.DomHelper.markup / overwrite / etc..
29374      */
29375     toObject : function()
29376     {
29377         var d = document.createElement('div');
29378         d.innerHTML = this.caption;
29379         
29380         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29381         
29382         var iw = this.align == 'center' ? this.width : '100%';
29383         var img =   {
29384             tag : 'img',
29385             contenteditable : 'false',
29386             src : this.image_src,
29387             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29388             style: {
29389                 width : iw,
29390                 maxWidth : iw + ' !important', // this is not getting rendered?
29391                 margin : m  
29392                 
29393             }
29394         };
29395         /*
29396         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29397                     '<a href="{2}">' + 
29398                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29399                     '</a>' + 
29400                 '</div>',
29401         */
29402                 
29403         if (this.href.length > 0) {
29404             img = {
29405                 tag : 'a',
29406                 href: this.href,
29407                 contenteditable : 'true',
29408                 cn : [
29409                     img
29410                 ]
29411             };
29412         }
29413         
29414         
29415         if (this.video_url.length > 0) {
29416             img = {
29417                 tag : 'div',
29418                 cls : this.cls,
29419                 frameborder : 0,
29420                 allowfullscreen : true,
29421                 width : 420,  // these are for video tricks - that we replace the outer
29422                 height : 315,
29423                 src : this.video_url,
29424                 cn : [
29425                     img
29426                 ]
29427             };
29428         }
29429         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29430         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29431         
29432   
29433         var ret =   {
29434             tag: 'figure',
29435             'data-block' : 'Figure',
29436             'data-width' : this.width,
29437             'data-caption' : this.caption, 
29438             contenteditable : 'false',
29439             
29440             style : {
29441                 display: 'block',
29442                 float :  this.align ,
29443                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29444                 width : this.align == 'center' ? '100%' : this.width,
29445                 margin:  '0px',
29446                 padding: this.align == 'center' ? '0' : '0 10px' ,
29447                 textAlign : this.align   // seems to work for email..
29448                 
29449             },
29450            
29451             
29452             align : this.align,
29453             cn : [
29454                 img,
29455               
29456                 {
29457                     tag: 'figcaption',
29458                     'data-display' : this.caption_display,
29459                     style : {
29460                         textAlign : 'left',
29461                         fontSize : '16px',
29462                         lineHeight : '24px',
29463                         display : this.caption_display,
29464                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29465                         margin: m,
29466                         width: this.align == 'center' ?  this.width : '100%' 
29467                     
29468                          
29469                     },
29470                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29471                     cn : [
29472                         {
29473                             tag: 'div',
29474                             style  : {
29475                                 marginTop : '16px',
29476                                 textAlign : 'left'
29477                             },
29478                             align: 'left',
29479                             cn : [
29480                                 {
29481                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29482                                     tag : 'i',
29483                                     contenteditable : true,
29484                                     html : captionhtml
29485                                 }
29486                                 
29487                             ]
29488                         }
29489                         
29490                     ]
29491                     
29492                 }
29493             ]
29494         };
29495         return ret;
29496          
29497     },
29498     
29499     readElement : function(node)
29500     {
29501         // this should not really come from the link...
29502         this.video_url = this.getVal(node, 'div', 'src');
29503         this.cls = this.getVal(node, 'div', 'class');
29504         this.href = this.getVal(node, 'a', 'href');
29505         
29506         
29507         this.image_src = this.getVal(node, 'img', 'src');
29508          
29509         this.align = this.getVal(node, 'figure', 'align');
29510         
29511         /// not really used - as hidden captions do not store the content here..
29512         var figcaption = this.getVal(node, 'figcaption', false);
29513         if (figcaption !== '') {
29514             this.caption = this.getVal(figcaption, 'i', 'html');
29515         }
29516         
29517
29518         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29519         var dc = this.getVal(node, true, 'data-caption');
29520         if (dc && dc.length) {
29521             this.caption = dc;
29522         }
29523         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29524         this.width = this.getVal(node, true, 'data-width');
29525         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29526         
29527     },
29528     removeNode : function()
29529     {
29530         return this.node;
29531     }
29532     
29533   
29534    
29535      
29536     
29537     
29538     
29539     
29540 })
29541
29542  
29543
29544 /**
29545  * @class Roo.htmleditor.BlockTable
29546  * Block that manages a table
29547  * 
29548  * @constructor
29549  * Create a new Filter.
29550  * @param {Object} config Configuration options
29551  */
29552
29553 Roo.htmleditor.BlockTable = function(cfg)
29554 {
29555     if (cfg.node) {
29556         this.readElement(cfg.node);
29557         this.updateElement(cfg.node);
29558     }
29559     Roo.apply(this, cfg);
29560     if (!cfg.node) {
29561         this.rows = [];
29562         for(var r = 0; r < this.no_row; r++) {
29563             this.rows[r] = [];
29564             for(var c = 0; c < this.no_col; c++) {
29565                 this.rows[r][c] = this.emptyCell();
29566             }
29567         }
29568     }
29569     
29570     
29571 }
29572 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29573  
29574     rows : false,
29575     no_col : 1,
29576     no_row : 1,
29577     
29578     
29579     width: '100%',
29580     
29581     // used by context menu
29582     friendly_name : 'Table',
29583     deleteTitle : 'Delete Table',
29584     // context menu is drawn once..
29585     
29586     contextMenu : function(toolbar)
29587     {
29588         
29589         var block = function() {
29590             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29591         };
29592         
29593         
29594         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29595         
29596         var syncValue = toolbar.editorcore.syncValue;
29597         
29598         var fields = {};
29599         
29600         return [
29601             {
29602                 xtype : 'TextItem',
29603                 text : "Width: ",
29604                 xns : rooui.Toolbar  //Boostrap?
29605             },
29606             {
29607                 xtype : 'ComboBox',
29608                 allowBlank : false,
29609                 displayField : 'val',
29610                 editable : true,
29611                 listWidth : 100,
29612                 triggerAction : 'all',
29613                 typeAhead : true,
29614                 valueField : 'val',
29615                 width : 100,
29616                 name : 'width',
29617                 listeners : {
29618                     select : function (combo, r, index)
29619                     {
29620                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29621                         var b = block();
29622                         b.width = r.get('val');
29623                         b.updateElement();
29624                         syncValue();
29625                         toolbar.editorcore.onEditorEvent();
29626                     }
29627                 },
29628                 xns : rooui.form,
29629                 store : {
29630                     xtype : 'SimpleStore',
29631                     data : [
29632                         ['100%'],
29633                         ['auto']
29634                     ],
29635                     fields : [ 'val'],
29636                     xns : Roo.data
29637                 }
29638             },
29639             // -------- Cols
29640             
29641             {
29642                 xtype : 'TextItem',
29643                 text : "Columns: ",
29644                 xns : rooui.Toolbar  //Boostrap?
29645             },
29646          
29647             {
29648                 xtype : 'Button',
29649                 text: '-',
29650                 listeners : {
29651                     click : function (_self, e)
29652                     {
29653                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29654                         block().removeColumn();
29655                         syncValue();
29656                         toolbar.editorcore.onEditorEvent();
29657                     }
29658                 },
29659                 xns : rooui.Toolbar
29660             },
29661             {
29662                 xtype : 'Button',
29663                 text: '+',
29664                 listeners : {
29665                     click : function (_self, e)
29666                     {
29667                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29668                         block().addColumn();
29669                         syncValue();
29670                         toolbar.editorcore.onEditorEvent();
29671                     }
29672                 },
29673                 xns : rooui.Toolbar
29674             },
29675             // -------- ROWS
29676             {
29677                 xtype : 'TextItem',
29678                 text : "Rows: ",
29679                 xns : rooui.Toolbar  //Boostrap?
29680             },
29681          
29682             {
29683                 xtype : 'Button',
29684                 text: '-',
29685                 listeners : {
29686                     click : function (_self, e)
29687                     {
29688                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29689                         block().removeRow();
29690                         syncValue();
29691                         toolbar.editorcore.onEditorEvent();
29692                     }
29693                 },
29694                 xns : rooui.Toolbar
29695             },
29696             {
29697                 xtype : 'Button',
29698                 text: '+',
29699                 listeners : {
29700                     click : function (_self, e)
29701                     {
29702                         block().addRow();
29703                         syncValue();
29704                         toolbar.editorcore.onEditorEvent();
29705                     }
29706                 },
29707                 xns : rooui.Toolbar
29708             },
29709             // -------- ROWS
29710             {
29711                 xtype : 'Button',
29712                 text: 'Reset Column Widths',
29713                 listeners : {
29714                     
29715                     click : function (_self, e)
29716                     {
29717                         block().resetWidths();
29718                         syncValue();
29719                         toolbar.editorcore.onEditorEvent();
29720                     }
29721                 },
29722                 xns : rooui.Toolbar
29723             } 
29724             
29725             
29726             
29727         ];
29728         
29729     },
29730     
29731     
29732   /**
29733      * create a DomHelper friendly object - for use with
29734      * Roo.DomHelper.markup / overwrite / etc..
29735      * ?? should it be called with option to hide all editing features?
29736      */
29737     toObject : function()
29738     {
29739         
29740         var ret = {
29741             tag : 'table',
29742             contenteditable : 'false', // this stops cell selection from picking the table.
29743             'data-block' : 'Table',
29744             style : {
29745                 width:  this.width,
29746                 border : 'solid 1px #000', // ??? hard coded?
29747                 'border-collapse' : 'collapse' 
29748             },
29749             cn : [
29750                 { tag : 'tbody' , cn : [] }
29751             ]
29752         };
29753         
29754         // do we have a head = not really 
29755         var ncols = 0;
29756         Roo.each(this.rows, function( row ) {
29757             var tr = {
29758                 tag: 'tr',
29759                 style : {
29760                     margin: '6px',
29761                     border : 'solid 1px #000',
29762                     textAlign : 'left' 
29763                 },
29764                 cn : [ ]
29765             };
29766             
29767             ret.cn[0].cn.push(tr);
29768             // does the row have any properties? ?? height?
29769             var nc = 0;
29770             Roo.each(row, function( cell ) {
29771                 
29772                 var td = {
29773                     tag : 'td',
29774                     contenteditable :  'true',
29775                     'data-block' : 'Td',
29776                     html : cell.html,
29777                     style : cell.style
29778                 };
29779                 if (cell.colspan > 1) {
29780                     td.colspan = cell.colspan ;
29781                     nc += cell.colspan;
29782                 } else {
29783                     nc++;
29784                 }
29785                 if (cell.rowspan > 1) {
29786                     td.rowspan = cell.rowspan ;
29787                 }
29788                 
29789                 
29790                 // widths ?
29791                 tr.cn.push(td);
29792                     
29793                 
29794             }, this);
29795             ncols = Math.max(nc, ncols);
29796             
29797             
29798         }, this);
29799         // add the header row..
29800         
29801         ncols++;
29802          
29803         
29804         return ret;
29805          
29806     },
29807     
29808     readElement : function(node)
29809     {
29810         node  = node ? node : this.node ;
29811         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29812         
29813         this.rows = [];
29814         this.no_row = 0;
29815         var trs = Array.from(node.rows);
29816         trs.forEach(function(tr) {
29817             var row =  [];
29818             this.rows.push(row);
29819             
29820             this.no_row++;
29821             var no_column = 0;
29822             Array.from(tr.cells).forEach(function(td) {
29823                 
29824                 var add = {
29825                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29826                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29827                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29828                     html : td.innerHTML
29829                 };
29830                 no_column += add.colspan;
29831                      
29832                 
29833                 row.push(add);
29834                 
29835                 
29836             },this);
29837             this.no_col = Math.max(this.no_col, no_column);
29838             
29839             
29840         },this);
29841         
29842         
29843     },
29844     normalizeRows: function()
29845     {
29846         var ret= [];
29847         var rid = -1;
29848         this.rows.forEach(function(row) {
29849             rid++;
29850             ret[rid] = [];
29851             row = this.normalizeRow(row);
29852             var cid = 0;
29853             row.forEach(function(c) {
29854                 while (typeof(ret[rid][cid]) != 'undefined') {
29855                     cid++;
29856                 }
29857                 if (typeof(ret[rid]) == 'undefined') {
29858                     ret[rid] = [];
29859                 }
29860                 ret[rid][cid] = c;
29861                 c.row = rid;
29862                 c.col = cid;
29863                 if (c.rowspan < 2) {
29864                     return;
29865                 }
29866                 
29867                 for(var i = 1 ;i < c.rowspan; i++) {
29868                     if (typeof(ret[rid+i]) == 'undefined') {
29869                         ret[rid+i] = [];
29870                     }
29871                     ret[rid+i][cid] = c;
29872                 }
29873             });
29874         }, this);
29875         return ret;
29876     
29877     },
29878     
29879     normalizeRow: function(row)
29880     {
29881         var ret= [];
29882         row.forEach(function(c) {
29883             if (c.colspan < 2) {
29884                 ret.push(c);
29885                 return;
29886             }
29887             for(var i =0 ;i < c.colspan; i++) {
29888                 ret.push(c);
29889             }
29890         });
29891         return ret;
29892     
29893     },
29894     
29895     deleteColumn : function(sel)
29896     {
29897         if (!sel || sel.type != 'col') {
29898             return;
29899         }
29900         if (this.no_col < 2) {
29901             return;
29902         }
29903         
29904         this.rows.forEach(function(row) {
29905             var cols = this.normalizeRow(row);
29906             var col = cols[sel.col];
29907             if (col.colspan > 1) {
29908                 col.colspan --;
29909             } else {
29910                 row.remove(col);
29911             }
29912             
29913         }, this);
29914         this.no_col--;
29915         
29916     },
29917     removeColumn : function()
29918     {
29919         this.deleteColumn({
29920             type: 'col',
29921             col : this.no_col-1
29922         });
29923         this.updateElement();
29924     },
29925     
29926      
29927     addColumn : function()
29928     {
29929         
29930         this.rows.forEach(function(row) {
29931             row.push(this.emptyCell());
29932            
29933         }, this);
29934         this.updateElement();
29935     },
29936     
29937     deleteRow : function(sel)
29938     {
29939         if (!sel || sel.type != 'row') {
29940             return;
29941         }
29942         
29943         if (this.no_row < 2) {
29944             return;
29945         }
29946         
29947         var rows = this.normalizeRows();
29948         
29949         
29950         rows[sel.row].forEach(function(col) {
29951             if (col.rowspan > 1) {
29952                 col.rowspan--;
29953             } else {
29954                 col.remove = 1; // flage it as removed.
29955             }
29956             
29957         }, this);
29958         var newrows = [];
29959         this.rows.forEach(function(row) {
29960             newrow = [];
29961             row.forEach(function(c) {
29962                 if (typeof(c.remove) == 'undefined') {
29963                     newrow.push(c);
29964                 }
29965                 
29966             });
29967             if (newrow.length > 0) {
29968                 newrows.push(row);
29969             }
29970         });
29971         this.rows =  newrows;
29972         
29973         
29974         
29975         this.no_row--;
29976         this.updateElement();
29977         
29978     },
29979     removeRow : function()
29980     {
29981         this.deleteRow({
29982             type: 'row',
29983             row : this.no_row-1
29984         });
29985         
29986     },
29987     
29988      
29989     addRow : function()
29990     {
29991         
29992         var row = [];
29993         for (var i = 0; i < this.no_col; i++ ) {
29994             
29995             row.push(this.emptyCell());
29996            
29997         }
29998         this.rows.push(row);
29999         this.updateElement();
30000         
30001     },
30002      
30003     // the default cell object... at present...
30004     emptyCell : function() {
30005         return (new Roo.htmleditor.BlockTd({})).toObject();
30006         
30007      
30008     },
30009     
30010     removeNode : function()
30011     {
30012         return this.node;
30013     },
30014     
30015     
30016     
30017     resetWidths : function()
30018     {
30019         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30020             var nn = Roo.htmleditor.Block.factory(n);
30021             nn.width = '';
30022             nn.updateElement(n);
30023         });
30024     }
30025     
30026     
30027     
30028     
30029 })
30030
30031 /**
30032  *
30033  * editing a TD?
30034  *
30035  * since selections really work on the table cell, then editing really should work from there
30036  *
30037  * The original plan was to support merging etc... - but that may not be needed yet..
30038  *
30039  * So this simple version will support:
30040  *   add/remove cols
30041  *   adjust the width +/-
30042  *   reset the width...
30043  *   
30044  *
30045  */
30046
30047
30048  
30049
30050 /**
30051  * @class Roo.htmleditor.BlockTable
30052  * Block that manages a table
30053  * 
30054  * @constructor
30055  * Create a new Filter.
30056  * @param {Object} config Configuration options
30057  */
30058
30059 Roo.htmleditor.BlockTd = function(cfg)
30060 {
30061     if (cfg.node) {
30062         this.readElement(cfg.node);
30063         this.updateElement(cfg.node);
30064     }
30065     Roo.apply(this, cfg);
30066      
30067     
30068     
30069 }
30070 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30071  
30072     node : false,
30073     
30074     width: '',
30075     textAlign : 'left',
30076     valign : 'top',
30077     
30078     colspan : 1,
30079     rowspan : 1,
30080     
30081     
30082     // used by context menu
30083     friendly_name : 'Table Cell',
30084     deleteTitle : false, // use our customer delete
30085     
30086     // context menu is drawn once..
30087     
30088     contextMenu : function(toolbar)
30089     {
30090         
30091         var cell = function() {
30092             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30093         };
30094         
30095         var table = function() {
30096             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30097         };
30098         
30099         var lr = false;
30100         var saveSel = function()
30101         {
30102             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30103         }
30104         var restoreSel = function()
30105         {
30106             if (lr) {
30107                 (function() {
30108                     toolbar.editorcore.focus();
30109                     var cr = toolbar.editorcore.getSelection();
30110                     cr.removeAllRanges();
30111                     cr.addRange(lr);
30112                     toolbar.editorcore.onEditorEvent();
30113                 }).defer(10, this);
30114                 
30115                 
30116             }
30117         }
30118         
30119         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30120         
30121         var syncValue = toolbar.editorcore.syncValue;
30122         
30123         var fields = {};
30124         
30125         return [
30126             {
30127                 xtype : 'Button',
30128                 text : 'Edit Table',
30129                 listeners : {
30130                     click : function() {
30131                         var t = toolbar.tb.selectedNode.closest('table');
30132                         toolbar.editorcore.selectNode(t);
30133                         toolbar.editorcore.onEditorEvent();                        
30134                     }
30135                 }
30136                 
30137             },
30138               
30139            
30140              
30141             {
30142                 xtype : 'TextItem',
30143                 text : "Column Width: ",
30144                  xns : rooui.Toolbar 
30145                
30146             },
30147             {
30148                 xtype : 'Button',
30149                 text: '-',
30150                 listeners : {
30151                     click : function (_self, e)
30152                     {
30153                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30154                         cell().shrinkColumn();
30155                         syncValue();
30156                          toolbar.editorcore.onEditorEvent();
30157                     }
30158                 },
30159                 xns : rooui.Toolbar
30160             },
30161             {
30162                 xtype : 'Button',
30163                 text: '+',
30164                 listeners : {
30165                     click : function (_self, e)
30166                     {
30167                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30168                         cell().growColumn();
30169                         syncValue();
30170                         toolbar.editorcore.onEditorEvent();
30171                     }
30172                 },
30173                 xns : rooui.Toolbar
30174             },
30175             
30176             {
30177                 xtype : 'TextItem',
30178                 text : "Vertical Align: ",
30179                 xns : rooui.Toolbar  //Boostrap?
30180             },
30181             {
30182                 xtype : 'ComboBox',
30183                 allowBlank : false,
30184                 displayField : 'val',
30185                 editable : true,
30186                 listWidth : 100,
30187                 triggerAction : 'all',
30188                 typeAhead : true,
30189                 valueField : 'val',
30190                 width : 100,
30191                 name : 'valign',
30192                 listeners : {
30193                     select : function (combo, r, index)
30194                     {
30195                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30196                         var b = cell();
30197                         b.valign = r.get('val');
30198                         b.updateElement();
30199                         syncValue();
30200                         toolbar.editorcore.onEditorEvent();
30201                     }
30202                 },
30203                 xns : rooui.form,
30204                 store : {
30205                     xtype : 'SimpleStore',
30206                     data : [
30207                         ['top'],
30208                         ['middle'],
30209                         ['bottom'] // there are afew more... 
30210                     ],
30211                     fields : [ 'val'],
30212                     xns : Roo.data
30213                 }
30214             },
30215             
30216             {
30217                 xtype : 'TextItem',
30218                 text : "Merge Cells: ",
30219                  xns : rooui.Toolbar 
30220                
30221             },
30222             
30223             
30224             {
30225                 xtype : 'Button',
30226                 text: 'Right',
30227                 listeners : {
30228                     click : function (_self, e)
30229                     {
30230                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30231                         cell().mergeRight();
30232                         //block().growColumn();
30233                         syncValue();
30234                         toolbar.editorcore.onEditorEvent();
30235                     }
30236                 },
30237                 xns : rooui.Toolbar
30238             },
30239              
30240             {
30241                 xtype : 'Button',
30242                 text: 'Below',
30243                 listeners : {
30244                     click : function (_self, e)
30245                     {
30246                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30247                         cell().mergeBelow();
30248                         //block().growColumn();
30249                         syncValue();
30250                         toolbar.editorcore.onEditorEvent();
30251                     }
30252                 },
30253                 xns : rooui.Toolbar
30254             },
30255             {
30256                 xtype : 'TextItem',
30257                 text : "| ",
30258                  xns : rooui.Toolbar 
30259                
30260             },
30261             
30262             {
30263                 xtype : 'Button',
30264                 text: 'Split',
30265                 listeners : {
30266                     click : function (_self, e)
30267                     {
30268                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30269                         cell().split();
30270                         syncValue();
30271                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30272                         toolbar.editorcore.onEditorEvent();
30273                                              
30274                     }
30275                 },
30276                 xns : rooui.Toolbar
30277             },
30278             {
30279                 xtype : 'Fill',
30280                 xns : rooui.Toolbar 
30281                
30282             },
30283         
30284           
30285             {
30286                 xtype : 'Button',
30287                 text: 'Delete',
30288                  
30289                 xns : rooui.Toolbar,
30290                 menu : {
30291                     xtype : 'Menu',
30292                     xns : rooui.menu,
30293                     items : [
30294                         {
30295                             xtype : 'Item',
30296                             html: 'Column',
30297                             listeners : {
30298                                 click : function (_self, e)
30299                                 {
30300                                     var t = table();
30301                                     
30302                                     cell().deleteColumn();
30303                                     syncValue();
30304                                     toolbar.editorcore.selectNode(t.node);
30305                                     toolbar.editorcore.onEditorEvent();   
30306                                 }
30307                             },
30308                             xns : rooui.menu
30309                         },
30310                         {
30311                             xtype : 'Item',
30312                             html: 'Row',
30313                             listeners : {
30314                                 click : function (_self, e)
30315                                 {
30316                                     var t = table();
30317                                     cell().deleteRow();
30318                                     syncValue();
30319                                     
30320                                     toolbar.editorcore.selectNode(t.node);
30321                                     toolbar.editorcore.onEditorEvent();   
30322                                                          
30323                                 }
30324                             },
30325                             xns : rooui.menu
30326                         },
30327                        {
30328                             xtype : 'Separator',
30329                             xns : rooui.menu
30330                         },
30331                         {
30332                             xtype : 'Item',
30333                             html: 'Table',
30334                             listeners : {
30335                                 click : function (_self, e)
30336                                 {
30337                                     var t = table();
30338                                     var nn = t.node.nextSibling || t.node.previousSibling;
30339                                     t.node.parentNode.removeChild(t.node);
30340                                     if (nn) { 
30341                                         toolbar.editorcore.selectNode(nn, true);
30342                                     }
30343                                     toolbar.editorcore.onEditorEvent();   
30344                                                          
30345                                 }
30346                             },
30347                             xns : rooui.menu
30348                         }
30349                     ]
30350                 }
30351             }
30352             
30353             // align... << fixme
30354             
30355         ];
30356         
30357     },
30358     
30359     
30360   /**
30361      * create a DomHelper friendly object - for use with
30362      * Roo.DomHelper.markup / overwrite / etc..
30363      * ?? should it be called with option to hide all editing features?
30364      */
30365  /**
30366      * create a DomHelper friendly object - for use with
30367      * Roo.DomHelper.markup / overwrite / etc..
30368      * ?? should it be called with option to hide all editing features?
30369      */
30370     toObject : function()
30371     {
30372         var ret = {
30373             tag : 'td',
30374             contenteditable : 'true', // this stops cell selection from picking the table.
30375             'data-block' : 'Td',
30376             valign : this.valign,
30377             style : {  
30378                 'text-align' :  this.textAlign,
30379                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30380                 'border-collapse' : 'collapse',
30381                 padding : '6px', // 8 for desktop / 4 for mobile
30382                 'vertical-align': this.valign
30383             },
30384             html : this.html
30385         };
30386         if (this.width != '') {
30387             ret.width = this.width;
30388             ret.style.width = this.width;
30389         }
30390         
30391         
30392         if (this.colspan > 1) {
30393             ret.colspan = this.colspan ;
30394         } 
30395         if (this.rowspan > 1) {
30396             ret.rowspan = this.rowspan ;
30397         }
30398         
30399            
30400         
30401         return ret;
30402          
30403     },
30404     
30405     readElement : function(node)
30406     {
30407         node  = node ? node : this.node ;
30408         this.width = node.style.width;
30409         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30410         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30411         this.html = node.innerHTML;
30412         if (node.style.textAlign != '') {
30413             this.textAlign = node.style.textAlign;
30414         }
30415         
30416         
30417     },
30418      
30419     // the default cell object... at present...
30420     emptyCell : function() {
30421         return {
30422             colspan :  1,
30423             rowspan :  1,
30424             textAlign : 'left',
30425             html : "&nbsp;" // is this going to be editable now?
30426         };
30427      
30428     },
30429     
30430     removeNode : function()
30431     {
30432         return this.node.closest('table');
30433          
30434     },
30435     
30436     cellData : false,
30437     
30438     colWidths : false,
30439     
30440     toTableArray  : function()
30441     {
30442         var ret = [];
30443         var tab = this.node.closest('tr').closest('table');
30444         Array.from(tab.rows).forEach(function(r, ri){
30445             ret[ri] = [];
30446         });
30447         var rn = 0;
30448         this.colWidths = [];
30449         var all_auto = true;
30450         Array.from(tab.rows).forEach(function(r, ri){
30451             
30452             var cn = 0;
30453             Array.from(r.cells).forEach(function(ce, ci){
30454                 var c =  {
30455                     cell : ce,
30456                     row : rn,
30457                     col: cn,
30458                     colspan : ce.colSpan,
30459                     rowspan : ce.rowSpan
30460                 };
30461                 if (ce.isEqualNode(this.node)) {
30462                     this.cellData = c;
30463                 }
30464                 // if we have been filled up by a row?
30465                 if (typeof(ret[rn][cn]) != 'undefined') {
30466                     while(typeof(ret[rn][cn]) != 'undefined') {
30467                         cn++;
30468                     }
30469                     c.col = cn;
30470                 }
30471                 
30472                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30473                     this.colWidths[cn] =   ce.style.width;
30474                     if (this.colWidths[cn] != '') {
30475                         all_auto = false;
30476                     }
30477                 }
30478                 
30479                 
30480                 if (c.colspan < 2 && c.rowspan < 2 ) {
30481                     ret[rn][cn] = c;
30482                     cn++;
30483                     return;
30484                 }
30485                 for(var j = 0; j < c.rowspan; j++) {
30486                     if (typeof(ret[rn+j]) == 'undefined') {
30487                         continue; // we have a problem..
30488                     }
30489                     ret[rn+j][cn] = c;
30490                     for(var i = 0; i < c.colspan; i++) {
30491                         ret[rn+j][cn+i] = c;
30492                     }
30493                 }
30494                 
30495                 cn += c.colspan;
30496             }, this);
30497             rn++;
30498         }, this);
30499         
30500         // initalize widths.?
30501         // either all widths or no widths..
30502         if (all_auto) {
30503             this.colWidths[0] = false; // no widths flag.
30504         }
30505         
30506         
30507         return ret;
30508         
30509     },
30510     
30511     
30512     
30513     
30514     mergeRight: function()
30515     {
30516          
30517         // get the contents of the next cell along..
30518         var tr = this.node.closest('tr');
30519         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30520         if (i >= tr.childNodes.length - 1) {
30521             return; // no cells on right to merge with.
30522         }
30523         var table = this.toTableArray();
30524         
30525         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30526             return; // nothing right?
30527         }
30528         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30529         // right cell - must be same rowspan and on the same row.
30530         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30531             return; // right hand side is not same rowspan.
30532         }
30533         
30534         
30535         
30536         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30537         tr.removeChild(rc.cell);
30538         this.colspan += rc.colspan;
30539         this.node.setAttribute('colspan', this.colspan);
30540
30541         var table = this.toTableArray();
30542         this.normalizeWidths(table);
30543         this.updateWidths(table);
30544     },
30545     
30546     
30547     mergeBelow : function()
30548     {
30549         var table = this.toTableArray();
30550         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30551             return; // no row below
30552         }
30553         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30554             return; // nothing right?
30555         }
30556         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30557         
30558         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30559             return; // right hand side is not same rowspan.
30560         }
30561         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30562         rc.cell.parentNode.removeChild(rc.cell);
30563         this.rowspan += rc.rowspan;
30564         this.node.setAttribute('rowspan', this.rowspan);
30565     },
30566     
30567     split: function()
30568     {
30569         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30570             return;
30571         }
30572         var table = this.toTableArray();
30573         var cd = this.cellData;
30574         this.rowspan = 1;
30575         this.colspan = 1;
30576         
30577         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30578              
30579             
30580             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30581                 if (r == cd.row && c == cd.col) {
30582                     this.node.removeAttribute('rowspan');
30583                     this.node.removeAttribute('colspan');
30584                 }
30585                  
30586                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30587                 ntd.removeAttribute('id'); 
30588                 ntd.style.width  = this.colWidths[c];
30589                 ntd.innerHTML = '';
30590                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30591             }
30592             
30593         }
30594         this.redrawAllCells(table);
30595         
30596     },
30597     
30598     
30599     
30600     redrawAllCells: function(table)
30601     {
30602         
30603          
30604         var tab = this.node.closest('tr').closest('table');
30605         var ctr = tab.rows[0].parentNode;
30606         Array.from(tab.rows).forEach(function(r, ri){
30607             
30608             Array.from(r.cells).forEach(function(ce, ci){
30609                 ce.parentNode.removeChild(ce);
30610             });
30611             r.parentNode.removeChild(r);
30612         });
30613         for(var r = 0 ; r < table.length; r++) {
30614             var re = tab.rows[r];
30615             
30616             var re = tab.ownerDocument.createElement('tr');
30617             ctr.appendChild(re);
30618             for(var c = 0 ; c < table[r].length; c++) {
30619                 if (table[r][c].cell === false) {
30620                     continue;
30621                 }
30622                 
30623                 re.appendChild(table[r][c].cell);
30624                  
30625                 table[r][c].cell = false;
30626             }
30627         }
30628         
30629     },
30630     updateWidths : function(table)
30631     {
30632         for(var r = 0 ; r < table.length; r++) {
30633            
30634             for(var c = 0 ; c < table[r].length; c++) {
30635                 if (table[r][c].cell === false) {
30636                     continue;
30637                 }
30638                 
30639                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30640                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30641                     el.width = Math.floor(this.colWidths[c])  +'%';
30642                     el.updateElement(el.node);
30643                 }
30644                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30645                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30646                     var width = 0;
30647                     for(var i = 0; i < table[r][c].colspan; i ++) {
30648                         width += Math.floor(this.colWidths[c + i]);
30649                     }
30650                     el.width = width  +'%';
30651                     el.updateElement(el.node);
30652                 }
30653                 table[r][c].cell = false; // done
30654             }
30655         }
30656     },
30657     normalizeWidths : function(table)
30658     {
30659         if (this.colWidths[0] === false) {
30660             var nw = 100.0 / this.colWidths.length;
30661             this.colWidths.forEach(function(w,i) {
30662                 this.colWidths[i] = nw;
30663             },this);
30664             return;
30665         }
30666     
30667         var t = 0, missing = [];
30668         
30669         this.colWidths.forEach(function(w,i) {
30670             //if you mix % and
30671             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30672             var add =  this.colWidths[i];
30673             if (add > 0) {
30674                 t+=add;
30675                 return;
30676             }
30677             missing.push(i);
30678             
30679             
30680         },this);
30681         var nc = this.colWidths.length;
30682         if (missing.length) {
30683             var mult = (nc - missing.length) / (1.0 * nc);
30684             var t = mult * t;
30685             var ew = (100 -t) / (1.0 * missing.length);
30686             this.colWidths.forEach(function(w,i) {
30687                 if (w > 0) {
30688                     this.colWidths[i] = w * mult;
30689                     return;
30690                 }
30691                 
30692                 this.colWidths[i] = ew;
30693             }, this);
30694             // have to make up numbers..
30695              
30696         }
30697         // now we should have all the widths..
30698         
30699     
30700     },
30701     
30702     shrinkColumn : function()
30703     {
30704         var table = this.toTableArray();
30705         this.normalizeWidths(table);
30706         var col = this.cellData.col;
30707         var nw = this.colWidths[col] * 0.8;
30708         if (nw < 5) {
30709             return;
30710         }
30711         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30712         this.colWidths.forEach(function(w,i) {
30713             if (i == col) {
30714                  this.colWidths[i] = nw;
30715                 return;
30716             }
30717             this.colWidths[i] += otherAdd
30718         }, this);
30719         this.updateWidths(table);
30720          
30721     },
30722     growColumn : function()
30723     {
30724         var table = this.toTableArray();
30725         this.normalizeWidths(table);
30726         var col = this.cellData.col;
30727         var nw = this.colWidths[col] * 1.2;
30728         if (nw > 90) {
30729             return;
30730         }
30731         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30732         this.colWidths.forEach(function(w,i) {
30733             if (i == col) {
30734                 this.colWidths[i] = nw;
30735                 return;
30736             }
30737             this.colWidths[i] -= otherSub
30738         }, this);
30739         this.updateWidths(table);
30740          
30741     },
30742     deleteRow : function()
30743     {
30744         // delete this rows 'tr'
30745         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30746         // then reduce the rowspan.
30747         var table = this.toTableArray();
30748         // this.cellData.row;
30749         for (var i =0;i< table[this.cellData.row].length ; i++) {
30750             var c = table[this.cellData.row][i];
30751             if (c.row != this.cellData.row) {
30752                 
30753                 c.rowspan--;
30754                 c.cell.setAttribute('rowspan', c.rowspan);
30755                 continue;
30756             }
30757             if (c.rowspan > 1) {
30758                 c.rowspan--;
30759                 c.cell.setAttribute('rowspan', c.rowspan);
30760             }
30761         }
30762         table.splice(this.cellData.row,1);
30763         this.redrawAllCells(table);
30764         
30765     },
30766     deleteColumn : function()
30767     {
30768         var table = this.toTableArray();
30769         
30770         for (var i =0;i< table.length ; i++) {
30771             var c = table[i][this.cellData.col];
30772             if (c.col != this.cellData.col) {
30773                 table[i][this.cellData.col].colspan--;
30774             } else if (c.colspan > 1) {
30775                 c.colspan--;
30776                 c.cell.setAttribute('colspan', c.colspan);
30777             }
30778             table[i].splice(this.cellData.col,1);
30779         }
30780         
30781         this.redrawAllCells(table);
30782     }
30783     
30784     
30785     
30786     
30787 })
30788
30789 //<script type="text/javascript">
30790
30791 /*
30792  * Based  Ext JS Library 1.1.1
30793  * Copyright(c) 2006-2007, Ext JS, LLC.
30794  * LGPL
30795  *
30796  */
30797  
30798 /**
30799  * @class Roo.HtmlEditorCore
30800  * @extends Roo.Component
30801  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30802  *
30803  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30804  */
30805
30806 Roo.HtmlEditorCore = function(config){
30807     
30808     
30809     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30810     
30811     
30812     this.addEvents({
30813         /**
30814          * @event initialize
30815          * Fires when the editor is fully initialized (including the iframe)
30816          * @param {Roo.HtmlEditorCore} this
30817          */
30818         initialize: true,
30819         /**
30820          * @event activate
30821          * Fires when the editor is first receives the focus. Any insertion must wait
30822          * until after this event.
30823          * @param {Roo.HtmlEditorCore} this
30824          */
30825         activate: true,
30826          /**
30827          * @event beforesync
30828          * Fires before the textarea is updated with content from the editor iframe. Return false
30829          * to cancel the sync.
30830          * @param {Roo.HtmlEditorCore} this
30831          * @param {String} html
30832          */
30833         beforesync: true,
30834          /**
30835          * @event beforepush
30836          * Fires before the iframe editor is updated with content from the textarea. Return false
30837          * to cancel the push.
30838          * @param {Roo.HtmlEditorCore} this
30839          * @param {String} html
30840          */
30841         beforepush: true,
30842          /**
30843          * @event sync
30844          * Fires when the textarea is updated with content from the editor iframe.
30845          * @param {Roo.HtmlEditorCore} this
30846          * @param {String} html
30847          */
30848         sync: true,
30849          /**
30850          * @event push
30851          * Fires when the iframe editor is updated with content from the textarea.
30852          * @param {Roo.HtmlEditorCore} this
30853          * @param {String} html
30854          */
30855         push: true,
30856         
30857         /**
30858          * @event editorevent
30859          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30860          * @param {Roo.HtmlEditorCore} this
30861          */
30862         editorevent: true 
30863         
30864         
30865     });
30866     
30867     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30868     
30869     // defaults : white / black...
30870     this.applyBlacklists();
30871     
30872     
30873     
30874 };
30875
30876
30877 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30878
30879
30880      /**
30881      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30882      */
30883     
30884     owner : false,
30885     
30886      /**
30887      * @cfg {String} css styling for resizing. (used on bootstrap only)
30888      */
30889     resize : false,
30890      /**
30891      * @cfg {Number} height (in pixels)
30892      */   
30893     height: 300,
30894    /**
30895      * @cfg {Number} width (in pixels)
30896      */   
30897     width: 500,
30898      /**
30899      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30900      *         if you are doing an email editor, this probably needs disabling, it's designed
30901      */
30902     autoClean: true,
30903     
30904     /**
30905      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30906      */
30907     enableBlocks : true,
30908     /**
30909      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30910      * 
30911      */
30912     stylesheets: false,
30913      /**
30914      * @cfg {String} language default en - language of text (usefull for rtl languages)
30915      * 
30916      */
30917     language: 'en',
30918     
30919     /**
30920      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30921      *          - by default they are stripped - if you are editing email you may need this.
30922      */
30923     allowComments: false,
30924     // id of frame..
30925     frameId: false,
30926     
30927     // private properties
30928     validationEvent : false,
30929     deferHeight: true,
30930     initialized : false,
30931     activated : false,
30932     sourceEditMode : false,
30933     onFocus : Roo.emptyFn,
30934     iframePad:3,
30935     hideMode:'offsets',
30936     
30937     clearUp: true,
30938     
30939     // blacklist + whitelisted elements..
30940     black: false,
30941     white: false,
30942      
30943     bodyCls : '',
30944
30945     
30946     undoManager : false,
30947     /**
30948      * Protected method that will not generally be called directly. It
30949      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30950      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30951      */
30952     getDocMarkup : function(){
30953         // body styles..
30954         var st = '';
30955         
30956         // inherit styels from page...?? 
30957         if (this.stylesheets === false) {
30958             
30959             Roo.get(document.head).select('style').each(function(node) {
30960                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30961             });
30962             
30963             Roo.get(document.head).select('link').each(function(node) { 
30964                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30965             });
30966             
30967         } else if (!this.stylesheets.length) {
30968                 // simple..
30969                 st = '<style type="text/css">' +
30970                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30971                    '</style>';
30972         } else {
30973             for (var i in this.stylesheets) {
30974                 if (typeof(this.stylesheets[i]) != 'string') {
30975                     continue;
30976                 }
30977                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30978             }
30979             
30980         }
30981         
30982         st +=  '<style type="text/css">' +
30983             'IMG { cursor: pointer } ' +
30984         '</style>';
30985         
30986         st += '<meta name="google" content="notranslate">';
30987         
30988         var cls = 'notranslate roo-htmleditor-body';
30989         
30990         if(this.bodyCls.length){
30991             cls += ' ' + this.bodyCls;
30992         }
30993         
30994         return '<html  class="notranslate" translate="no"><head>' + st  +
30995             //<style type="text/css">' +
30996             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30997             //'</style>' +
30998             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30999     },
31000
31001     // private
31002     onRender : function(ct, position)
31003     {
31004         var _t = this;
31005         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31006         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31007         
31008         
31009         this.el.dom.style.border = '0 none';
31010         this.el.dom.setAttribute('tabIndex', -1);
31011         this.el.addClass('x-hidden hide');
31012         
31013         
31014         
31015         if(Roo.isIE){ // fix IE 1px bogus margin
31016             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31017         }
31018        
31019         
31020         this.frameId = Roo.id();
31021         
31022         var ifcfg = {
31023             tag: 'iframe',
31024             cls: 'form-control', // bootstrap..
31025             id: this.frameId,
31026             name: this.frameId,
31027             frameBorder : 'no',
31028             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31029         };
31030         if (this.resize) {
31031             ifcfg.style = { resize : this.resize };
31032         }
31033         
31034         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31035         
31036         
31037         this.iframe = iframe.dom;
31038
31039         this.assignDocWin();
31040         
31041         this.doc.designMode = 'on';
31042        
31043         this.doc.open();
31044         this.doc.write(this.getDocMarkup());
31045         this.doc.close();
31046
31047         
31048         var task = { // must defer to wait for browser to be ready
31049             run : function(){
31050                 //console.log("run task?" + this.doc.readyState);
31051                 this.assignDocWin();
31052                 if(this.doc.body || this.doc.readyState == 'complete'){
31053                     try {
31054                         this.doc.designMode="on";
31055                         
31056                     } catch (e) {
31057                         return;
31058                     }
31059                     Roo.TaskMgr.stop(task);
31060                     this.initEditor.defer(10, this);
31061                 }
31062             },
31063             interval : 10,
31064             duration: 10000,
31065             scope: this
31066         };
31067         Roo.TaskMgr.start(task);
31068
31069     },
31070
31071     // private
31072     onResize : function(w, h)
31073     {
31074          Roo.log('resize: ' +w + ',' + h );
31075         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31076         if(!this.iframe){
31077             return;
31078         }
31079         if(typeof w == 'number'){
31080             
31081             this.iframe.style.width = w + 'px';
31082         }
31083         if(typeof h == 'number'){
31084             
31085             this.iframe.style.height = h + 'px';
31086             if(this.doc){
31087                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31088             }
31089         }
31090         
31091     },
31092
31093     /**
31094      * Toggles the editor between standard and source edit mode.
31095      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31096      */
31097     toggleSourceEdit : function(sourceEditMode){
31098         
31099         this.sourceEditMode = sourceEditMode === true;
31100         
31101         if(this.sourceEditMode){
31102  
31103             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31104             
31105         }else{
31106             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31107             //this.iframe.className = '';
31108             this.deferFocus();
31109         }
31110         //this.setSize(this.owner.wrap.getSize());
31111         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31112     },
31113
31114     
31115   
31116
31117     /**
31118      * Protected method that will not generally be called directly. If you need/want
31119      * custom HTML cleanup, this is the method you should override.
31120      * @param {String} html The HTML to be cleaned
31121      * return {String} The cleaned HTML
31122      */
31123     cleanHtml : function(html)
31124     {
31125         html = String(html);
31126         if(html.length > 5){
31127             if(Roo.isSafari){ // strip safari nonsense
31128                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31129             }
31130         }
31131         if(html == '&nbsp;'){
31132             html = '';
31133         }
31134         return html;
31135     },
31136
31137     /**
31138      * HTML Editor -> Textarea
31139      * Protected method that will not generally be called directly. Syncs the contents
31140      * of the editor iframe with the textarea.
31141      */
31142     syncValue : function()
31143     {
31144         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31145         if(this.initialized){
31146             
31147             if (this.undoManager) {
31148                 this.undoManager.addEvent();
31149             }
31150
31151             
31152             var bd = (this.doc.body || this.doc.documentElement);
31153            
31154             
31155             var sel = this.win.getSelection();
31156             
31157             var div = document.createElement('div');
31158             div.innerHTML = bd.innerHTML;
31159             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31160             if (gtx.length > 0) {
31161                 var rm = gtx.item(0).parentNode;
31162                 rm.parentNode.removeChild(rm);
31163             }
31164             
31165            
31166             if (this.enableBlocks) {
31167                 new Roo.htmleditor.FilterBlock({ node : div });
31168             }
31169             
31170             var html = div.innerHTML;
31171             
31172             //?? tidy?
31173             if (this.autoClean) {
31174                 
31175                 new Roo.htmleditor.FilterAttributes({
31176                     node : div,
31177                     attrib_white : [
31178                             'href',
31179                             'src',
31180                             'name',
31181                             'align',
31182                             'colspan',
31183                             'rowspan',
31184                             'data-display',
31185                             'data-width',
31186                             'data-caption',
31187                             'start' ,
31188                             'style',
31189                             // youtube embed.
31190                             'class',
31191                             'allowfullscreen',
31192                             'frameborder',
31193                             'width',
31194                             'height',
31195                             'alt'
31196                             ],
31197                     attrib_clean : ['href', 'src' ] 
31198                 });
31199                 
31200                 var tidy = new Roo.htmleditor.TidySerializer({
31201                     inner:  true
31202                 });
31203                 html  = tidy.serialize(div);
31204                 
31205             }
31206             
31207             
31208             if(Roo.isSafari){
31209                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31210                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31211                 if(m && m[1]){
31212                     html = '<div style="'+m[0]+'">' + html + '</div>';
31213                 }
31214             }
31215             html = this.cleanHtml(html);
31216             // fix up the special chars.. normaly like back quotes in word...
31217             // however we do not want to do this with chinese..
31218             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31219                 
31220                 var cc = match.charCodeAt();
31221
31222                 // Get the character value, handling surrogate pairs
31223                 if (match.length == 2) {
31224                     // It's a surrogate pair, calculate the Unicode code point
31225                     var high = match.charCodeAt(0) - 0xD800;
31226                     var low  = match.charCodeAt(1) - 0xDC00;
31227                     cc = (high * 0x400) + low + 0x10000;
31228                 }  else if (
31229                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31230                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31231                     (cc >= 0xf900 && cc < 0xfb00 )
31232                 ) {
31233                         return match;
31234                 }  
31235          
31236                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31237                 return "&#" + cc + ";";
31238                 
31239                 
31240             });
31241             
31242             
31243              
31244             if(this.owner.fireEvent('beforesync', this, html) !== false){
31245                 this.el.dom.value = html;
31246                 this.owner.fireEvent('sync', this, html);
31247             }
31248         }
31249     },
31250
31251     /**
31252      * TEXTAREA -> EDITABLE
31253      * Protected method that will not generally be called directly. Pushes the value of the textarea
31254      * into the iframe editor.
31255      */
31256     pushValue : function()
31257     {
31258         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31259         if(this.initialized){
31260             var v = this.el.dom.value.trim();
31261             
31262             
31263             if(this.owner.fireEvent('beforepush', this, v) !== false){
31264                 var d = (this.doc.body || this.doc.documentElement);
31265                 d.innerHTML = v;
31266                  
31267                 this.el.dom.value = d.innerHTML;
31268                 this.owner.fireEvent('push', this, v);
31269             }
31270             if (this.autoClean) {
31271                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31272                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31273             }
31274             if (this.enableBlocks) {
31275                 Roo.htmleditor.Block.initAll(this.doc.body);
31276             }
31277             
31278             this.updateLanguage();
31279             
31280             var lc = this.doc.body.lastChild;
31281             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31282                 // add an extra line at the end.
31283                 this.doc.body.appendChild(this.doc.createElement('br'));
31284             }
31285             
31286             
31287         }
31288     },
31289
31290     // private
31291     deferFocus : function(){
31292         this.focus.defer(10, this);
31293     },
31294
31295     // doc'ed in Field
31296     focus : function(){
31297         if(this.win && !this.sourceEditMode){
31298             this.win.focus();
31299         }else{
31300             this.el.focus();
31301         }
31302     },
31303     
31304     assignDocWin: function()
31305     {
31306         var iframe = this.iframe;
31307         
31308          if(Roo.isIE){
31309             this.doc = iframe.contentWindow.document;
31310             this.win = iframe.contentWindow;
31311         } else {
31312 //            if (!Roo.get(this.frameId)) {
31313 //                return;
31314 //            }
31315 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31316 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31317             
31318             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31319                 return;
31320             }
31321             
31322             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31323             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31324         }
31325     },
31326     
31327     // private
31328     initEditor : function(){
31329         //console.log("INIT EDITOR");
31330         this.assignDocWin();
31331         
31332         
31333         
31334         this.doc.designMode="on";
31335         this.doc.open();
31336         this.doc.write(this.getDocMarkup());
31337         this.doc.close();
31338         
31339         var dbody = (this.doc.body || this.doc.documentElement);
31340         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31341         // this copies styles from the containing element into thsi one..
31342         // not sure why we need all of this..
31343         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31344         
31345         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31346         //ss['background-attachment'] = 'fixed'; // w3c
31347         dbody.bgProperties = 'fixed'; // ie
31348         dbody.setAttribute("translate", "no");
31349         
31350         //Roo.DomHelper.applyStyles(dbody, ss);
31351         Roo.EventManager.on(this.doc, {
31352              
31353             'mouseup': this.onEditorEvent,
31354             'dblclick': this.onEditorEvent,
31355             'click': this.onEditorEvent,
31356             'keyup': this.onEditorEvent,
31357             
31358             buffer:100,
31359             scope: this
31360         });
31361         Roo.EventManager.on(this.doc, {
31362             'paste': this.onPasteEvent,
31363             scope : this
31364         });
31365         if(Roo.isGecko){
31366             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31367         }
31368         //??? needed???
31369         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31370             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31371         }
31372         this.initialized = true;
31373
31374         
31375         // initialize special key events - enter
31376         new Roo.htmleditor.KeyEnter({core : this});
31377         
31378          
31379         
31380         this.owner.fireEvent('initialize', this);
31381         this.pushValue();
31382     },
31383     // this is to prevent a href clicks resulting in a redirect?
31384    
31385     onPasteEvent : function(e,v)
31386     {
31387         // I think we better assume paste is going to be a dirty load of rubish from word..
31388         
31389         // even pasting into a 'email version' of this widget will have to clean up that mess.
31390         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31391         
31392         // check what type of paste - if it's an image, then handle it differently.
31393         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31394             // pasting images? 
31395             var urlAPI = (window.createObjectURL && window) || 
31396                 (window.URL && URL.revokeObjectURL && URL) || 
31397                 (window.webkitURL && webkitURL);
31398             
31399             var r = new FileReader();
31400             var t = this;
31401             r.addEventListener('load',function()
31402             {
31403                 
31404                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31405                 // is insert asycn?
31406                 if (t.enableBlocks) {
31407                     
31408                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31409                         if (img.closest('figure')) { // assume!! that it's aready
31410                             return;
31411                         }
31412                         var fig  = new Roo.htmleditor.BlockFigure({
31413                             image_src  : img.src
31414                         });
31415                         fig.updateElement(img); // replace it..
31416                         
31417                     });
31418                 }
31419                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31420                 t.owner.fireEvent('paste', this);
31421             });
31422             r.readAsDataURL(cd.files[0]);
31423             
31424             e.preventDefault();
31425             
31426             return false;
31427         }
31428         if (cd.types.indexOf('text/html') < 0 ) {
31429             return false;
31430         }
31431         var images = [];
31432         var html = cd.getData('text/html'); // clipboard event
31433         if (cd.types.indexOf('text/rtf') > -1) {
31434             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31435             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31436         }
31437         //Roo.log(images);
31438         //Roo.log(imgs);
31439         // fixme..
31440         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31441                        .map(function(g) { return g.toDataURL(); })
31442                        .filter(function(g) { return g != 'about:blank'; });
31443         
31444         //Roo.log(html);
31445         html = this.cleanWordChars(html);
31446         
31447         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31448         
31449         
31450         var sn = this.getParentElement();
31451         // check if d contains a table, and prevent nesting??
31452         //Roo.log(d.getElementsByTagName('table'));
31453         //Roo.log(sn);
31454         //Roo.log(sn.closest('table'));
31455         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31456             e.preventDefault();
31457             this.insertAtCursor("You can not nest tables");
31458             //Roo.log("prevent?"); // fixme - 
31459             return false;
31460         }
31461         
31462         
31463         
31464         if (images.length > 0) {
31465             // replace all v:imagedata - with img.
31466             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31467             Roo.each(ar, function(node) {
31468                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31469                 node.parentNode.removeChild(node);
31470             });
31471             
31472             
31473             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31474                 img.setAttribute('src', images[i]);
31475             });
31476         }
31477         if (this.autoClean) {
31478             new Roo.htmleditor.FilterWord({ node : d });
31479             
31480             new Roo.htmleditor.FilterStyleToTag({ node : d });
31481             new Roo.htmleditor.FilterAttributes({
31482                 node : d,
31483                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31484                 attrib_clean : ['href', 'src' ] 
31485             });
31486             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31487             // should be fonts..
31488             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31489             new Roo.htmleditor.FilterParagraph({ node : d });
31490             new Roo.htmleditor.FilterSpan({ node : d });
31491             new Roo.htmleditor.FilterLongBr({ node : d });
31492             new Roo.htmleditor.FilterComment({ node : d });
31493             
31494             
31495         }
31496         if (this.enableBlocks) {
31497                 
31498             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31499                 if (img.closest('figure')) { // assume!! that it's aready
31500                     return;
31501                 }
31502                 var fig  = new Roo.htmleditor.BlockFigure({
31503                     image_src  : img.src
31504                 });
31505                 fig.updateElement(img); // replace it..
31506                 
31507             });
31508         }
31509         
31510         
31511         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31512         if (this.enableBlocks) {
31513             Roo.htmleditor.Block.initAll(this.doc.body);
31514         }
31515          
31516         
31517         e.preventDefault();
31518         this.owner.fireEvent('paste', this);
31519         return false;
31520         // default behaveiour should be our local cleanup paste? (optional?)
31521         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31522         //this.owner.fireEvent('paste', e, v);
31523     },
31524     // private
31525     onDestroy : function(){
31526         
31527         
31528         
31529         if(this.rendered){
31530             
31531             //for (var i =0; i < this.toolbars.length;i++) {
31532             //    // fixme - ask toolbars for heights?
31533             //    this.toolbars[i].onDestroy();
31534            // }
31535             
31536             //this.wrap.dom.innerHTML = '';
31537             //this.wrap.remove();
31538         }
31539     },
31540
31541     // private
31542     onFirstFocus : function(){
31543         
31544         this.assignDocWin();
31545         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31546         
31547         this.activated = true;
31548          
31549     
31550         if(Roo.isGecko){ // prevent silly gecko errors
31551             this.win.focus();
31552             var s = this.win.getSelection();
31553             if(!s.focusNode || s.focusNode.nodeType != 3){
31554                 var r = s.getRangeAt(0);
31555                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31556                 r.collapse(true);
31557                 this.deferFocus();
31558             }
31559             try{
31560                 this.execCmd('useCSS', true);
31561                 this.execCmd('styleWithCSS', false);
31562             }catch(e){}
31563         }
31564         this.owner.fireEvent('activate', this);
31565     },
31566
31567     // private
31568     adjustFont: function(btn){
31569         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31570         //if(Roo.isSafari){ // safari
31571         //    adjust *= 2;
31572        // }
31573         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31574         if(Roo.isSafari){ // safari
31575             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31576             v =  (v < 10) ? 10 : v;
31577             v =  (v > 48) ? 48 : v;
31578             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31579             
31580         }
31581         
31582         
31583         v = Math.max(1, v+adjust);
31584         
31585         this.execCmd('FontSize', v  );
31586     },
31587
31588     onEditorEvent : function(e)
31589     {
31590          
31591         
31592         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31593             return; // we do not handle this.. (undo manager does..)
31594         }
31595         // clicking a 'block'?
31596         
31597         // in theory this detects if the last element is not a br, then we try and do that.
31598         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31599         if (e &&
31600             e.target.nodeName == 'BODY' &&
31601             e.type == "mouseup" &&
31602             this.doc.body.lastChild
31603            ) {
31604             var lc = this.doc.body.lastChild;
31605             // gtx-trans is google translate plugin adding crap.
31606             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31607                 lc = lc.previousSibling;
31608             }
31609             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31610             // if last element is <BR> - then dont do anything.
31611             
31612                 var ns = this.doc.createElement('br');
31613                 this.doc.body.appendChild(ns);
31614                 range = this.doc.createRange();
31615                 range.setStartAfter(ns);
31616                 range.collapse(true);
31617                 var sel = this.win.getSelection();
31618                 sel.removeAllRanges();
31619                 sel.addRange(range);
31620             }
31621         }
31622         
31623         
31624         
31625         this.fireEditorEvent(e);
31626       //  this.updateToolbar();
31627         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31628     },
31629     
31630     fireEditorEvent: function(e)
31631     {
31632         this.owner.fireEvent('editorevent', this, e);
31633     },
31634
31635     insertTag : function(tg)
31636     {
31637         // could be a bit smarter... -> wrap the current selected tRoo..
31638         if (tg.toLowerCase() == 'span' ||
31639             tg.toLowerCase() == 'code' ||
31640             tg.toLowerCase() == 'sup' ||
31641             tg.toLowerCase() == 'sub' 
31642             ) {
31643             
31644             range = this.createRange(this.getSelection());
31645             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31646             wrappingNode.appendChild(range.extractContents());
31647             range.insertNode(wrappingNode);
31648
31649             return;
31650             
31651             
31652             
31653         }
31654         this.execCmd("formatblock",   tg);
31655         this.undoManager.addEvent(); 
31656     },
31657     
31658     insertText : function(txt)
31659     {
31660         
31661         
31662         var range = this.createRange();
31663         range.deleteContents();
31664                //alert(Sender.getAttribute('label'));
31665                
31666         range.insertNode(this.doc.createTextNode(txt));
31667         this.undoManager.addEvent();
31668     } ,
31669     
31670      
31671
31672     /**
31673      * Executes a Midas editor command on the editor document and performs necessary focus and
31674      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31675      * @param {String} cmd The Midas command
31676      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31677      */
31678     relayCmd : function(cmd, value)
31679     {
31680         
31681         switch (cmd) {
31682             case 'justifyleft':
31683             case 'justifyright':
31684             case 'justifycenter':
31685                 // if we are in a cell, then we will adjust the
31686                 var n = this.getParentElement();
31687                 var td = n.closest('td');
31688                 if (td) {
31689                     var bl = Roo.htmleditor.Block.factory(td);
31690                     bl.textAlign = cmd.replace('justify','');
31691                     bl.updateElement();
31692                     this.owner.fireEvent('editorevent', this);
31693                     return;
31694                 }
31695                 this.execCmd('styleWithCSS', true); // 
31696                 break;
31697             case 'bold':
31698             case 'italic':
31699             case 'underline':                
31700                 // if there is no selection, then we insert, and set the curson inside it..
31701                 this.execCmd('styleWithCSS', false); 
31702                 break;
31703                 
31704         
31705             default:
31706                 break;
31707         }
31708         
31709         
31710         this.win.focus();
31711         this.execCmd(cmd, value);
31712         this.owner.fireEvent('editorevent', this);
31713         //this.updateToolbar();
31714         this.owner.deferFocus();
31715     },
31716
31717     /**
31718      * Executes a Midas editor command directly on the editor document.
31719      * For visual commands, you should use {@link #relayCmd} instead.
31720      * <b>This should only be called after the editor is initialized.</b>
31721      * @param {String} cmd The Midas command
31722      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31723      */
31724     execCmd : function(cmd, value){
31725         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31726         this.syncValue();
31727     },
31728  
31729  
31730    
31731     /**
31732      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31733      * to insert tRoo.
31734      * @param {String} text | dom node.. 
31735      */
31736     insertAtCursor : function(text)
31737     {
31738         
31739         if(!this.activated){
31740             return;
31741         }
31742          
31743         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31744             this.win.focus();
31745             
31746             
31747             // from jquery ui (MIT licenced)
31748             var range, node;
31749             var win = this.win;
31750             
31751             if (win.getSelection && win.getSelection().getRangeAt) {
31752                 
31753                 // delete the existing?
31754                 
31755                 this.createRange(this.getSelection()).deleteContents();
31756                 range = win.getSelection().getRangeAt(0);
31757                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31758                 range.insertNode(node);
31759                 range = range.cloneRange();
31760                 range.collapse(false);
31761                  
31762                 win.getSelection().removeAllRanges();
31763                 win.getSelection().addRange(range);
31764                 
31765                 
31766                 
31767             } else if (win.document.selection && win.document.selection.createRange) {
31768                 // no firefox support
31769                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31770                 win.document.selection.createRange().pasteHTML(txt);
31771             
31772             } else {
31773                 // no firefox support
31774                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31775                 this.execCmd('InsertHTML', txt);
31776             } 
31777             this.syncValue();
31778             
31779             this.deferFocus();
31780         }
31781     },
31782  // private
31783     mozKeyPress : function(e){
31784         if(e.ctrlKey){
31785             var c = e.getCharCode(), cmd;
31786           
31787             if(c > 0){
31788                 c = String.fromCharCode(c).toLowerCase();
31789                 switch(c){
31790                     case 'b':
31791                         cmd = 'bold';
31792                         break;
31793                     case 'i':
31794                         cmd = 'italic';
31795                         break;
31796                     
31797                     case 'u':
31798                         cmd = 'underline';
31799                         break;
31800                     
31801                     //case 'v':
31802                       //  this.cleanUpPaste.defer(100, this);
31803                       //  return;
31804                         
31805                 }
31806                 if(cmd){
31807                     
31808                     this.relayCmd(cmd);
31809                     //this.win.focus();
31810                     //this.execCmd(cmd);
31811                     //this.deferFocus();
31812                     e.preventDefault();
31813                 }
31814                 
31815             }
31816         }
31817     },
31818
31819     // private
31820     fixKeys : function(){ // load time branching for fastest keydown performance
31821         
31822         
31823         if(Roo.isIE){
31824             return function(e){
31825                 var k = e.getKey(), r;
31826                 if(k == e.TAB){
31827                     e.stopEvent();
31828                     r = this.doc.selection.createRange();
31829                     if(r){
31830                         r.collapse(true);
31831                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31832                         this.deferFocus();
31833                     }
31834                     return;
31835                 }
31836                 /// this is handled by Roo.htmleditor.KeyEnter
31837                  /*
31838                 if(k == e.ENTER){
31839                     r = this.doc.selection.createRange();
31840                     if(r){
31841                         var target = r.parentElement();
31842                         if(!target || target.tagName.toLowerCase() != 'li'){
31843                             e.stopEvent();
31844                             r.pasteHTML('<br/>');
31845                             r.collapse(false);
31846                             r.select();
31847                         }
31848                     }
31849                 }
31850                 */
31851                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31852                 //    this.cleanUpPaste.defer(100, this);
31853                 //    return;
31854                 //}
31855                 
31856                 
31857             };
31858         }else if(Roo.isOpera){
31859             return function(e){
31860                 var k = e.getKey();
31861                 if(k == e.TAB){
31862                     e.stopEvent();
31863                     this.win.focus();
31864                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31865                     this.deferFocus();
31866                 }
31867                
31868                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31869                 //    this.cleanUpPaste.defer(100, this);
31870                  //   return;
31871                 //}
31872                 
31873             };
31874         }else if(Roo.isSafari){
31875             return function(e){
31876                 var k = e.getKey();
31877                 
31878                 if(k == e.TAB){
31879                     e.stopEvent();
31880                     this.execCmd('InsertText','\t');
31881                     this.deferFocus();
31882                     return;
31883                 }
31884                  this.mozKeyPress(e);
31885                 
31886                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31887                  //   this.cleanUpPaste.defer(100, this);
31888                  //   return;
31889                // }
31890                 
31891              };
31892         }
31893     }(),
31894     
31895     getAllAncestors: function()
31896     {
31897         var p = this.getSelectedNode();
31898         var a = [];
31899         if (!p) {
31900             a.push(p); // push blank onto stack..
31901             p = this.getParentElement();
31902         }
31903         
31904         
31905         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31906             a.push(p);
31907             p = p.parentNode;
31908         }
31909         a.push(this.doc.body);
31910         return a;
31911     },
31912     lastSel : false,
31913     lastSelNode : false,
31914     
31915     
31916     getSelection : function() 
31917     {
31918         this.assignDocWin();
31919         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31920     },
31921     /**
31922      * Select a dom node
31923      * @param {DomElement} node the node to select
31924      */
31925     selectNode : function(node, collapse)
31926     {
31927         var nodeRange = node.ownerDocument.createRange();
31928         try {
31929             nodeRange.selectNode(node);
31930         } catch (e) {
31931             nodeRange.selectNodeContents(node);
31932         }
31933         if (collapse === true) {
31934             nodeRange.collapse(true);
31935         }
31936         //
31937         var s = this.win.getSelection();
31938         s.removeAllRanges();
31939         s.addRange(nodeRange);
31940     },
31941     
31942     getSelectedNode: function() 
31943     {
31944         // this may only work on Gecko!!!
31945         
31946         // should we cache this!!!!
31947         
31948          
31949          
31950         var range = this.createRange(this.getSelection()).cloneRange();
31951         
31952         if (Roo.isIE) {
31953             var parent = range.parentElement();
31954             while (true) {
31955                 var testRange = range.duplicate();
31956                 testRange.moveToElementText(parent);
31957                 if (testRange.inRange(range)) {
31958                     break;
31959                 }
31960                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31961                     break;
31962                 }
31963                 parent = parent.parentElement;
31964             }
31965             return parent;
31966         }
31967         
31968         // is ancestor a text element.
31969         var ac =  range.commonAncestorContainer;
31970         if (ac.nodeType == 3) {
31971             ac = ac.parentNode;
31972         }
31973         
31974         var ar = ac.childNodes;
31975          
31976         var nodes = [];
31977         var other_nodes = [];
31978         var has_other_nodes = false;
31979         for (var i=0;i<ar.length;i++) {
31980             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31981                 continue;
31982             }
31983             // fullly contained node.
31984             
31985             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31986                 nodes.push(ar[i]);
31987                 continue;
31988             }
31989             
31990             // probably selected..
31991             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31992                 other_nodes.push(ar[i]);
31993                 continue;
31994             }
31995             // outer..
31996             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31997                 continue;
31998             }
31999             
32000             
32001             has_other_nodes = true;
32002         }
32003         if (!nodes.length && other_nodes.length) {
32004             nodes= other_nodes;
32005         }
32006         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32007             return false;
32008         }
32009         
32010         return nodes[0];
32011     },
32012     
32013     
32014     createRange: function(sel)
32015     {
32016         // this has strange effects when using with 
32017         // top toolbar - not sure if it's a great idea.
32018         //this.editor.contentWindow.focus();
32019         if (typeof sel != "undefined") {
32020             try {
32021                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32022             } catch(e) {
32023                 return this.doc.createRange();
32024             }
32025         } else {
32026             return this.doc.createRange();
32027         }
32028     },
32029     getParentElement: function()
32030     {
32031         
32032         this.assignDocWin();
32033         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32034         
32035         var range = this.createRange(sel);
32036          
32037         try {
32038             var p = range.commonAncestorContainer;
32039             while (p.nodeType == 3) { // text node
32040                 p = p.parentNode;
32041             }
32042             return p;
32043         } catch (e) {
32044             return null;
32045         }
32046     
32047     },
32048     /***
32049      *
32050      * Range intersection.. the hard stuff...
32051      *  '-1' = before
32052      *  '0' = hits..
32053      *  '1' = after.
32054      *         [ -- selected range --- ]
32055      *   [fail]                        [fail]
32056      *
32057      *    basically..
32058      *      if end is before start or  hits it. fail.
32059      *      if start is after end or hits it fail.
32060      *
32061      *   if either hits (but other is outside. - then it's not 
32062      *   
32063      *    
32064      **/
32065     
32066     
32067     // @see http://www.thismuchiknow.co.uk/?p=64.
32068     rangeIntersectsNode : function(range, node)
32069     {
32070         var nodeRange = node.ownerDocument.createRange();
32071         try {
32072             nodeRange.selectNode(node);
32073         } catch (e) {
32074             nodeRange.selectNodeContents(node);
32075         }
32076     
32077         var rangeStartRange = range.cloneRange();
32078         rangeStartRange.collapse(true);
32079     
32080         var rangeEndRange = range.cloneRange();
32081         rangeEndRange.collapse(false);
32082     
32083         var nodeStartRange = nodeRange.cloneRange();
32084         nodeStartRange.collapse(true);
32085     
32086         var nodeEndRange = nodeRange.cloneRange();
32087         nodeEndRange.collapse(false);
32088     
32089         return rangeStartRange.compareBoundaryPoints(
32090                  Range.START_TO_START, nodeEndRange) == -1 &&
32091                rangeEndRange.compareBoundaryPoints(
32092                  Range.START_TO_START, nodeStartRange) == 1;
32093         
32094          
32095     },
32096     rangeCompareNode : function(range, node)
32097     {
32098         var nodeRange = node.ownerDocument.createRange();
32099         try {
32100             nodeRange.selectNode(node);
32101         } catch (e) {
32102             nodeRange.selectNodeContents(node);
32103         }
32104         
32105         
32106         range.collapse(true);
32107     
32108         nodeRange.collapse(true);
32109      
32110         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32111         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32112          
32113         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32114         
32115         var nodeIsBefore   =  ss == 1;
32116         var nodeIsAfter    = ee == -1;
32117         
32118         if (nodeIsBefore && nodeIsAfter) {
32119             return 0; // outer
32120         }
32121         if (!nodeIsBefore && nodeIsAfter) {
32122             return 1; //right trailed.
32123         }
32124         
32125         if (nodeIsBefore && !nodeIsAfter) {
32126             return 2;  // left trailed.
32127         }
32128         // fully contined.
32129         return 3;
32130     },
32131  
32132     cleanWordChars : function(input) {// change the chars to hex code
32133         
32134        var swapCodes  = [ 
32135             [    8211, "&#8211;" ], 
32136             [    8212, "&#8212;" ], 
32137             [    8216,  "'" ],  
32138             [    8217, "'" ],  
32139             [    8220, '"' ],  
32140             [    8221, '"' ],  
32141             [    8226, "*" ],  
32142             [    8230, "..." ]
32143         ]; 
32144         var output = input;
32145         Roo.each(swapCodes, function(sw) { 
32146             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32147             
32148             output = output.replace(swapper, sw[1]);
32149         });
32150         
32151         return output;
32152     },
32153     
32154      
32155     
32156         
32157     
32158     cleanUpChild : function (node)
32159     {
32160         
32161         new Roo.htmleditor.FilterComment({node : node});
32162         new Roo.htmleditor.FilterAttributes({
32163                 node : node,
32164                 attrib_black : this.ablack,
32165                 attrib_clean : this.aclean,
32166                 style_white : this.cwhite,
32167                 style_black : this.cblack
32168         });
32169         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32170         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32171          
32172         
32173     },
32174     
32175     /**
32176      * Clean up MS wordisms...
32177      * @deprecated - use filter directly
32178      */
32179     cleanWord : function(node)
32180     {
32181         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32182         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32183         
32184     },
32185    
32186     
32187     /**
32188
32189      * @deprecated - use filters
32190      */
32191     cleanTableWidths : function(node)
32192     {
32193         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32194         
32195  
32196     },
32197     
32198      
32199         
32200     applyBlacklists : function()
32201     {
32202         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32203         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32204         
32205         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32206         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32207         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32208         
32209         this.white = [];
32210         this.black = [];
32211         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32212             if (b.indexOf(tag) > -1) {
32213                 return;
32214             }
32215             this.white.push(tag);
32216             
32217         }, this);
32218         
32219         Roo.each(w, function(tag) {
32220             if (b.indexOf(tag) > -1) {
32221                 return;
32222             }
32223             if (this.white.indexOf(tag) > -1) {
32224                 return;
32225             }
32226             this.white.push(tag);
32227             
32228         }, this);
32229         
32230         
32231         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32232             if (w.indexOf(tag) > -1) {
32233                 return;
32234             }
32235             this.black.push(tag);
32236             
32237         }, this);
32238         
32239         Roo.each(b, function(tag) {
32240             if (w.indexOf(tag) > -1) {
32241                 return;
32242             }
32243             if (this.black.indexOf(tag) > -1) {
32244                 return;
32245             }
32246             this.black.push(tag);
32247             
32248         }, this);
32249         
32250         
32251         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32252         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32253         
32254         this.cwhite = [];
32255         this.cblack = [];
32256         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32257             if (b.indexOf(tag) > -1) {
32258                 return;
32259             }
32260             this.cwhite.push(tag);
32261             
32262         }, this);
32263         
32264         Roo.each(w, function(tag) {
32265             if (b.indexOf(tag) > -1) {
32266                 return;
32267             }
32268             if (this.cwhite.indexOf(tag) > -1) {
32269                 return;
32270             }
32271             this.cwhite.push(tag);
32272             
32273         }, this);
32274         
32275         
32276         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32277             if (w.indexOf(tag) > -1) {
32278                 return;
32279             }
32280             this.cblack.push(tag);
32281             
32282         }, this);
32283         
32284         Roo.each(b, function(tag) {
32285             if (w.indexOf(tag) > -1) {
32286                 return;
32287             }
32288             if (this.cblack.indexOf(tag) > -1) {
32289                 return;
32290             }
32291             this.cblack.push(tag);
32292             
32293         }, this);
32294     },
32295     
32296     setStylesheets : function(stylesheets)
32297     {
32298         if(typeof(stylesheets) == 'string'){
32299             Roo.get(this.iframe.contentDocument.head).createChild({
32300                 tag : 'link',
32301                 rel : 'stylesheet',
32302                 type : 'text/css',
32303                 href : stylesheets
32304             });
32305             
32306             return;
32307         }
32308         var _this = this;
32309      
32310         Roo.each(stylesheets, function(s) {
32311             if(!s.length){
32312                 return;
32313             }
32314             
32315             Roo.get(_this.iframe.contentDocument.head).createChild({
32316                 tag : 'link',
32317                 rel : 'stylesheet',
32318                 type : 'text/css',
32319                 href : s
32320             });
32321         });
32322
32323         
32324     },
32325     
32326     
32327     updateLanguage : function()
32328     {
32329         if (!this.iframe || !this.iframe.contentDocument) {
32330             return;
32331         }
32332         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32333     },
32334     
32335     
32336     removeStylesheets : function()
32337     {
32338         var _this = this;
32339         
32340         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32341             s.remove();
32342         });
32343     },
32344     
32345     setStyle : function(style)
32346     {
32347         Roo.get(this.iframe.contentDocument.head).createChild({
32348             tag : 'style',
32349             type : 'text/css',
32350             html : style
32351         });
32352
32353         return;
32354     }
32355     
32356     // hide stuff that is not compatible
32357     /**
32358      * @event blur
32359      * @hide
32360      */
32361     /**
32362      * @event change
32363      * @hide
32364      */
32365     /**
32366      * @event focus
32367      * @hide
32368      */
32369     /**
32370      * @event specialkey
32371      * @hide
32372      */
32373     /**
32374      * @cfg {String} fieldClass @hide
32375      */
32376     /**
32377      * @cfg {String} focusClass @hide
32378      */
32379     /**
32380      * @cfg {String} autoCreate @hide
32381      */
32382     /**
32383      * @cfg {String} inputType @hide
32384      */
32385     /**
32386      * @cfg {String} invalidClass @hide
32387      */
32388     /**
32389      * @cfg {String} invalidText @hide
32390      */
32391     /**
32392      * @cfg {String} msgFx @hide
32393      */
32394     /**
32395      * @cfg {String} validateOnBlur @hide
32396      */
32397 });
32398
32399 Roo.HtmlEditorCore.white = [
32400         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32401         
32402        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32403        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32404        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32405        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32406        'TABLE',   'UL',         'XMP', 
32407        
32408        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32409       'THEAD',   'TR', 
32410      
32411       'DIR', 'MENU', 'OL', 'UL', 'DL',
32412        
32413       'EMBED',  'OBJECT'
32414 ];
32415
32416
32417 Roo.HtmlEditorCore.black = [
32418     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32419         'APPLET', // 
32420         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32421         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32422         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32423         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32424         //'FONT' // CLEAN LATER..
32425         'COLGROUP', 'COL'   // messy tables.
32426         
32427         
32428 ];
32429 Roo.HtmlEditorCore.clean = [ // ?? needed???
32430      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32431 ];
32432 Roo.HtmlEditorCore.tag_remove = [
32433     'FONT', 'TBODY'  
32434 ];
32435 // attributes..
32436
32437 Roo.HtmlEditorCore.ablack = [
32438     'on'
32439 ];
32440     
32441 Roo.HtmlEditorCore.aclean = [ 
32442     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32443 ];
32444
32445 // protocols..
32446 Roo.HtmlEditorCore.pwhite= [
32447         'http',  'https',  'mailto'
32448 ];
32449
32450 // white listed style attributes.
32451 Roo.HtmlEditorCore.cwhite= [
32452       //  'text-align', /// default is to allow most things..
32453       
32454          
32455 //        'font-size'//??
32456 ];
32457
32458 // black listed style attributes.
32459 Roo.HtmlEditorCore.cblack= [
32460       //  'font-size' -- this can be set by the project 
32461 ];
32462
32463
32464
32465
32466     /*
32467  * - LGPL
32468  *
32469  * HtmlEditor
32470  * 
32471  */
32472
32473 /**
32474  * @class Roo.bootstrap.form.HtmlEditor
32475  * @extends Roo.bootstrap.form.TextArea
32476  * Bootstrap HtmlEditor class
32477
32478  * @constructor
32479  * Create a new HtmlEditor
32480  * @param {Object} config The config object
32481  */
32482
32483 Roo.bootstrap.form.HtmlEditor = function(config){
32484
32485     this.addEvents({
32486             /**
32487              * @event initialize
32488              * Fires when the editor is fully initialized (including the iframe)
32489              * @param {Roo.bootstrap.form.HtmlEditor} this
32490              */
32491             initialize: true,
32492             /**
32493              * @event activate
32494              * Fires when the editor is first receives the focus. Any insertion must wait
32495              * until after this event.
32496              * @param {Roo.bootstrap.form.HtmlEditor} this
32497              */
32498             activate: true,
32499              /**
32500              * @event beforesync
32501              * Fires before the textarea is updated with content from the editor iframe. Return false
32502              * to cancel the sync.
32503              * @param {Roo.bootstrap.form.HtmlEditor} this
32504              * @param {String} html
32505              */
32506             beforesync: true,
32507              /**
32508              * @event beforepush
32509              * Fires before the iframe editor is updated with content from the textarea. Return false
32510              * to cancel the push.
32511              * @param {Roo.bootstrap.form.HtmlEditor} this
32512              * @param {String} html
32513              */
32514             beforepush: true,
32515              /**
32516              * @event sync
32517              * Fires when the textarea is updated with content from the editor iframe.
32518              * @param {Roo.bootstrap.form.HtmlEditor} this
32519              * @param {String} html
32520              */
32521             sync: true,
32522              /**
32523              * @event push
32524              * Fires when the iframe editor is updated with content from the textarea.
32525              * @param {Roo.bootstrap.form.HtmlEditor} this
32526              * @param {String} html
32527              */
32528             push: true,
32529              /**
32530              * @event editmodechange
32531              * Fires when the editor switches edit modes
32532              * @param {Roo.bootstrap.form.HtmlEditor} this
32533              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32534              */
32535             editmodechange: true,
32536             /**
32537              * @event editorevent
32538              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32539              * @param {Roo.bootstrap.form.HtmlEditor} this
32540              */
32541             editorevent: true,
32542             /**
32543              * @event firstfocus
32544              * Fires when on first focus - needed by toolbars..
32545              * @param {Roo.bootstrap.form.HtmlEditor} this
32546              */
32547             firstfocus: true,
32548             /**
32549              * @event autosave
32550              * Auto save the htmlEditor value as a file into Events
32551              * @param {Roo.bootstrap.form.HtmlEditor} this
32552              */
32553             autosave: true,
32554             /**
32555              * @event savedpreview
32556              * preview the saved version of htmlEditor
32557              * @param {Roo.bootstrap.form.HtmlEditor} this
32558              */
32559             savedpreview: true,
32560              /**
32561             * @event stylesheetsclick
32562             * Fires when press the Sytlesheets button
32563             * @param {Roo.HtmlEditorCore} this
32564             */
32565             stylesheetsclick: true,
32566             /**
32567             * @event paste
32568             * Fires when press user pastes into the editor
32569             * @param {Roo.HtmlEditorCore} this
32570             */
32571             paste: true,
32572             /**
32573             * @event imageadd
32574             * Fires when on any editor when an image is added (excluding paste)
32575             * @param {Roo.bootstrap.form.HtmlEditor} this
32576             */
32577            imageadd: true ,
32578             /**
32579             * @event imageupdated
32580             * Fires when on any editor when an image is changed (excluding paste)
32581             * @param {Roo.bootstrap.form.HtmlEditor} this
32582             * @param {HTMLElement} img could also be a figure if blocks are enabled
32583             */
32584            imageupdate: true ,
32585            /**
32586             * @event imagedelete
32587             * Fires when on any editor when an image is deleted
32588             * @param {Roo.bootstrap.form.HtmlEditor} this
32589             * @param {HTMLElement} img could also be a figure if blocks are enabled
32590             */
32591            imagedelete: true  
32592     });
32593     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32594     if (!this.toolbars) {
32595         this.toolbars = [];
32596     }
32597     
32598     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32599     
32600 };
32601
32602
32603 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32604     
32605     
32606       /**
32607      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32608      */
32609     toolbars : true,
32610     
32611      /**
32612     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32613     */
32614     btns : [],
32615    
32616      /**
32617      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32618      */
32619     resize : false,
32620      /**
32621      * @cfg {Number} height (in pixels)
32622      */   
32623     height: 300,
32624    /**
32625      * @cfg {Number} width (in pixels)
32626      */   
32627     width: false,
32628     
32629     /**
32630      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32631      * 
32632      */
32633     stylesheets: false,
32634     
32635     // id of frame..
32636     frameId: false,
32637     
32638     // private properties
32639     validationEvent : false,
32640     deferHeight: true,
32641     initialized : false,
32642     activated : false,
32643     
32644     onFocus : Roo.emptyFn,
32645     iframePad:3,
32646     hideMode:'offsets',
32647     
32648     tbContainer : false,
32649     
32650     bodyCls : '',
32651     
32652     toolbarContainer :function() {
32653         return this.wrap.select('.x-html-editor-tb',true).first();
32654     },
32655
32656     /**
32657      * Protected method that will not generally be called directly. It
32658      * is called when the editor creates its toolbar. Override this method if you need to
32659      * add custom toolbar buttons.
32660      * @param {HtmlEditor} editor
32661      */
32662     createToolbar : function()
32663     {
32664         //Roo.log('renewing');
32665         //Roo.log("create toolbars");
32666         if (this.toolbars === false) {
32667             return;
32668         }
32669         if (this.toolbars === true) {
32670             this.toolbars = [ 'Standard' ];
32671         }
32672         
32673         var ar = Array.from(this.toolbars);
32674         this.toolbars = [];
32675         ar.forEach(function(t,i) {
32676             if (typeof(t) == 'string') {
32677                 t = {
32678                     xtype : t
32679                 };
32680             }
32681             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32682                 t.editor = this;
32683                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32684                 t = Roo.factory(t);
32685             }
32686             this.toolbars[i] = t;
32687             this.toolbars[i].render(this.toolbarContainer());
32688         }, this);
32689         
32690         
32691     },
32692
32693      
32694     // private
32695     onRender : function(ct, position)
32696     {
32697        // Roo.log("Call onRender: " + this.xtype);
32698         var _t = this;
32699         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32700       
32701         this.wrap = this.inputEl().wrap({
32702             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32703         });
32704         
32705         this.editorcore.onRender(ct, position);
32706          
32707          
32708         this.createToolbar(this);
32709        
32710         
32711           
32712         
32713     },
32714
32715     // private
32716     onResize : function(w, h)
32717     {
32718         Roo.log('resize: ' +w + ',' + h );
32719         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32720         var ew = false;
32721         var eh = false;
32722         
32723         if(this.inputEl() ){
32724             if(typeof w == 'number'){
32725                 var aw = w - this.wrap.getFrameWidth('lr');
32726                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32727                 ew = aw;
32728             }
32729             if(typeof h == 'number'){
32730                  var tbh = -11;  // fixme it needs to tool bar size!
32731                 for (var i =0; i < this.toolbars.length;i++) {
32732                     // fixme - ask toolbars for heights?
32733                     tbh += this.toolbars[i].el.getHeight();
32734                     //if (this.toolbars[i].footer) {
32735                     //    tbh += this.toolbars[i].footer.el.getHeight();
32736                     //}
32737                 }
32738               
32739                 
32740                 
32741                 
32742                 
32743                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32744                 ah -= 5; // knock a few pixes off for look..
32745                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32746                 var eh = ah;
32747             }
32748         }
32749         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32750         this.editorcore.onResize(ew,eh);
32751         
32752     },
32753
32754     /**
32755      * Toggles the editor between standard and source edit mode.
32756      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32757      */
32758     toggleSourceEdit : function(sourceEditMode)
32759     {
32760         this.editorcore.toggleSourceEdit(sourceEditMode);
32761         
32762         if(this.editorcore.sourceEditMode){
32763             Roo.log('editor - showing textarea');
32764             
32765 //            Roo.log('in');
32766 //            Roo.log(this.syncValue());
32767             this.syncValue();
32768             this.inputEl().removeClass(['hide', 'x-hidden']);
32769             this.inputEl().dom.removeAttribute('tabIndex');
32770             this.inputEl().focus();
32771         }else{
32772             Roo.log('editor - hiding textarea');
32773 //            Roo.log('out')
32774 //            Roo.log(this.pushValue()); 
32775             this.pushValue();
32776             
32777             this.inputEl().addClass(['hide', 'x-hidden']);
32778             this.inputEl().dom.setAttribute('tabIndex', -1);
32779             //this.deferFocus();
32780         }
32781          
32782         //if(this.resizable){
32783         //    this.setSize(this.wrap.getSize());
32784         //}
32785         
32786         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32787     },
32788  
32789     // private (for BoxComponent)
32790     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32791
32792     // private (for BoxComponent)
32793     getResizeEl : function(){
32794         return this.wrap;
32795     },
32796
32797     // private (for BoxComponent)
32798     getPositionEl : function(){
32799         return this.wrap;
32800     },
32801
32802     // private
32803     initEvents : function(){
32804         this.originalValue = this.getValue();
32805     },
32806
32807 //    /**
32808 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32809 //     * @method
32810 //     */
32811 //    markInvalid : Roo.emptyFn,
32812 //    /**
32813 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32814 //     * @method
32815 //     */
32816 //    clearInvalid : Roo.emptyFn,
32817
32818     setValue : function(v){
32819         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32820         this.editorcore.pushValue();
32821     },
32822
32823      
32824     // private
32825     deferFocus : function(){
32826         this.focus.defer(10, this);
32827     },
32828
32829     // doc'ed in Field
32830     focus : function(){
32831         this.editorcore.focus();
32832         
32833     },
32834       
32835
32836     // private
32837     onDestroy : function(){
32838         
32839         
32840         
32841         if(this.rendered){
32842             
32843             for (var i =0; i < this.toolbars.length;i++) {
32844                 // fixme - ask toolbars for heights?
32845                 this.toolbars[i].onDestroy();
32846             }
32847             
32848             this.wrap.dom.innerHTML = '';
32849             this.wrap.remove();
32850         }
32851     },
32852
32853     // private
32854     onFirstFocus : function(){
32855         //Roo.log("onFirstFocus");
32856         this.editorcore.onFirstFocus();
32857          for (var i =0; i < this.toolbars.length;i++) {
32858             this.toolbars[i].onFirstFocus();
32859         }
32860         
32861     },
32862     
32863     // private
32864     syncValue : function()
32865     {   
32866         this.editorcore.syncValue();
32867     },
32868     
32869     pushValue : function()
32870     {   
32871         this.editorcore.pushValue();
32872     }
32873      
32874     
32875     // hide stuff that is not compatible
32876     /**
32877      * @event blur
32878      * @hide
32879      */
32880     /**
32881      * @event change
32882      * @hide
32883      */
32884     /**
32885      * @event focus
32886      * @hide
32887      */
32888     /**
32889      * @event specialkey
32890      * @hide
32891      */
32892     /**
32893      * @cfg {String} fieldClass @hide
32894      */
32895     /**
32896      * @cfg {String} focusClass @hide
32897      */
32898     /**
32899      * @cfg {String} autoCreate @hide
32900      */
32901     /**
32902      * @cfg {String} inputType @hide
32903      */
32904      
32905     /**
32906      * @cfg {String} invalidText @hide
32907      */
32908     /**
32909      * @cfg {String} msgFx @hide
32910      */
32911     /**
32912      * @cfg {String} validateOnBlur @hide
32913      */
32914 });
32915  
32916     
32917    
32918    
32919    
32920       
32921 /**
32922  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32923  * @parent Roo.bootstrap.form.HtmlEditor
32924  * @extends Roo.bootstrap.nav.Simplebar
32925  * Basic Toolbar
32926  * 
32927  * @example
32928  * Usage:
32929  *
32930  new Roo.bootstrap.form.HtmlEditor({
32931     ....
32932     toolbars : [
32933         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32934             disable : { fonts: 1 , format: 1, ..., ... , ...],
32935             btns : [ .... ]
32936         })
32937     }
32938      
32939  * 
32940  * @cfg {Object} disable List of elements to disable..
32941  * @cfg {Array} btns List of additional buttons.
32942  * 
32943  * 
32944  * NEEDS Extra CSS? 
32945  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32946  */
32947  
32948 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32949 {
32950     
32951     Roo.apply(this, config);
32952     
32953     // default disabled, based on 'good practice'..
32954     this.disable = this.disable || {};
32955     Roo.applyIf(this.disable, {
32956         fontSize : true,
32957         colors : true,
32958         specialElements : true
32959     });
32960     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32961     
32962     this.editor = config.editor;
32963     this.editorcore = config.editor.editorcore;
32964     
32965     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32966     
32967     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32968     // dont call parent... till later.
32969 }
32970 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
32971      
32972     bar : true,
32973     
32974     editor : false,
32975     editorcore : false,
32976     
32977     
32978     formats : [
32979         "p" ,  
32980         "h1","h2","h3","h4","h5","h6", 
32981         "pre", "code", 
32982         "abbr", "acronym", "address", "cite", "samp", "var",
32983         'div','span'
32984     ],
32985     
32986     
32987     deleteBtn: false,
32988     
32989     onRender : function(ct, position)
32990     {
32991        // Roo.log("Call onRender: " + this.xtype);
32992         
32993        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32994        Roo.log(this.el);
32995        this.el.dom.style.marginBottom = '0';
32996        var _this = this;
32997        var editorcore = this.editorcore;
32998        var editor= this.editor;
32999        
33000        var children = [];
33001        var btn = function(id, cmd , toggle, handler, html){
33002        
33003             var  event = toggle ? 'toggle' : 'click';
33004        
33005             var a = {
33006                 size : 'sm',
33007                 xtype: 'Button',
33008                 xns: Roo.bootstrap,
33009                 //glyphicon : id,
33010                 btnid : id,
33011                 fa: id,
33012                 cls : 'roo-html-editor-btn-' + id,
33013                 cmd : cmd, // why id || cmd
33014                 enableToggle: toggle !== false,
33015                 html : html || '',
33016                 pressed : toggle ? false : null,
33017                 listeners : {}
33018             };
33019             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33020                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33021             };
33022             children.push(a);
33023             return a;
33024        }
33025        
33026     //    var cb_box = function...
33027         
33028         var style = {
33029                 xtype: 'Button',
33030                 size : 'sm',
33031                 xns: Roo.bootstrap,
33032                 fa : 'font',
33033                 cls : 'roo-html-editor-font-chooser',
33034                 //html : 'submit'
33035                 menu : {
33036                     xtype: 'Menu',
33037                     xns: Roo.bootstrap,
33038                     items:  []
33039                 }
33040         };
33041         Roo.each(this.formats, function(f) {
33042             style.menu.items.push({
33043                 xtype :'MenuItem',
33044                 xns: Roo.bootstrap,
33045                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33046                 tagname : f,
33047                 listeners : {
33048                     click : function()
33049                     {
33050                         editorcore.insertTag(this.tagname);
33051                         editor.focus();
33052                     }
33053                 }
33054                 
33055             });
33056         });
33057         children.push(style);   
33058         
33059         btn('bold',         'bold',true);
33060         btn('italic',       'italic',true);
33061         btn('underline',     'underline',true);
33062         btn('align-left',   'justifyleft',true);
33063         btn('align-center', 'justifycenter',true);
33064         btn('align-right' , 'justifyright',true);
33065         btn('link', false, true, this.onLinkClick);
33066         
33067         
33068         btn('image', false, true, this.onImageClick);
33069         btn('list','insertunorderedlist',true);
33070         btn('list-ol','insertorderedlist',true);
33071
33072         btn('pencil', false,true, function(btn){
33073                 Roo.log(this);
33074                 this.toggleSourceEdit(btn.pressed);
33075         });
33076         
33077         if (this.editor.btns.length > 0) {
33078             for (var i = 0; i<this.editor.btns.length; i++) {
33079                 children.push(this.editor.btns[i]);
33080             }
33081         }
33082         
33083         
33084          
33085         this.xtype = 'NavSimplebar'; // why?
33086         
33087         for(var i=0;i< children.length;i++) {
33088             
33089             this.buttons.add(this.addxtypeChild(children[i]));
33090             
33091         }
33092         this.buildToolbarDelete();
33093
33094         editor.on('editorevent', this.updateToolbar, this);
33095     },
33096     
33097     buildToolbarDelete : function()
33098     {
33099         
33100        /* this.addxtypeChild({
33101             xtype : 'Element',
33102             xns : Roo.bootstrap,
33103             cls : 'roo-htmleditor-fill'
33104         });
33105         */
33106         this.deleteBtn = this.addxtypeChild({
33107             size : 'sm',
33108             xtype: 'Button',
33109             xns: Roo.bootstrap,
33110             fa: 'trash',
33111             listeners : {
33112                 click : this.onDelete.createDelegate(this)
33113             }
33114         });
33115         this.deleteBtn.hide();     
33116         
33117     },
33118     
33119     onImageClick : function()
33120     {
33121         if (this.input) {
33122             this.input.un('change', this.onFileSelected, this);
33123         }
33124         this.input = Roo.get(document.body).createChild({ 
33125           tag: 'input', 
33126           type : 'file', 
33127           style : 'display:none', 
33128           multiple: 'multiple'
33129        });
33130         this.input.on('change', this.onFileSelected, this);
33131         this.input.dom.click();
33132     },
33133     
33134     onFileSelected : function(e)
33135     {
33136          e.preventDefault();
33137         
33138         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33139             return;
33140         }
33141     
33142          
33143         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33144     },
33145     
33146     addFiles : function(far, fire_add) {
33147
33148          
33149         var editor =  this.editorcore;
33150   
33151         if (!far.length) {
33152             if (fire_add) {
33153                 this.editor.syncValue();
33154                 editor.owner.fireEvent('editorevent', editor.owner, false);
33155                 editor.owner.fireEvent('imageadd', editor.owner, false);
33156             }
33157             return;
33158         }
33159         
33160         var f = far.pop();
33161         
33162         if (!f.type.match(/^image/)) {
33163             this.addFiles(far, fire_add);
33164             return;
33165         }
33166          
33167         var sn = this.selectedNode;
33168         
33169         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33170         
33171         
33172         var reader = new FileReader();
33173         reader.addEventListener('load', (function() {
33174             if (bl) {
33175                 bl.image_src = reader.result;
33176                 //bl.caption = f.name;
33177                 bl.updateElement(sn);
33178                 this.editor.syncValue();
33179                 editor.owner.fireEvent('editorevent', editor.owner, false);
33180                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33181                 // we only do the first file!! and replace.
33182                 return;
33183             }
33184             if (this.editorcore.enableBlocks) {
33185                 var fig = new Roo.htmleditor.BlockFigure({
33186                     image_src :  reader.result,
33187                     caption : '',
33188                     caption_display : 'none'  //default to hide captions..
33189                  });
33190                 editor.insertAtCursor(fig.toHTML());
33191                 this.addFiles(far, true);
33192                 return;
33193             }
33194             // just a standard img..
33195             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33196                 sn.src = reader.result;
33197                 this.editor.syncValue();
33198                 editor.owner.fireEvent('editorevent', editor.owner, false);
33199                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33200                 return;
33201             }
33202             editor.insertAtCursor('<img src="' + reader.result +'">');
33203             this.addFiles(far, true);
33204             
33205         }).createDelegate(this));
33206         reader.readAsDataURL(f);
33207         
33208     
33209      },
33210     
33211     
33212     onBtnClick : function(id)
33213     {
33214        this.editorcore.relayCmd(id);
33215        this.editorcore.focus();
33216     },
33217     
33218     onLinkClick : function(btn) {
33219         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33220                 this.selectedNode.getAttribute('href') : '';
33221             
33222         Roo.bootstrap.MessageBox.show({
33223             title : "Add / Edit Link URL",
33224             msg : "Enter the URL for the link",
33225             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33226             minWidth: 250,
33227             scope : this,
33228             prompt:true,
33229             multiline: false,
33230             modal : true,
33231             value : url,
33232             fn:  function(pressed, newurl) {
33233                 if (pressed != 'ok') {
33234                     this.editorcore.focus();
33235                     return;
33236                 }
33237                 if (url != '') {
33238                     this.selectedNode.setAttribute('href', newurl);
33239                     return;
33240                 }
33241                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33242                     this.editorcore.relayCmd('createlink', newurl);
33243                 }
33244                 this.editorcore.focus();
33245             }
33246         });
33247     },
33248     /**
33249      * Protected method that will not generally be called directly. It triggers
33250      * a toolbar update by reading the markup state of the current selection in the editor.
33251      */
33252     updateToolbar: function(editor ,ev, sel){
33253
33254         if(!this.editorcore.activated){
33255             this.editor.onFirstFocus(); // is this neeed?
33256             return;
33257         }
33258
33259         var btns = this.buttons; 
33260         var doc = this.editorcore.doc;
33261         var hasToggle  = false;
33262         btns.each(function(e) {
33263             if (e.enableToggle && e.cmd) {
33264                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33265                 e.setActive(doc.queryCommandState(e.cmd));
33266             }
33267         }, this);
33268         
33269         
33270         if (ev &&
33271             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33272             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33273             // they have click on an image...
33274             // let's see if we can change the selection...
33275             sel = ev.target;
33276             
33277         }
33278         
33279         var ans = this.editorcore.getAllAncestors();
33280         if (!sel) { 
33281             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33282             sel = sel ? sel : this.editorcore.doc.body;
33283             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33284             
33285         }
33286         
33287         var lastSel = this.selectedNode;
33288         this.selectedNode = sel;
33289          
33290         // ok see if we are editing a block?
33291         
33292         var db = false;
33293         // you are not actually selecting the block.
33294         if (sel && sel.hasAttribute('data-block')) {
33295             db = sel;
33296         } else if (sel && sel.closest('[data-block]')) {
33297             db = sel.closest('[data-block]');
33298         }
33299         
33300         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33301             e.classList.remove('roo-ed-selection');
33302         });
33303         
33304         var block = false;
33305         if (db && this.editorcore.enableBlocks) {
33306             block = Roo.htmleditor.Block.factory(db);
33307             
33308             if (block) {
33309                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33310                     ' roo-ed-selection';
33311                 sel = this.selectedNode = db;
33312             }
33313         }
33314         
33315         // highlight the 'a'..
33316         var tn = sel && sel.tagName.toUpperCase() || '';
33317         if (!block && sel && tn != 'A') {
33318             var asel = sel.closest('A');
33319             if (asel) {
33320                 sel = asel;
33321             }
33322         }
33323        
33324         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33325         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33326         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33327         
33328         Roo.bootstrap.menu.Manager.hideAll();
33329          
33330         
33331         
33332         
33333         
33334         // handle delete button..
33335         if (hasToggle || (tn.length && tn == 'BODY')) {
33336             this.deleteBtn.hide();
33337             return;
33338             
33339         }
33340         this.deleteBtn.show();
33341         
33342         
33343         
33344         //this.editorsyncValue();
33345     },
33346     onFirstFocus: function() {
33347         this.buttons.each(function(item){
33348            item.enable();
33349         });
33350     },
33351     
33352     onDelete : function()
33353     {
33354         var range = this.editorcore.createRange();
33355         var selection = this.editorcore.getSelection();
33356         var sn = this.selectedNode;
33357         range.setStart(sn,0);
33358         range.setEnd(sn,0); 
33359         
33360         
33361         if (sn.hasAttribute('data-block')) {
33362             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33363             if (block) {
33364                 sn = block.removeNode();
33365                 sn.parentNode.removeChild(sn);
33366                 selection.removeAllRanges();
33367                 selection.addRange(range);
33368                 this.updateToolbar(null, null, null);
33369                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33370                     this.editor.syncValue();
33371                     this.editor.fireEvent('imagedelete', this.editor, sn);
33372                 }
33373                 
33374                 this.selectedNode = false;
33375                 this.editorcore.fireEditorEvent(false);
33376                 return;
33377             }   
33378              
33379         }
33380         if (!sn) {
33381             return; // should not really happen..
33382         }
33383         if (sn && sn.tagName == 'BODY') {
33384             return;
33385         }
33386         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33387         
33388         // remove and keep parents.
33389         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33390         a.replaceTag(sn);
33391         
33392         selection.removeAllRanges();
33393         selection.addRange(range);
33394         if (sn.tagName.toUpperCase() == 'IMG"') {
33395             this.editor.syncValue();
33396             this.editor.fireEvent('imagedelete', this.editor, sn);
33397         }
33398         
33399         this.selectedNode = false;
33400         this.editorcore.fireEditorEvent(false);
33401         
33402         
33403     },
33404     
33405     
33406     toggleSourceEdit : function(sourceEditMode){
33407         
33408           
33409         if(sourceEditMode){
33410             Roo.log("disabling buttons");
33411            this.buttons.each( function(item){
33412                 if(item.cmd != 'pencil'){
33413                     item.disable();
33414                 }
33415             });
33416           
33417         }else{
33418             Roo.log("enabling buttons");
33419             if(this.editorcore.initialized){
33420                 this.buttons.each( function(item){
33421                     item.enable();
33422                 });
33423             }
33424             
33425         }
33426         Roo.log("calling toggole on editor");
33427         // tell the editor that it's been pressed..
33428         this.editor.toggleSourceEdit(sourceEditMode);
33429        
33430     }
33431 });
33432
33433
33434
33435
33436  
33437 /*
33438  * - LGPL
33439  */
33440
33441 /**
33442  * @class Roo.bootstrap.form.Markdown
33443  * @extends Roo.bootstrap.form.TextArea
33444  * Bootstrap Showdown editable area
33445  * @cfg {string} content
33446  * 
33447  * @constructor
33448  * Create a new Showdown
33449  */
33450
33451 Roo.bootstrap.form.Markdown = function(config){
33452     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33453    
33454 };
33455
33456 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33457     
33458     editing :false,
33459     
33460     initEvents : function()
33461     {
33462         
33463         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33464         this.markdownEl = this.el.createChild({
33465             cls : 'roo-markdown-area'
33466         });
33467         this.inputEl().addClass('d-none');
33468         if (this.getValue() == '') {
33469             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33470             
33471         } else {
33472             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33473         }
33474         this.markdownEl.on('click', this.toggleTextEdit, this);
33475         this.on('blur', this.toggleTextEdit, this);
33476         this.on('specialkey', this.resizeTextArea, this);
33477     },
33478     
33479     toggleTextEdit : function()
33480     {
33481         var sh = this.markdownEl.getHeight();
33482         this.inputEl().addClass('d-none');
33483         this.markdownEl.addClass('d-none');
33484         if (!this.editing) {
33485             // show editor?
33486             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33487             this.inputEl().removeClass('d-none');
33488             this.inputEl().focus();
33489             this.editing = true;
33490             return;
33491         }
33492         // show showdown...
33493         this.updateMarkdown();
33494         this.markdownEl.removeClass('d-none');
33495         this.editing = false;
33496         return;
33497     },
33498     updateMarkdown : function()
33499     {
33500         if (this.getValue() == '') {
33501             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33502             return;
33503         }
33504  
33505         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33506     },
33507     
33508     resizeTextArea: function () {
33509         
33510         var sh = 100;
33511         Roo.log([sh, this.getValue().split("\n").length * 30]);
33512         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33513     },
33514     setValue : function(val)
33515     {
33516         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33517         if (!this.editing) {
33518             this.updateMarkdown();
33519         }
33520         
33521     },
33522     focus : function()
33523     {
33524         if (!this.editing) {
33525             this.toggleTextEdit();
33526         }
33527         
33528     }
33529
33530
33531 });/*
33532  * Based on:
33533  * Ext JS Library 1.1.1
33534  * Copyright(c) 2006-2007, Ext JS, LLC.
33535  *
33536  * Originally Released Under LGPL - original licence link has changed is not relivant.
33537  *
33538  * Fork - LGPL
33539  * <script type="text/javascript">
33540  */
33541  
33542 /**
33543  * @class Roo.bootstrap.PagingToolbar
33544  * @extends Roo.bootstrap.nav.Simplebar
33545  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33546  * @constructor
33547  * Create a new PagingToolbar
33548  * @param {Object} config The config object
33549  * @param {Roo.data.Store} store
33550  */
33551 Roo.bootstrap.PagingToolbar = function(config)
33552 {
33553     // old args format still supported... - xtype is prefered..
33554         // created from xtype...
33555     
33556     this.ds = config.dataSource;
33557     
33558     if (config.store && !this.ds) {
33559         this.store= Roo.factory(config.store, Roo.data);
33560         this.ds = this.store;
33561         this.ds.xmodule = this.xmodule || false;
33562     }
33563     
33564     this.toolbarItems = [];
33565     if (config.items) {
33566         this.toolbarItems = config.items;
33567     }
33568     
33569     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33570     
33571     this.cursor = 0;
33572     
33573     if (this.ds) { 
33574         this.bind(this.ds);
33575     }
33576     
33577     if (Roo.bootstrap.version == 4) {
33578         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33579     } else {
33580         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33581     }
33582     
33583 };
33584
33585 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33586     /**
33587      * @cfg {Roo.bootstrap.Button} buttons[]
33588      * Buttons for the toolbar
33589      */
33590      /**
33591      * @cfg {Roo.data.Store} store
33592      * The underlying data store providing the paged data
33593      */
33594     /**
33595      * @cfg {String/HTMLElement/Element} container
33596      * container The id or element that will contain the toolbar
33597      */
33598     /**
33599      * @cfg {Boolean} displayInfo
33600      * True to display the displayMsg (defaults to false)
33601      */
33602     /**
33603      * @cfg {Number} pageSize
33604      * The number of records to display per page (defaults to 20)
33605      */
33606     pageSize: 20,
33607     /**
33608      * @cfg {String} displayMsg
33609      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33610      */
33611     displayMsg : 'Displaying {0} - {1} of {2}',
33612     /**
33613      * @cfg {String} emptyMsg
33614      * The message to display when no records are found (defaults to "No data to display")
33615      */
33616     emptyMsg : 'No data to display',
33617     /**
33618      * Customizable piece of the default paging text (defaults to "Page")
33619      * @type String
33620      */
33621     beforePageText : "Page",
33622     /**
33623      * Customizable piece of the default paging text (defaults to "of %0")
33624      * @type String
33625      */
33626     afterPageText : "of {0}",
33627     /**
33628      * Customizable piece of the default paging text (defaults to "First Page")
33629      * @type String
33630      */
33631     firstText : "First Page",
33632     /**
33633      * Customizable piece of the default paging text (defaults to "Previous Page")
33634      * @type String
33635      */
33636     prevText : "Previous Page",
33637     /**
33638      * Customizable piece of the default paging text (defaults to "Next Page")
33639      * @type String
33640      */
33641     nextText : "Next Page",
33642     /**
33643      * Customizable piece of the default paging text (defaults to "Last Page")
33644      * @type String
33645      */
33646     lastText : "Last Page",
33647     /**
33648      * Customizable piece of the default paging text (defaults to "Refresh")
33649      * @type String
33650      */
33651     refreshText : "Refresh",
33652
33653     buttons : false,
33654     // private
33655     onRender : function(ct, position) 
33656     {
33657         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33658         this.navgroup.parentId = this.id;
33659         this.navgroup.onRender(this.el, null);
33660         // add the buttons to the navgroup
33661         
33662         if(this.displayInfo){
33663             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33664             this.displayEl = this.el.select('.x-paging-info', true).first();
33665 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33666 //            this.displayEl = navel.el.select('span',true).first();
33667         }
33668         
33669         var _this = this;
33670         
33671         if(this.buttons){
33672             Roo.each(_this.buttons, function(e){ // this might need to use render????
33673                Roo.factory(e).render(_this.el);
33674             });
33675         }
33676             
33677         Roo.each(_this.toolbarItems, function(e) {
33678             _this.navgroup.addItem(e);
33679         });
33680         
33681         
33682         this.first = this.navgroup.addItem({
33683             tooltip: this.firstText,
33684             cls: "prev btn-outline-secondary",
33685             html : ' <i class="fa fa-step-backward"></i>',
33686             disabled: true,
33687             preventDefault: true,
33688             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33689         });
33690         
33691         this.prev =  this.navgroup.addItem({
33692             tooltip: this.prevText,
33693             cls: "prev btn-outline-secondary",
33694             html : ' <i class="fa fa-backward"></i>',
33695             disabled: true,
33696             preventDefault: true,
33697             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33698         });
33699     //this.addSeparator();
33700         
33701         
33702         var field = this.navgroup.addItem( {
33703             tagtype : 'span',
33704             cls : 'x-paging-position  btn-outline-secondary',
33705              disabled: true,
33706             html : this.beforePageText  +
33707                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33708                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33709          } ); //?? escaped?
33710         
33711         this.field = field.el.select('input', true).first();
33712         this.field.on("keydown", this.onPagingKeydown, this);
33713         this.field.on("focus", function(){this.dom.select();});
33714     
33715     
33716         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33717         //this.field.setHeight(18);
33718         //this.addSeparator();
33719         this.next = this.navgroup.addItem({
33720             tooltip: this.nextText,
33721             cls: "next btn-outline-secondary",
33722             html : ' <i class="fa fa-forward"></i>',
33723             disabled: true,
33724             preventDefault: true,
33725             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33726         });
33727         this.last = this.navgroup.addItem({
33728             tooltip: this.lastText,
33729             html : ' <i class="fa fa-step-forward"></i>',
33730             cls: "next btn-outline-secondary",
33731             disabled: true,
33732             preventDefault: true,
33733             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33734         });
33735     //this.addSeparator();
33736         this.loading = this.navgroup.addItem({
33737             tooltip: this.refreshText,
33738             cls: "btn-outline-secondary",
33739             html : ' <i class="fa fa-refresh"></i>',
33740             preventDefault: true,
33741             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33742         });
33743         
33744     },
33745
33746     // private
33747     updateInfo : function(){
33748         if(this.displayEl){
33749             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33750             var msg = count == 0 ?
33751                 this.emptyMsg :
33752                 String.format(
33753                     this.displayMsg,
33754                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33755                 );
33756             this.displayEl.update(msg);
33757         }
33758     },
33759
33760     // private
33761     onLoad : function(ds, r, o)
33762     {
33763         this.cursor = o.params && o.params.start ? o.params.start : 0;
33764         
33765         var d = this.getPageData(),
33766             ap = d.activePage,
33767             ps = d.pages;
33768         
33769         
33770         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33771         this.field.dom.value = ap;
33772         this.first.setDisabled(ap == 1);
33773         this.prev.setDisabled(ap == 1);
33774         this.next.setDisabled(ap == ps);
33775         this.last.setDisabled(ap == ps);
33776         this.loading.enable();
33777         this.updateInfo();
33778     },
33779
33780     // private
33781     getPageData : function(){
33782         var total = this.ds.getTotalCount();
33783         return {
33784             total : total,
33785             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33786             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33787         };
33788     },
33789
33790     // private
33791     onLoadError : function(proxy, o){
33792         this.loading.enable();
33793         if (this.ds.events.loadexception.listeners.length  < 2) {
33794             // nothing has been assigned to loadexception except this...
33795             // so 
33796             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33797
33798         }
33799     },
33800
33801     // private
33802     onPagingKeydown : function(e){
33803         var k = e.getKey();
33804         var d = this.getPageData();
33805         if(k == e.RETURN){
33806             var v = this.field.dom.value, pageNum;
33807             if(!v || isNaN(pageNum = parseInt(v, 10))){
33808                 this.field.dom.value = d.activePage;
33809                 return;
33810             }
33811             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33812             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33813             e.stopEvent();
33814         }
33815         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))
33816         {
33817           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33818           this.field.dom.value = pageNum;
33819           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33820           e.stopEvent();
33821         }
33822         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33823         {
33824           var v = this.field.dom.value, pageNum; 
33825           var increment = (e.shiftKey) ? 10 : 1;
33826           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33827                 increment *= -1;
33828           }
33829           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33830             this.field.dom.value = d.activePage;
33831             return;
33832           }
33833           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33834           {
33835             this.field.dom.value = parseInt(v, 10) + increment;
33836             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33837             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33838           }
33839           e.stopEvent();
33840         }
33841     },
33842
33843     // private
33844     beforeLoad : function(){
33845         if(this.loading){
33846             this.loading.disable();
33847         }
33848     },
33849
33850     // private
33851     onClick : function(which){
33852         
33853         var ds = this.ds;
33854         if (!ds) {
33855             return;
33856         }
33857         
33858         switch(which){
33859             case "first":
33860                 ds.load({params:{start: 0, limit: this.pageSize}});
33861             break;
33862             case "prev":
33863                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33864             break;
33865             case "next":
33866                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33867             break;
33868             case "last":
33869                 var total = ds.getTotalCount();
33870                 var extra = total % this.pageSize;
33871                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33872                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33873             break;
33874             case "refresh":
33875                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33876             break;
33877         }
33878     },
33879
33880     /**
33881      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33882      * @param {Roo.data.Store} store The data store to unbind
33883      */
33884     unbind : function(ds){
33885         ds.un("beforeload", this.beforeLoad, this);
33886         ds.un("load", this.onLoad, this);
33887         ds.un("loadexception", this.onLoadError, this);
33888         ds.un("remove", this.updateInfo, this);
33889         ds.un("add", this.updateInfo, this);
33890         this.ds = undefined;
33891     },
33892
33893     /**
33894      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33895      * @param {Roo.data.Store} store The data store to bind
33896      */
33897     bind : function(ds){
33898         ds.on("beforeload", this.beforeLoad, this);
33899         ds.on("load", this.onLoad, this);
33900         ds.on("loadexception", this.onLoadError, this);
33901         ds.on("remove", this.updateInfo, this);
33902         ds.on("add", this.updateInfo, this);
33903         this.ds = ds;
33904     }
33905 });/*
33906  * - LGPL
33907  *
33908  * element
33909  * 
33910  */
33911
33912 /**
33913  * @class Roo.bootstrap.MessageBar
33914  * @extends Roo.bootstrap.Component
33915  * Bootstrap MessageBar class
33916  * @cfg {String} html contents of the MessageBar
33917  * @cfg {String} weight (info | success | warning | danger) default info
33918  * @cfg {String} beforeClass insert the bar before the given class
33919  * @cfg {Boolean} closable (true | false) default false
33920  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33921  * 
33922  * @constructor
33923  * Create a new Element
33924  * @param {Object} config The config object
33925  */
33926
33927 Roo.bootstrap.MessageBar = function(config){
33928     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33929 };
33930
33931 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33932     
33933     html: '',
33934     weight: 'info',
33935     closable: false,
33936     fixed: false,
33937     beforeClass: 'bootstrap-sticky-wrap',
33938     
33939     getAutoCreate : function(){
33940         
33941         var cfg = {
33942             tag: 'div',
33943             cls: 'alert alert-dismissable alert-' + this.weight,
33944             cn: [
33945                 {
33946                     tag: 'span',
33947                     cls: 'message',
33948                     html: this.html || ''
33949                 }
33950             ]
33951         };
33952         
33953         if(this.fixed){
33954             cfg.cls += ' alert-messages-fixed';
33955         }
33956         
33957         if(this.closable){
33958             cfg.cn.push({
33959                 tag: 'button',
33960                 cls: 'close',
33961                 html: 'x'
33962             });
33963         }
33964         
33965         return cfg;
33966     },
33967     
33968     onRender : function(ct, position)
33969     {
33970         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33971         
33972         if(!this.el){
33973             var cfg = Roo.apply({},  this.getAutoCreate());
33974             cfg.id = Roo.id();
33975             
33976             if (this.cls) {
33977                 cfg.cls += ' ' + this.cls;
33978             }
33979             if (this.style) {
33980                 cfg.style = this.style;
33981             }
33982             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33983             
33984             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33985         }
33986         
33987         this.el.select('>button.close').on('click', this.hide, this);
33988         
33989     },
33990     
33991     show : function()
33992     {
33993         if (!this.rendered) {
33994             this.render();
33995         }
33996         
33997         this.el.show();
33998         
33999         this.fireEvent('show', this);
34000         
34001     },
34002     
34003     hide : function()
34004     {
34005         if (!this.rendered) {
34006             this.render();
34007         }
34008         
34009         this.el.hide();
34010         
34011         this.fireEvent('hide', this);
34012     },
34013     
34014     update : function()
34015     {
34016 //        var e = this.el.dom.firstChild;
34017 //        
34018 //        if(this.closable){
34019 //            e = e.nextSibling;
34020 //        }
34021 //        
34022 //        e.data = this.html || '';
34023
34024         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34025     }
34026    
34027 });
34028
34029  
34030
34031      /*
34032  * - LGPL
34033  *
34034  * Graph
34035  * 
34036  */
34037
34038
34039 /**
34040  * @class Roo.bootstrap.Graph
34041  * @extends Roo.bootstrap.Component
34042  * Bootstrap Graph class
34043 > Prameters
34044  -sm {number} sm 4
34045  -md {number} md 5
34046  @cfg {String} graphtype  bar | vbar | pie
34047  @cfg {number} g_x coodinator | centre x (pie)
34048  @cfg {number} g_y coodinator | centre y (pie)
34049  @cfg {number} g_r radius (pie)
34050  @cfg {number} g_height height of the chart (respected by all elements in the set)
34051  @cfg {number} g_width width of the chart (respected by all elements in the set)
34052  @cfg {Object} title The title of the chart
34053     
34054  -{Array}  values
34055  -opts (object) options for the chart 
34056      o {
34057      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34058      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34059      o vgutter (number)
34060      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.
34061      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34062      o to
34063      o stretch (boolean)
34064      o }
34065  -opts (object) options for the pie
34066      o{
34067      o cut
34068      o startAngle (number)
34069      o endAngle (number)
34070      } 
34071  *
34072  * @constructor
34073  * Create a new Input
34074  * @param {Object} config The config object
34075  */
34076
34077 Roo.bootstrap.Graph = function(config){
34078     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34079     
34080     this.addEvents({
34081         // img events
34082         /**
34083          * @event click
34084          * The img click event for the img.
34085          * @param {Roo.EventObject} e
34086          */
34087         "click" : true
34088     });
34089 };
34090
34091 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34092     
34093     sm: 4,
34094     md: 5,
34095     graphtype: 'bar',
34096     g_height: 250,
34097     g_width: 400,
34098     g_x: 50,
34099     g_y: 50,
34100     g_r: 30,
34101     opts:{
34102         //g_colors: this.colors,
34103         g_type: 'soft',
34104         g_gutter: '20%'
34105
34106     },
34107     title : false,
34108
34109     getAutoCreate : function(){
34110         
34111         var cfg = {
34112             tag: 'div',
34113             html : null
34114         };
34115         
34116         
34117         return  cfg;
34118     },
34119
34120     onRender : function(ct,position){
34121         
34122         
34123         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34124         
34125         if (typeof(Raphael) == 'undefined') {
34126             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34127             return;
34128         }
34129         
34130         this.raphael = Raphael(this.el.dom);
34131         
34132                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34133                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34134                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34135                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34136                 /*
34137                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34138                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34139                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34140                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34141                 
34142                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34143                 r.barchart(330, 10, 300, 220, data1);
34144                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34145                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34146                 */
34147                 
34148                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34149                 // r.barchart(30, 30, 560, 250,  xdata, {
34150                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34151                 //     axis : "0 0 1 1",
34152                 //     axisxlabels :  xdata
34153                 //     //yvalues : cols,
34154                    
34155                 // });
34156 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34157 //        
34158 //        this.load(null,xdata,{
34159 //                axis : "0 0 1 1",
34160 //                axisxlabels :  xdata
34161 //                });
34162
34163     },
34164
34165     load : function(graphtype,xdata,opts)
34166     {
34167         this.raphael.clear();
34168         if(!graphtype) {
34169             graphtype = this.graphtype;
34170         }
34171         if(!opts){
34172             opts = this.opts;
34173         }
34174         var r = this.raphael,
34175             fin = function () {
34176                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34177             },
34178             fout = function () {
34179                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34180             },
34181             pfin = function() {
34182                 this.sector.stop();
34183                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34184
34185                 if (this.label) {
34186                     this.label[0].stop();
34187                     this.label[0].attr({ r: 7.5 });
34188                     this.label[1].attr({ "font-weight": 800 });
34189                 }
34190             },
34191             pfout = function() {
34192                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34193
34194                 if (this.label) {
34195                     this.label[0].animate({ r: 5 }, 500, "bounce");
34196                     this.label[1].attr({ "font-weight": 400 });
34197                 }
34198             };
34199
34200         switch(graphtype){
34201             case 'bar':
34202                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34203                 break;
34204             case 'hbar':
34205                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34206                 break;
34207             case 'pie':
34208 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34209 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34210 //            
34211                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34212                 
34213                 break;
34214
34215         }
34216         
34217         if(this.title){
34218             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34219         }
34220         
34221     },
34222     
34223     setTitle: function(o)
34224     {
34225         this.title = o;
34226     },
34227     
34228     initEvents: function() {
34229         
34230         if(!this.href){
34231             this.el.on('click', this.onClick, this);
34232         }
34233     },
34234     
34235     onClick : function(e)
34236     {
34237         Roo.log('img onclick');
34238         this.fireEvent('click', this, e);
34239     }
34240    
34241 });
34242
34243  
34244 Roo.bootstrap.dash = {};/*
34245  * - LGPL
34246  *
34247  * numberBox
34248  * 
34249  */
34250 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34251
34252 /**
34253  * @class Roo.bootstrap.dash.NumberBox
34254  * @extends Roo.bootstrap.Component
34255  * Bootstrap NumberBox class
34256  * @cfg {String} headline Box headline
34257  * @cfg {String} content Box content
34258  * @cfg {String} icon Box icon
34259  * @cfg {String} footer Footer text
34260  * @cfg {String} fhref Footer href
34261  * 
34262  * @constructor
34263  * Create a new NumberBox
34264  * @param {Object} config The config object
34265  */
34266
34267
34268 Roo.bootstrap.dash.NumberBox = function(config){
34269     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34270     
34271 };
34272
34273 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34274     
34275     headline : '',
34276     content : '',
34277     icon : '',
34278     footer : '',
34279     fhref : '',
34280     ficon : '',
34281     
34282     getAutoCreate : function(){
34283         
34284         var cfg = {
34285             tag : 'div',
34286             cls : 'small-box ',
34287             cn : [
34288                 {
34289                     tag : 'div',
34290                     cls : 'inner',
34291                     cn :[
34292                         {
34293                             tag : 'h3',
34294                             cls : 'roo-headline',
34295                             html : this.headline
34296                         },
34297                         {
34298                             tag : 'p',
34299                             cls : 'roo-content',
34300                             html : this.content
34301                         }
34302                     ]
34303                 }
34304             ]
34305         };
34306         
34307         if(this.icon){
34308             cfg.cn.push({
34309                 tag : 'div',
34310                 cls : 'icon',
34311                 cn :[
34312                     {
34313                         tag : 'i',
34314                         cls : 'ion ' + this.icon
34315                     }
34316                 ]
34317             });
34318         }
34319         
34320         if(this.footer){
34321             var footer = {
34322                 tag : 'a',
34323                 cls : 'small-box-footer',
34324                 href : this.fhref || '#',
34325                 html : this.footer
34326             };
34327             
34328             cfg.cn.push(footer);
34329             
34330         }
34331         
34332         return  cfg;
34333     },
34334
34335     onRender : function(ct,position){
34336         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34337
34338
34339        
34340                 
34341     },
34342
34343     setHeadline: function (value)
34344     {
34345         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34346     },
34347     
34348     setFooter: function (value, href)
34349     {
34350         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34351         
34352         if(href){
34353             this.el.select('a.small-box-footer',true).first().attr('href', href);
34354         }
34355         
34356     },
34357
34358     setContent: function (value)
34359     {
34360         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34361     },
34362
34363     initEvents: function() 
34364     {   
34365         
34366     }
34367     
34368 });
34369
34370  
34371 /*
34372  * - LGPL
34373  *
34374  * TabBox
34375  * 
34376  */
34377 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34378
34379 /**
34380  * @class Roo.bootstrap.dash.TabBox
34381  * @extends Roo.bootstrap.Component
34382  * @children Roo.bootstrap.dash.TabPane
34383  * Bootstrap TabBox class
34384  * @cfg {String} title Title of the TabBox
34385  * @cfg {String} icon Icon of the TabBox
34386  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34387  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34388  * 
34389  * @constructor
34390  * Create a new TabBox
34391  * @param {Object} config The config object
34392  */
34393
34394
34395 Roo.bootstrap.dash.TabBox = function(config){
34396     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34397     this.addEvents({
34398         // raw events
34399         /**
34400          * @event addpane
34401          * When a pane is added
34402          * @param {Roo.bootstrap.dash.TabPane} pane
34403          */
34404         "addpane" : true,
34405         /**
34406          * @event activatepane
34407          * When a pane is activated
34408          * @param {Roo.bootstrap.dash.TabPane} pane
34409          */
34410         "activatepane" : true
34411         
34412          
34413     });
34414     
34415     this.panes = [];
34416 };
34417
34418 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34419
34420     title : '',
34421     icon : false,
34422     showtabs : true,
34423     tabScrollable : false,
34424     
34425     getChildContainer : function()
34426     {
34427         return this.el.select('.tab-content', true).first();
34428     },
34429     
34430     getAutoCreate : function(){
34431         
34432         var header = {
34433             tag: 'li',
34434             cls: 'pull-left header',
34435             html: this.title,
34436             cn : []
34437         };
34438         
34439         if(this.icon){
34440             header.cn.push({
34441                 tag: 'i',
34442                 cls: 'fa ' + this.icon
34443             });
34444         }
34445         
34446         var h = {
34447             tag: 'ul',
34448             cls: 'nav nav-tabs pull-right',
34449             cn: [
34450                 header
34451             ]
34452         };
34453         
34454         if(this.tabScrollable){
34455             h = {
34456                 tag: 'div',
34457                 cls: 'tab-header',
34458                 cn: [
34459                     {
34460                         tag: 'ul',
34461                         cls: 'nav nav-tabs pull-right',
34462                         cn: [
34463                             header
34464                         ]
34465                     }
34466                 ]
34467             };
34468         }
34469         
34470         var cfg = {
34471             tag: 'div',
34472             cls: 'nav-tabs-custom',
34473             cn: [
34474                 h,
34475                 {
34476                     tag: 'div',
34477                     cls: 'tab-content no-padding',
34478                     cn: []
34479                 }
34480             ]
34481         };
34482
34483         return  cfg;
34484     },
34485     initEvents : function()
34486     {
34487         //Roo.log('add add pane handler');
34488         this.on('addpane', this.onAddPane, this);
34489     },
34490      /**
34491      * Updates the box title
34492      * @param {String} html to set the title to.
34493      */
34494     setTitle : function(value)
34495     {
34496         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34497     },
34498     onAddPane : function(pane)
34499     {
34500         this.panes.push(pane);
34501         //Roo.log('addpane');
34502         //Roo.log(pane);
34503         // tabs are rendere left to right..
34504         if(!this.showtabs){
34505             return;
34506         }
34507         
34508         var ctr = this.el.select('.nav-tabs', true).first();
34509          
34510          
34511         var existing = ctr.select('.nav-tab',true);
34512         var qty = existing.getCount();;
34513         
34514         
34515         var tab = ctr.createChild({
34516             tag : 'li',
34517             cls : 'nav-tab' + (qty ? '' : ' active'),
34518             cn : [
34519                 {
34520                     tag : 'a',
34521                     href:'#',
34522                     html : pane.title
34523                 }
34524             ]
34525         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34526         pane.tab = tab;
34527         
34528         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34529         if (!qty) {
34530             pane.el.addClass('active');
34531         }
34532         
34533                 
34534     },
34535     onTabClick : function(ev,un,ob,pane)
34536     {
34537         //Roo.log('tab - prev default');
34538         ev.preventDefault();
34539         
34540         
34541         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34542         pane.tab.addClass('active');
34543         //Roo.log(pane.title);
34544         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34545         // technically we should have a deactivate event.. but maybe add later.
34546         // and it should not de-activate the selected tab...
34547         this.fireEvent('activatepane', pane);
34548         pane.el.addClass('active');
34549         pane.fireEvent('activate');
34550         
34551         
34552     },
34553     
34554     getActivePane : function()
34555     {
34556         var r = false;
34557         Roo.each(this.panes, function(p) {
34558             if(p.el.hasClass('active')){
34559                 r = p;
34560                 return false;
34561             }
34562             
34563             return;
34564         });
34565         
34566         return r;
34567     }
34568     
34569     
34570 });
34571
34572  
34573 /*
34574  * - LGPL
34575  *
34576  * Tab pane
34577  * 
34578  */
34579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34580 /**
34581  * @class Roo.bootstrap.TabPane
34582  * @extends Roo.bootstrap.Component
34583  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34584  * Bootstrap TabPane class
34585  * @cfg {Boolean} active (false | true) Default false
34586  * @cfg {String} title title of panel
34587
34588  * 
34589  * @constructor
34590  * Create a new TabPane
34591  * @param {Object} config The config object
34592  */
34593
34594 Roo.bootstrap.dash.TabPane = function(config){
34595     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34596     
34597     this.addEvents({
34598         // raw events
34599         /**
34600          * @event activate
34601          * When a pane is activated
34602          * @param {Roo.bootstrap.dash.TabPane} pane
34603          */
34604         "activate" : true
34605          
34606     });
34607 };
34608
34609 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34610     
34611     active : false,
34612     title : '',
34613     
34614     // the tabBox that this is attached to.
34615     tab : false,
34616      
34617     getAutoCreate : function() 
34618     {
34619         var cfg = {
34620             tag: 'div',
34621             cls: 'tab-pane'
34622         };
34623         
34624         if(this.active){
34625             cfg.cls += ' active';
34626         }
34627         
34628         return cfg;
34629     },
34630     initEvents  : function()
34631     {
34632         //Roo.log('trigger add pane handler');
34633         this.parent().fireEvent('addpane', this)
34634     },
34635     
34636      /**
34637      * Updates the tab title 
34638      * @param {String} html to set the title to.
34639      */
34640     setTitle: function(str)
34641     {
34642         if (!this.tab) {
34643             return;
34644         }
34645         this.title = str;
34646         this.tab.select('a', true).first().dom.innerHTML = str;
34647         
34648     }
34649     
34650     
34651     
34652 });
34653
34654  
34655
34656
34657  /*
34658  * - LGPL
34659  *
34660  * Tooltip
34661  * 
34662  */
34663
34664 /**
34665  * @class Roo.bootstrap.Tooltip
34666  * Bootstrap Tooltip class
34667  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34668  * to determine which dom element triggers the tooltip.
34669  * 
34670  * It needs to add support for additional attributes like tooltip-position
34671  * 
34672  * @constructor
34673  * Create a new Toolti
34674  * @param {Object} config The config object
34675  */
34676
34677 Roo.bootstrap.Tooltip = function(config){
34678     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34679     
34680     this.alignment = Roo.bootstrap.Tooltip.alignment;
34681     
34682     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34683         this.alignment = config.alignment;
34684     }
34685     
34686 };
34687
34688 Roo.apply(Roo.bootstrap.Tooltip, {
34689     /**
34690      * @function init initialize tooltip monitoring.
34691      * @static
34692      */
34693     currentEl : false,
34694     currentTip : false,
34695     currentRegion : false,
34696     
34697     //  init : delay?
34698     
34699     init : function()
34700     {
34701         Roo.get(document).on('mouseover', this.enter ,this);
34702         Roo.get(document).on('mouseout', this.leave, this);
34703          
34704         
34705         this.currentTip = new Roo.bootstrap.Tooltip();
34706     },
34707     
34708     enter : function(ev)
34709     {
34710         var dom = ev.getTarget();
34711         
34712         //Roo.log(['enter',dom]);
34713         var el = Roo.fly(dom);
34714         if (this.currentEl) {
34715             //Roo.log(dom);
34716             //Roo.log(this.currentEl);
34717             //Roo.log(this.currentEl.contains(dom));
34718             if (this.currentEl == el) {
34719                 return;
34720             }
34721             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34722                 return;
34723             }
34724
34725         }
34726         
34727         if (this.currentTip.el) {
34728             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34729         }    
34730         //Roo.log(ev);
34731         
34732         if(!el || el.dom == document){
34733             return;
34734         }
34735         
34736         var bindEl = el; 
34737         var pel = false;
34738         if (!el.attr('tooltip')) {
34739             pel = el.findParent("[tooltip]");
34740             if (pel) {
34741                 bindEl = Roo.get(pel);
34742             }
34743         }
34744         
34745        
34746         
34747         // you can not look for children, as if el is the body.. then everythign is the child..
34748         if (!pel && !el.attr('tooltip')) { //
34749             if (!el.select("[tooltip]").elements.length) {
34750                 return;
34751             }
34752             // is the mouse over this child...?
34753             bindEl = el.select("[tooltip]").first();
34754             var xy = ev.getXY();
34755             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34756                 //Roo.log("not in region.");
34757                 return;
34758             }
34759             //Roo.log("child element over..");
34760             
34761         }
34762         this.currentEl = el;
34763         this.currentTip.bind(bindEl);
34764         this.currentRegion = Roo.lib.Region.getRegion(dom);
34765         this.currentTip.enter();
34766         
34767     },
34768     leave : function(ev)
34769     {
34770         var dom = ev.getTarget();
34771         //Roo.log(['leave',dom]);
34772         if (!this.currentEl) {
34773             return;
34774         }
34775         
34776         
34777         if (dom != this.currentEl.dom) {
34778             return;
34779         }
34780         var xy = ev.getXY();
34781         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34782             return;
34783         }
34784         // only activate leave if mouse cursor is outside... bounding box..
34785         
34786         
34787         
34788         
34789         if (this.currentTip) {
34790             this.currentTip.leave();
34791         }
34792         //Roo.log('clear currentEl');
34793         this.currentEl = false;
34794         
34795         
34796     },
34797     alignment : {
34798         'left' : ['r-l', [-2,0], 'right'],
34799         'right' : ['l-r', [2,0], 'left'],
34800         'bottom' : ['t-b', [0,2], 'top'],
34801         'top' : [ 'b-t', [0,-2], 'bottom']
34802     }
34803     
34804 });
34805
34806
34807 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34808     
34809     
34810     bindEl : false,
34811     
34812     delay : null, // can be { show : 300 , hide: 500}
34813     
34814     timeout : null,
34815     
34816     hoverState : null, //???
34817     
34818     placement : 'bottom', 
34819     
34820     alignment : false,
34821     
34822     getAutoCreate : function(){
34823     
34824         var cfg = {
34825            cls : 'tooltip',   
34826            role : 'tooltip',
34827            cn : [
34828                 {
34829                     cls : 'tooltip-arrow arrow'
34830                 },
34831                 {
34832                     cls : 'tooltip-inner'
34833                 }
34834            ]
34835         };
34836         
34837         return cfg;
34838     },
34839     bind : function(el)
34840     {
34841         this.bindEl = el;
34842     },
34843     
34844     initEvents : function()
34845     {
34846         this.arrowEl = this.el.select('.arrow', true).first();
34847         this.innerEl = this.el.select('.tooltip-inner', true).first();
34848     },
34849     
34850     enter : function () {
34851        
34852         if (this.timeout != null) {
34853             clearTimeout(this.timeout);
34854         }
34855         
34856         this.hoverState = 'in';
34857          //Roo.log("enter - show");
34858         if (!this.delay || !this.delay.show) {
34859             this.show();
34860             return;
34861         }
34862         var _t = this;
34863         this.timeout = setTimeout(function () {
34864             if (_t.hoverState == 'in') {
34865                 _t.show();
34866             }
34867         }, this.delay.show);
34868     },
34869     leave : function()
34870     {
34871         clearTimeout(this.timeout);
34872     
34873         this.hoverState = 'out';
34874          if (!this.delay || !this.delay.hide) {
34875             this.hide();
34876             return;
34877         }
34878        
34879         var _t = this;
34880         this.timeout = setTimeout(function () {
34881             //Roo.log("leave - timeout");
34882             
34883             if (_t.hoverState == 'out') {
34884                 _t.hide();
34885                 Roo.bootstrap.Tooltip.currentEl = false;
34886             }
34887         }, delay);
34888     },
34889     
34890     show : function (msg)
34891     {
34892         if (!this.el) {
34893             this.render(document.body);
34894         }
34895         // set content.
34896         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34897         
34898         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34899         
34900         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34901         
34902         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34903                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34904
34905         if(this.bindEl.attr('tooltip-class')) {
34906             this.el.addClass(this.bindEl.attr('tooltip-class'));
34907         }
34908         
34909         var placement = typeof this.placement == 'function' ?
34910             this.placement.call(this, this.el, on_el) :
34911             this.placement;
34912         
34913         if(this.bindEl.attr('tooltip-placement')) {
34914             placement = this.bindEl.attr('tooltip-placement');
34915         }
34916             
34917         var autoToken = /\s?auto?\s?/i;
34918         var autoPlace = autoToken.test(placement);
34919         if (autoPlace) {
34920             placement = placement.replace(autoToken, '') || 'top';
34921         }
34922         
34923         //this.el.detach()
34924         //this.el.setXY([0,0]);
34925         this.el.show();
34926         //this.el.dom.style.display='block';
34927         
34928         //this.el.appendTo(on_el);
34929         
34930         var p = this.getPosition();
34931         var box = this.el.getBox();
34932         
34933         if (autoPlace) {
34934             // fixme..
34935         }
34936         
34937         var align = this.alignment[placement];
34938         
34939         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34940         
34941         if(placement == 'top' || placement == 'bottom'){
34942             if(xy[0] < 0){
34943                 placement = 'right';
34944             }
34945             
34946             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34947                 placement = 'left';
34948             }
34949             
34950             var scroll = Roo.select('body', true).first().getScroll();
34951             
34952             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34953                 placement = 'top';
34954             }
34955             
34956             align = this.alignment[placement];
34957             
34958             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34959             
34960         }
34961         
34962         var elems = document.getElementsByTagName('div');
34963         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34964         for (var i = 0; i < elems.length; i++) {
34965           var zindex = Number.parseInt(
34966                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34967                 10
34968           );
34969           if (zindex > highest) {
34970             highest = zindex;
34971           }
34972         }
34973         
34974         
34975         
34976         this.el.dom.style.zIndex = highest;
34977         
34978         this.el.alignTo(this.bindEl, align[0],align[1]);
34979         //var arrow = this.el.select('.arrow',true).first();
34980         //arrow.set(align[2], 
34981         
34982         this.el.addClass(placement);
34983         this.el.addClass("bs-tooltip-"+ placement);
34984         
34985         this.el.addClass('in fade show');
34986         
34987         this.hoverState = null;
34988         
34989         if (this.el.hasClass('fade')) {
34990             // fade it?
34991         }
34992         
34993         
34994         
34995         
34996         
34997     },
34998     hide : function()
34999     {
35000          
35001         if (!this.el) {
35002             return;
35003         }
35004         //this.el.setXY([0,0]);
35005         if(this.bindEl.attr('tooltip-class')) {
35006             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35007         }
35008         this.el.removeClass(['show', 'in']);
35009         //this.el.hide();
35010         
35011     }
35012     
35013 });
35014  
35015
35016  /*
35017  * - LGPL
35018  *
35019  * Location Picker
35020  * 
35021  */
35022
35023 /**
35024  * @class Roo.bootstrap.LocationPicker
35025  * @extends Roo.bootstrap.Component
35026  * Bootstrap LocationPicker class
35027  * @cfg {Number} latitude Position when init default 0
35028  * @cfg {Number} longitude Position when init default 0
35029  * @cfg {Number} zoom default 15
35030  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35031  * @cfg {Boolean} mapTypeControl default false
35032  * @cfg {Boolean} disableDoubleClickZoom default false
35033  * @cfg {Boolean} scrollwheel default true
35034  * @cfg {Boolean} streetViewControl default false
35035  * @cfg {Number} radius default 0
35036  * @cfg {String} locationName
35037  * @cfg {Boolean} draggable default true
35038  * @cfg {Boolean} enableAutocomplete default false
35039  * @cfg {Boolean} enableReverseGeocode default true
35040  * @cfg {String} markerTitle
35041  * 
35042  * @constructor
35043  * Create a new LocationPicker
35044  * @param {Object} config The config object
35045  */
35046
35047
35048 Roo.bootstrap.LocationPicker = function(config){
35049     
35050     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35051     
35052     this.addEvents({
35053         /**
35054          * @event initial
35055          * Fires when the picker initialized.
35056          * @param {Roo.bootstrap.LocationPicker} this
35057          * @param {Google Location} location
35058          */
35059         initial : true,
35060         /**
35061          * @event positionchanged
35062          * Fires when the picker position changed.
35063          * @param {Roo.bootstrap.LocationPicker} this
35064          * @param {Google Location} location
35065          */
35066         positionchanged : true,
35067         /**
35068          * @event resize
35069          * Fires when the map resize.
35070          * @param {Roo.bootstrap.LocationPicker} this
35071          */
35072         resize : true,
35073         /**
35074          * @event show
35075          * Fires when the map show.
35076          * @param {Roo.bootstrap.LocationPicker} this
35077          */
35078         show : true,
35079         /**
35080          * @event hide
35081          * Fires when the map hide.
35082          * @param {Roo.bootstrap.LocationPicker} this
35083          */
35084         hide : true,
35085         /**
35086          * @event mapClick
35087          * Fires when click the map.
35088          * @param {Roo.bootstrap.LocationPicker} this
35089          * @param {Map event} e
35090          */
35091         mapClick : true,
35092         /**
35093          * @event mapRightClick
35094          * Fires when right click the map.
35095          * @param {Roo.bootstrap.LocationPicker} this
35096          * @param {Map event} e
35097          */
35098         mapRightClick : true,
35099         /**
35100          * @event markerClick
35101          * Fires when click the marker.
35102          * @param {Roo.bootstrap.LocationPicker} this
35103          * @param {Map event} e
35104          */
35105         markerClick : true,
35106         /**
35107          * @event markerRightClick
35108          * Fires when right click the marker.
35109          * @param {Roo.bootstrap.LocationPicker} this
35110          * @param {Map event} e
35111          */
35112         markerRightClick : true,
35113         /**
35114          * @event OverlayViewDraw
35115          * Fires when OverlayView Draw
35116          * @param {Roo.bootstrap.LocationPicker} this
35117          */
35118         OverlayViewDraw : true,
35119         /**
35120          * @event OverlayViewOnAdd
35121          * Fires when OverlayView Draw
35122          * @param {Roo.bootstrap.LocationPicker} this
35123          */
35124         OverlayViewOnAdd : true,
35125         /**
35126          * @event OverlayViewOnRemove
35127          * Fires when OverlayView Draw
35128          * @param {Roo.bootstrap.LocationPicker} this
35129          */
35130         OverlayViewOnRemove : true,
35131         /**
35132          * @event OverlayViewShow
35133          * Fires when OverlayView Draw
35134          * @param {Roo.bootstrap.LocationPicker} this
35135          * @param {Pixel} cpx
35136          */
35137         OverlayViewShow : true,
35138         /**
35139          * @event OverlayViewHide
35140          * Fires when OverlayView Draw
35141          * @param {Roo.bootstrap.LocationPicker} this
35142          */
35143         OverlayViewHide : true,
35144         /**
35145          * @event loadexception
35146          * Fires when load google lib failed.
35147          * @param {Roo.bootstrap.LocationPicker} this
35148          */
35149         loadexception : true
35150     });
35151         
35152 };
35153
35154 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35155     
35156     gMapContext: false,
35157     
35158     latitude: 0,
35159     longitude: 0,
35160     zoom: 15,
35161     mapTypeId: false,
35162     mapTypeControl: false,
35163     disableDoubleClickZoom: false,
35164     scrollwheel: true,
35165     streetViewControl: false,
35166     radius: 0,
35167     locationName: '',
35168     draggable: true,
35169     enableAutocomplete: false,
35170     enableReverseGeocode: true,
35171     markerTitle: '',
35172     
35173     getAutoCreate: function()
35174     {
35175
35176         var cfg = {
35177             tag: 'div',
35178             cls: 'roo-location-picker'
35179         };
35180         
35181         return cfg
35182     },
35183     
35184     initEvents: function(ct, position)
35185     {       
35186         if(!this.el.getWidth() || this.isApplied()){
35187             return;
35188         }
35189         
35190         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35191         
35192         this.initial();
35193     },
35194     
35195     initial: function()
35196     {
35197         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35198             this.fireEvent('loadexception', this);
35199             return;
35200         }
35201         
35202         if(!this.mapTypeId){
35203             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35204         }
35205         
35206         this.gMapContext = this.GMapContext();
35207         
35208         this.initOverlayView();
35209         
35210         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35211         
35212         var _this = this;
35213                 
35214         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35215             _this.setPosition(_this.gMapContext.marker.position);
35216         });
35217         
35218         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35219             _this.fireEvent('mapClick', this, event);
35220             
35221         });
35222
35223         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35224             _this.fireEvent('mapRightClick', this, event);
35225             
35226         });
35227         
35228         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35229             _this.fireEvent('markerClick', this, event);
35230             
35231         });
35232
35233         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35234             _this.fireEvent('markerRightClick', this, event);
35235             
35236         });
35237         
35238         this.setPosition(this.gMapContext.location);
35239         
35240         this.fireEvent('initial', this, this.gMapContext.location);
35241     },
35242     
35243     initOverlayView: function()
35244     {
35245         var _this = this;
35246         
35247         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35248             
35249             draw: function()
35250             {
35251                 _this.fireEvent('OverlayViewDraw', _this);
35252             },
35253             
35254             onAdd: function()
35255             {
35256                 _this.fireEvent('OverlayViewOnAdd', _this);
35257             },
35258             
35259             onRemove: function()
35260             {
35261                 _this.fireEvent('OverlayViewOnRemove', _this);
35262             },
35263             
35264             show: function(cpx)
35265             {
35266                 _this.fireEvent('OverlayViewShow', _this, cpx);
35267             },
35268             
35269             hide: function()
35270             {
35271                 _this.fireEvent('OverlayViewHide', _this);
35272             }
35273             
35274         });
35275     },
35276     
35277     fromLatLngToContainerPixel: function(event)
35278     {
35279         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35280     },
35281     
35282     isApplied: function() 
35283     {
35284         return this.getGmapContext() == false ? false : true;
35285     },
35286     
35287     getGmapContext: function() 
35288     {
35289         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35290     },
35291     
35292     GMapContext: function() 
35293     {
35294         var position = new google.maps.LatLng(this.latitude, this.longitude);
35295         
35296         var _map = new google.maps.Map(this.el.dom, {
35297             center: position,
35298             zoom: this.zoom,
35299             mapTypeId: this.mapTypeId,
35300             mapTypeControl: this.mapTypeControl,
35301             disableDoubleClickZoom: this.disableDoubleClickZoom,
35302             scrollwheel: this.scrollwheel,
35303             streetViewControl: this.streetViewControl,
35304             locationName: this.locationName,
35305             draggable: this.draggable,
35306             enableAutocomplete: this.enableAutocomplete,
35307             enableReverseGeocode: this.enableReverseGeocode
35308         });
35309         
35310         var _marker = new google.maps.Marker({
35311             position: position,
35312             map: _map,
35313             title: this.markerTitle,
35314             draggable: this.draggable
35315         });
35316         
35317         return {
35318             map: _map,
35319             marker: _marker,
35320             circle: null,
35321             location: position,
35322             radius: this.radius,
35323             locationName: this.locationName,
35324             addressComponents: {
35325                 formatted_address: null,
35326                 addressLine1: null,
35327                 addressLine2: null,
35328                 streetName: null,
35329                 streetNumber: null,
35330                 city: null,
35331                 district: null,
35332                 state: null,
35333                 stateOrProvince: null
35334             },
35335             settings: this,
35336             domContainer: this.el.dom,
35337             geodecoder: new google.maps.Geocoder()
35338         };
35339     },
35340     
35341     drawCircle: function(center, radius, options) 
35342     {
35343         if (this.gMapContext.circle != null) {
35344             this.gMapContext.circle.setMap(null);
35345         }
35346         if (radius > 0) {
35347             radius *= 1;
35348             options = Roo.apply({}, options, {
35349                 strokeColor: "#0000FF",
35350                 strokeOpacity: .35,
35351                 strokeWeight: 2,
35352                 fillColor: "#0000FF",
35353                 fillOpacity: .2
35354             });
35355             
35356             options.map = this.gMapContext.map;
35357             options.radius = radius;
35358             options.center = center;
35359             this.gMapContext.circle = new google.maps.Circle(options);
35360             return this.gMapContext.circle;
35361         }
35362         
35363         return null;
35364     },
35365     
35366     setPosition: function(location) 
35367     {
35368         this.gMapContext.location = location;
35369         this.gMapContext.marker.setPosition(location);
35370         this.gMapContext.map.panTo(location);
35371         this.drawCircle(location, this.gMapContext.radius, {});
35372         
35373         var _this = this;
35374         
35375         if (this.gMapContext.settings.enableReverseGeocode) {
35376             this.gMapContext.geodecoder.geocode({
35377                 latLng: this.gMapContext.location
35378             }, function(results, status) {
35379                 
35380                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35381                     _this.gMapContext.locationName = results[0].formatted_address;
35382                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35383                     
35384                     _this.fireEvent('positionchanged', this, location);
35385                 }
35386             });
35387             
35388             return;
35389         }
35390         
35391         this.fireEvent('positionchanged', this, location);
35392     },
35393     
35394     resize: function()
35395     {
35396         google.maps.event.trigger(this.gMapContext.map, "resize");
35397         
35398         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35399         
35400         this.fireEvent('resize', this);
35401     },
35402     
35403     setPositionByLatLng: function(latitude, longitude)
35404     {
35405         this.setPosition(new google.maps.LatLng(latitude, longitude));
35406     },
35407     
35408     getCurrentPosition: function() 
35409     {
35410         return {
35411             latitude: this.gMapContext.location.lat(),
35412             longitude: this.gMapContext.location.lng()
35413         };
35414     },
35415     
35416     getAddressName: function() 
35417     {
35418         return this.gMapContext.locationName;
35419     },
35420     
35421     getAddressComponents: function() 
35422     {
35423         return this.gMapContext.addressComponents;
35424     },
35425     
35426     address_component_from_google_geocode: function(address_components) 
35427     {
35428         var result = {};
35429         
35430         for (var i = 0; i < address_components.length; i++) {
35431             var component = address_components[i];
35432             if (component.types.indexOf("postal_code") >= 0) {
35433                 result.postalCode = component.short_name;
35434             } else if (component.types.indexOf("street_number") >= 0) {
35435                 result.streetNumber = component.short_name;
35436             } else if (component.types.indexOf("route") >= 0) {
35437                 result.streetName = component.short_name;
35438             } else if (component.types.indexOf("neighborhood") >= 0) {
35439                 result.city = component.short_name;
35440             } else if (component.types.indexOf("locality") >= 0) {
35441                 result.city = component.short_name;
35442             } else if (component.types.indexOf("sublocality") >= 0) {
35443                 result.district = component.short_name;
35444             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35445                 result.stateOrProvince = component.short_name;
35446             } else if (component.types.indexOf("country") >= 0) {
35447                 result.country = component.short_name;
35448             }
35449         }
35450         
35451         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35452         result.addressLine2 = "";
35453         return result;
35454     },
35455     
35456     setZoomLevel: function(zoom)
35457     {
35458         this.gMapContext.map.setZoom(zoom);
35459     },
35460     
35461     show: function()
35462     {
35463         if(!this.el){
35464             return;
35465         }
35466         
35467         this.el.show();
35468         
35469         this.resize();
35470         
35471         this.fireEvent('show', this);
35472     },
35473     
35474     hide: function()
35475     {
35476         if(!this.el){
35477             return;
35478         }
35479         
35480         this.el.hide();
35481         
35482         this.fireEvent('hide', this);
35483     }
35484     
35485 });
35486
35487 Roo.apply(Roo.bootstrap.LocationPicker, {
35488     
35489     OverlayView : function(map, options)
35490     {
35491         options = options || {};
35492         
35493         this.setMap(map);
35494     }
35495     
35496     
35497 });/**
35498  * @class Roo.bootstrap.Alert
35499  * @extends Roo.bootstrap.Component
35500  * Bootstrap Alert class - shows an alert area box
35501  * eg
35502  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35503   Enter a valid email address
35504 </div>
35505  * @licence LGPL
35506  * @cfg {String} title The title of alert
35507  * @cfg {String} html The content of alert
35508  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35509  * @cfg {String} fa font-awesomeicon
35510  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35511  * @cfg {Boolean} close true to show a x closer
35512  * 
35513  * 
35514  * @constructor
35515  * Create a new alert
35516  * @param {Object} config The config object
35517  */
35518
35519
35520 Roo.bootstrap.Alert = function(config){
35521     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35522     
35523 };
35524
35525 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35526     
35527     title: '',
35528     html: '',
35529     weight: false,
35530     fa: false,
35531     faicon: false, // BC
35532     close : false,
35533     
35534     
35535     getAutoCreate : function()
35536     {
35537         
35538         var cfg = {
35539             tag : 'div',
35540             cls : 'alert',
35541             cn : [
35542                 {
35543                     tag: 'button',
35544                     type :  "button",
35545                     cls: "close",
35546                     html : '×',
35547                     style : this.close ? '' : 'display:none'
35548                 },
35549                 {
35550                     tag : 'i',
35551                     cls : 'roo-alert-icon'
35552                     
35553                 },
35554                 {
35555                     tag : 'b',
35556                     cls : 'roo-alert-title',
35557                     html : this.title
35558                 },
35559                 {
35560                     tag : 'span',
35561                     cls : 'roo-alert-text',
35562                     html : this.html
35563                 }
35564             ]
35565         };
35566         
35567         if(this.faicon){
35568             cfg.cn[0].cls += ' fa ' + this.faicon;
35569         }
35570         if(this.fa){
35571             cfg.cn[0].cls += ' fa ' + this.fa;
35572         }
35573         
35574         if(this.weight){
35575             cfg.cls += ' alert-' + this.weight;
35576         }
35577         
35578         return cfg;
35579     },
35580     
35581     initEvents: function() 
35582     {
35583         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35584         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35585         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35586         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35587         if (this.seconds > 0) {
35588             this.hide.defer(this.seconds, this);
35589         }
35590     },
35591     /**
35592      * Set the Title Message HTML
35593      * @param {String} html
35594      */
35595     setTitle : function(str)
35596     {
35597         this.titleEl.dom.innerHTML = str;
35598     },
35599      
35600      /**
35601      * Set the Body Message HTML
35602      * @param {String} html
35603      */
35604     setHtml : function(str)
35605     {
35606         this.htmlEl.dom.innerHTML = str;
35607     },
35608     /**
35609      * Set the Weight of the alert
35610      * @param {String} (success|info|warning|danger) weight
35611      */
35612     
35613     setWeight : function(weight)
35614     {
35615         if(this.weight){
35616             this.el.removeClass('alert-' + this.weight);
35617         }
35618         
35619         this.weight = weight;
35620         
35621         this.el.addClass('alert-' + this.weight);
35622     },
35623       /**
35624      * Set the Icon of the alert
35625      * @param {String} see fontawsome names (name without the 'fa-' bit)
35626      */
35627     setIcon : function(icon)
35628     {
35629         if(this.faicon){
35630             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35631         }
35632         
35633         this.faicon = icon;
35634         
35635         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35636     },
35637     /**
35638      * Hide the Alert
35639      */
35640     hide: function() 
35641     {
35642         this.el.hide();   
35643     },
35644     /**
35645      * Show the Alert
35646      */
35647     show: function() 
35648     {  
35649         this.el.show();   
35650     }
35651     
35652 });
35653
35654  
35655 /*
35656 * Licence: LGPL
35657 */
35658
35659 /**
35660  * @class Roo.bootstrap.UploadCropbox
35661  * @extends Roo.bootstrap.Component
35662  * Bootstrap UploadCropbox class
35663  * @cfg {String} emptyText show when image has been loaded
35664  * @cfg {String} rotateNotify show when image too small to rotate
35665  * @cfg {Number} errorTimeout default 3000
35666  * @cfg {Number} minWidth default 300
35667  * @cfg {Number} minHeight default 300
35668  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35669  * @cfg {Boolean} isDocument (true|false) default false
35670  * @cfg {String} url action url
35671  * @cfg {String} paramName default 'imageUpload'
35672  * @cfg {String} method default POST
35673  * @cfg {Boolean} loadMask (true|false) default true
35674  * @cfg {Boolean} loadingText default 'Loading...'
35675  * 
35676  * @constructor
35677  * Create a new UploadCropbox
35678  * @param {Object} config The config object
35679  */
35680
35681 Roo.bootstrap.UploadCropbox = function(config){
35682     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35683     
35684     this.addEvents({
35685         /**
35686          * @event beforeselectfile
35687          * Fire before select file
35688          * @param {Roo.bootstrap.UploadCropbox} this
35689          */
35690         "beforeselectfile" : true,
35691         /**
35692          * @event initial
35693          * Fire after initEvent
35694          * @param {Roo.bootstrap.UploadCropbox} this
35695          */
35696         "initial" : true,
35697         /**
35698          * @event crop
35699          * Fire after initEvent
35700          * @param {Roo.bootstrap.UploadCropbox} this
35701          * @param {String} data
35702          */
35703         "crop" : true,
35704         /**
35705          * @event prepare
35706          * Fire when preparing the file data
35707          * @param {Roo.bootstrap.UploadCropbox} this
35708          * @param {Object} file
35709          */
35710         "prepare" : true,
35711         /**
35712          * @event exception
35713          * Fire when get exception
35714          * @param {Roo.bootstrap.UploadCropbox} this
35715          * @param {XMLHttpRequest} xhr
35716          */
35717         "exception" : true,
35718         /**
35719          * @event beforeloadcanvas
35720          * Fire before load the canvas
35721          * @param {Roo.bootstrap.UploadCropbox} this
35722          * @param {String} src
35723          */
35724         "beforeloadcanvas" : true,
35725         /**
35726          * @event trash
35727          * Fire when trash image
35728          * @param {Roo.bootstrap.UploadCropbox} this
35729          */
35730         "trash" : true,
35731         /**
35732          * @event download
35733          * Fire when download the image
35734          * @param {Roo.bootstrap.UploadCropbox} this
35735          */
35736         "download" : true,
35737         /**
35738          * @event footerbuttonclick
35739          * Fire when footerbuttonclick
35740          * @param {Roo.bootstrap.UploadCropbox} this
35741          * @param {String} type
35742          */
35743         "footerbuttonclick" : true,
35744         /**
35745          * @event resize
35746          * Fire when resize
35747          * @param {Roo.bootstrap.UploadCropbox} this
35748          */
35749         "resize" : true,
35750         /**
35751          * @event rotate
35752          * Fire when rotate the image
35753          * @param {Roo.bootstrap.UploadCropbox} this
35754          * @param {String} pos
35755          */
35756         "rotate" : true,
35757         /**
35758          * @event inspect
35759          * Fire when inspect the file
35760          * @param {Roo.bootstrap.UploadCropbox} this
35761          * @param {Object} file
35762          */
35763         "inspect" : true,
35764         /**
35765          * @event upload
35766          * Fire when xhr upload the file
35767          * @param {Roo.bootstrap.UploadCropbox} this
35768          * @param {Object} data
35769          */
35770         "upload" : true,
35771         /**
35772          * @event arrange
35773          * Fire when arrange the file data
35774          * @param {Roo.bootstrap.UploadCropbox} this
35775          * @param {Object} formData
35776          */
35777         "arrange" : true
35778     });
35779     
35780     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35781 };
35782
35783 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35784     
35785     emptyText : 'Click to upload image',
35786     rotateNotify : 'Image is too small to rotate',
35787     errorTimeout : 3000,
35788     scale : 0,
35789     baseScale : 1,
35790     rotate : 0,
35791     dragable : false,
35792     pinching : false,
35793     mouseX : 0,
35794     mouseY : 0,
35795     cropData : false,
35796     minWidth : 300,
35797     minHeight : 300,
35798     file : false,
35799     exif : {},
35800     baseRotate : 1,
35801     cropType : 'image/jpeg',
35802     buttons : false,
35803     canvasLoaded : false,
35804     isDocument : false,
35805     method : 'POST',
35806     paramName : 'imageUpload',
35807     loadMask : true,
35808     loadingText : 'Loading...',
35809     maskEl : false,
35810     
35811     getAutoCreate : function()
35812     {
35813         var cfg = {
35814             tag : 'div',
35815             cls : 'roo-upload-cropbox',
35816             cn : [
35817                 {
35818                     tag : 'input',
35819                     cls : 'roo-upload-cropbox-selector',
35820                     type : 'file'
35821                 },
35822                 {
35823                     tag : 'div',
35824                     cls : 'roo-upload-cropbox-body',
35825                     style : 'cursor:pointer',
35826                     cn : [
35827                         {
35828                             tag : 'div',
35829                             cls : 'roo-upload-cropbox-preview'
35830                         },
35831                         {
35832                             tag : 'div',
35833                             cls : 'roo-upload-cropbox-thumb'
35834                         },
35835                         {
35836                             tag : 'div',
35837                             cls : 'roo-upload-cropbox-empty-notify',
35838                             html : this.emptyText
35839                         },
35840                         {
35841                             tag : 'div',
35842                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35843                             html : this.rotateNotify
35844                         }
35845                     ]
35846                 },
35847                 {
35848                     tag : 'div',
35849                     cls : 'roo-upload-cropbox-footer',
35850                     cn : {
35851                         tag : 'div',
35852                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35853                         cn : []
35854                     }
35855                 }
35856             ]
35857         };
35858         
35859         return cfg;
35860     },
35861     
35862     onRender : function(ct, position)
35863     {
35864         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35865         
35866         if (this.buttons.length) {
35867             
35868             Roo.each(this.buttons, function(bb) {
35869                 
35870                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35871                 
35872                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35873                 
35874             }, this);
35875         }
35876         
35877         if(this.loadMask){
35878             this.maskEl = this.el;
35879         }
35880     },
35881     
35882     initEvents : function()
35883     {
35884         this.urlAPI = (window.createObjectURL && window) || 
35885                                 (window.URL && URL.revokeObjectURL && URL) || 
35886                                 (window.webkitURL && webkitURL);
35887                         
35888         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35889         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35890         
35891         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35892         this.selectorEl.hide();
35893         
35894         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35895         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35896         
35897         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35898         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35899         this.thumbEl.hide();
35900         
35901         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35902         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35903         
35904         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35905         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35906         this.errorEl.hide();
35907         
35908         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35909         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35910         this.footerEl.hide();
35911         
35912         this.setThumbBoxSize();
35913         
35914         this.bind();
35915         
35916         this.resize();
35917         
35918         this.fireEvent('initial', this);
35919     },
35920
35921     bind : function()
35922     {
35923         var _this = this;
35924         
35925         window.addEventListener("resize", function() { _this.resize(); } );
35926         
35927         this.bodyEl.on('click', this.beforeSelectFile, this);
35928         
35929         if(Roo.isTouch){
35930             this.bodyEl.on('touchstart', this.onTouchStart, this);
35931             this.bodyEl.on('touchmove', this.onTouchMove, this);
35932             this.bodyEl.on('touchend', this.onTouchEnd, this);
35933         }
35934         
35935         if(!Roo.isTouch){
35936             this.bodyEl.on('mousedown', this.onMouseDown, this);
35937             this.bodyEl.on('mousemove', this.onMouseMove, this);
35938             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35939             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35940             Roo.get(document).on('mouseup', this.onMouseUp, this);
35941         }
35942         
35943         this.selectorEl.on('change', this.onFileSelected, this);
35944     },
35945     
35946     reset : function()
35947     {    
35948         this.scale = 0;
35949         this.baseScale = 1;
35950         this.rotate = 0;
35951         this.baseRotate = 1;
35952         this.dragable = false;
35953         this.pinching = false;
35954         this.mouseX = 0;
35955         this.mouseY = 0;
35956         this.cropData = false;
35957         this.notifyEl.dom.innerHTML = this.emptyText;
35958         
35959         this.selectorEl.dom.value = '';
35960         
35961     },
35962     
35963     resize : function()
35964     {
35965         if(this.fireEvent('resize', this) != false){
35966             this.setThumbBoxPosition();
35967             this.setCanvasPosition();
35968         }
35969     },
35970     
35971     onFooterButtonClick : function(e, el, o, type)
35972     {
35973         switch (type) {
35974             case 'rotate-left' :
35975                 this.onRotateLeft(e);
35976                 break;
35977             case 'rotate-right' :
35978                 this.onRotateRight(e);
35979                 break;
35980             case 'picture' :
35981                 this.beforeSelectFile(e);
35982                 break;
35983             case 'trash' :
35984                 this.trash(e);
35985                 break;
35986             case 'crop' :
35987                 this.crop(e);
35988                 break;
35989             case 'download' :
35990                 this.download(e);
35991                 break;
35992             default :
35993                 break;
35994         }
35995         
35996         this.fireEvent('footerbuttonclick', this, type);
35997     },
35998     
35999     beforeSelectFile : function(e)
36000     {
36001         e.preventDefault();
36002         
36003         if(this.fireEvent('beforeselectfile', this) != false){
36004             this.selectorEl.dom.click();
36005         }
36006     },
36007     
36008     onFileSelected : function(e)
36009     {
36010         e.preventDefault();
36011         
36012         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36013             return;
36014         }
36015         
36016         var file = this.selectorEl.dom.files[0];
36017         
36018         if(this.fireEvent('inspect', this, file) != false){
36019             this.prepare(file);
36020         }
36021         
36022     },
36023     
36024     trash : function(e)
36025     {
36026         this.fireEvent('trash', this);
36027     },
36028     
36029     download : function(e)
36030     {
36031         this.fireEvent('download', this);
36032     },
36033     
36034     loadCanvas : function(src)
36035     {   
36036         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36037             
36038             this.reset();
36039             
36040             this.imageEl = document.createElement('img');
36041             
36042             var _this = this;
36043             
36044             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36045             
36046             this.imageEl.src = src;
36047         }
36048     },
36049     
36050     onLoadCanvas : function()
36051     {   
36052         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36053         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36054         
36055         this.bodyEl.un('click', this.beforeSelectFile, this);
36056         
36057         this.notifyEl.hide();
36058         this.thumbEl.show();
36059         this.footerEl.show();
36060         
36061         this.baseRotateLevel();
36062         
36063         if(this.isDocument){
36064             this.setThumbBoxSize();
36065         }
36066         
36067         this.setThumbBoxPosition();
36068         
36069         this.baseScaleLevel();
36070         
36071         this.draw();
36072         
36073         this.resize();
36074         
36075         this.canvasLoaded = true;
36076         
36077         if(this.loadMask){
36078             this.maskEl.unmask();
36079         }
36080         
36081     },
36082     
36083     setCanvasPosition : function()
36084     {   
36085         if(!this.canvasEl){
36086             return;
36087         }
36088         
36089         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36090         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36091         
36092         this.previewEl.setLeft(pw);
36093         this.previewEl.setTop(ph);
36094         
36095     },
36096     
36097     onMouseDown : function(e)
36098     {   
36099         e.stopEvent();
36100         
36101         this.dragable = true;
36102         this.pinching = false;
36103         
36104         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36105             this.dragable = false;
36106             return;
36107         }
36108         
36109         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36110         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36111         
36112     },
36113     
36114     onMouseMove : function(e)
36115     {   
36116         e.stopEvent();
36117         
36118         if(!this.canvasLoaded){
36119             return;
36120         }
36121         
36122         if (!this.dragable){
36123             return;
36124         }
36125         
36126         var minX = Math.ceil(this.thumbEl.getLeft(true));
36127         var minY = Math.ceil(this.thumbEl.getTop(true));
36128         
36129         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36130         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36131         
36132         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36133         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36134         
36135         x = x - this.mouseX;
36136         y = y - this.mouseY;
36137         
36138         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36139         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36140         
36141         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36142         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36143         
36144         this.previewEl.setLeft(bgX);
36145         this.previewEl.setTop(bgY);
36146         
36147         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36148         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36149     },
36150     
36151     onMouseUp : function(e)
36152     {   
36153         e.stopEvent();
36154         
36155         this.dragable = false;
36156     },
36157     
36158     onMouseWheel : function(e)
36159     {   
36160         e.stopEvent();
36161         
36162         this.startScale = this.scale;
36163         
36164         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36165         
36166         if(!this.zoomable()){
36167             this.scale = this.startScale;
36168             return;
36169         }
36170         
36171         this.draw();
36172         
36173         return;
36174     },
36175     
36176     zoomable : function()
36177     {
36178         var minScale = this.thumbEl.getWidth() / this.minWidth;
36179         
36180         if(this.minWidth < this.minHeight){
36181             minScale = this.thumbEl.getHeight() / this.minHeight;
36182         }
36183         
36184         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36185         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36186         
36187         if(
36188                 this.isDocument &&
36189                 (this.rotate == 0 || this.rotate == 180) && 
36190                 (
36191                     width > this.imageEl.OriginWidth || 
36192                     height > this.imageEl.OriginHeight ||
36193                     (width < this.minWidth && height < this.minHeight)
36194                 )
36195         ){
36196             return false;
36197         }
36198         
36199         if(
36200                 this.isDocument &&
36201                 (this.rotate == 90 || this.rotate == 270) && 
36202                 (
36203                     width > this.imageEl.OriginWidth || 
36204                     height > this.imageEl.OriginHeight ||
36205                     (width < this.minHeight && height < this.minWidth)
36206                 )
36207         ){
36208             return false;
36209         }
36210         
36211         if(
36212                 !this.isDocument &&
36213                 (this.rotate == 0 || this.rotate == 180) && 
36214                 (
36215                     width < this.minWidth || 
36216                     width > this.imageEl.OriginWidth || 
36217                     height < this.minHeight || 
36218                     height > this.imageEl.OriginHeight
36219                 )
36220         ){
36221             return false;
36222         }
36223         
36224         if(
36225                 !this.isDocument &&
36226                 (this.rotate == 90 || this.rotate == 270) && 
36227                 (
36228                     width < this.minHeight || 
36229                     width > this.imageEl.OriginWidth || 
36230                     height < this.minWidth || 
36231                     height > this.imageEl.OriginHeight
36232                 )
36233         ){
36234             return false;
36235         }
36236         
36237         return true;
36238         
36239     },
36240     
36241     onRotateLeft : function(e)
36242     {   
36243         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36244             
36245             var minScale = this.thumbEl.getWidth() / this.minWidth;
36246             
36247             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36248             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36249             
36250             this.startScale = this.scale;
36251             
36252             while (this.getScaleLevel() < minScale){
36253             
36254                 this.scale = this.scale + 1;
36255                 
36256                 if(!this.zoomable()){
36257                     break;
36258                 }
36259                 
36260                 if(
36261                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36262                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36263                 ){
36264                     continue;
36265                 }
36266                 
36267                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36268
36269                 this.draw();
36270                 
36271                 return;
36272             }
36273             
36274             this.scale = this.startScale;
36275             
36276             this.onRotateFail();
36277             
36278             return false;
36279         }
36280         
36281         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36282
36283         if(this.isDocument){
36284             this.setThumbBoxSize();
36285             this.setThumbBoxPosition();
36286             this.setCanvasPosition();
36287         }
36288         
36289         this.draw();
36290         
36291         this.fireEvent('rotate', this, 'left');
36292         
36293     },
36294     
36295     onRotateRight : function(e)
36296     {
36297         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36298             
36299             var minScale = this.thumbEl.getWidth() / this.minWidth;
36300         
36301             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36302             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36303             
36304             this.startScale = this.scale;
36305             
36306             while (this.getScaleLevel() < minScale){
36307             
36308                 this.scale = this.scale + 1;
36309                 
36310                 if(!this.zoomable()){
36311                     break;
36312                 }
36313                 
36314                 if(
36315                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36316                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36317                 ){
36318                     continue;
36319                 }
36320                 
36321                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36322
36323                 this.draw();
36324                 
36325                 return;
36326             }
36327             
36328             this.scale = this.startScale;
36329             
36330             this.onRotateFail();
36331             
36332             return false;
36333         }
36334         
36335         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36336
36337         if(this.isDocument){
36338             this.setThumbBoxSize();
36339             this.setThumbBoxPosition();
36340             this.setCanvasPosition();
36341         }
36342         
36343         this.draw();
36344         
36345         this.fireEvent('rotate', this, 'right');
36346     },
36347     
36348     onRotateFail : function()
36349     {
36350         this.errorEl.show(true);
36351         
36352         var _this = this;
36353         
36354         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36355     },
36356     
36357     draw : function()
36358     {
36359         this.previewEl.dom.innerHTML = '';
36360         
36361         var canvasEl = document.createElement("canvas");
36362         
36363         var contextEl = canvasEl.getContext("2d");
36364         
36365         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36366         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36367         var center = this.imageEl.OriginWidth / 2;
36368         
36369         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36370             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36371             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36372             center = this.imageEl.OriginHeight / 2;
36373         }
36374         
36375         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36376         
36377         contextEl.translate(center, center);
36378         contextEl.rotate(this.rotate * Math.PI / 180);
36379
36380         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36381         
36382         this.canvasEl = document.createElement("canvas");
36383         
36384         this.contextEl = this.canvasEl.getContext("2d");
36385         
36386         switch (this.rotate) {
36387             case 0 :
36388                 
36389                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36390                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36391                 
36392                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36393                 
36394                 break;
36395             case 90 : 
36396                 
36397                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36398                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36399                 
36400                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36401                     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);
36402                     break;
36403                 }
36404                 
36405                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36406                 
36407                 break;
36408             case 180 :
36409                 
36410                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36411                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36412                 
36413                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36414                     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);
36415                     break;
36416                 }
36417                 
36418                 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);
36419                 
36420                 break;
36421             case 270 :
36422                 
36423                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36424                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36425         
36426                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36427                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36428                     break;
36429                 }
36430                 
36431                 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);
36432                 
36433                 break;
36434             default : 
36435                 break;
36436         }
36437         
36438         this.previewEl.appendChild(this.canvasEl);
36439         
36440         this.setCanvasPosition();
36441     },
36442     
36443     crop : function()
36444     {
36445         if(!this.canvasLoaded){
36446             return;
36447         }
36448         
36449         var imageCanvas = document.createElement("canvas");
36450         
36451         var imageContext = imageCanvas.getContext("2d");
36452         
36453         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36454         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36455         
36456         var center = imageCanvas.width / 2;
36457         
36458         imageContext.translate(center, center);
36459         
36460         imageContext.rotate(this.rotate * Math.PI / 180);
36461         
36462         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36463         
36464         var canvas = document.createElement("canvas");
36465         
36466         var context = canvas.getContext("2d");
36467                 
36468         canvas.width = this.minWidth;
36469         canvas.height = this.minHeight;
36470
36471         switch (this.rotate) {
36472             case 0 :
36473                 
36474                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36475                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36476                 
36477                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36478                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36479                 
36480                 var targetWidth = this.minWidth - 2 * x;
36481                 var targetHeight = this.minHeight - 2 * y;
36482                 
36483                 var scale = 1;
36484                 
36485                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36486                     scale = targetWidth / width;
36487                 }
36488                 
36489                 if(x > 0 && y == 0){
36490                     scale = targetHeight / height;
36491                 }
36492                 
36493                 if(x > 0 && y > 0){
36494                     scale = targetWidth / width;
36495                     
36496                     if(width < height){
36497                         scale = targetHeight / height;
36498                     }
36499                 }
36500                 
36501                 context.scale(scale, scale);
36502                 
36503                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36504                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36505
36506                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36507                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36508
36509                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36510                 
36511                 break;
36512             case 90 : 
36513                 
36514                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36515                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36516                 
36517                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36518                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36519                 
36520                 var targetWidth = this.minWidth - 2 * x;
36521                 var targetHeight = this.minHeight - 2 * y;
36522                 
36523                 var scale = 1;
36524                 
36525                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36526                     scale = targetWidth / width;
36527                 }
36528                 
36529                 if(x > 0 && y == 0){
36530                     scale = targetHeight / height;
36531                 }
36532                 
36533                 if(x > 0 && y > 0){
36534                     scale = targetWidth / width;
36535                     
36536                     if(width < height){
36537                         scale = targetHeight / height;
36538                     }
36539                 }
36540                 
36541                 context.scale(scale, scale);
36542                 
36543                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36544                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36545
36546                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36547                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36548                 
36549                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36550                 
36551                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36552                 
36553                 break;
36554             case 180 :
36555                 
36556                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36557                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36558                 
36559                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36560                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36561                 
36562                 var targetWidth = this.minWidth - 2 * x;
36563                 var targetHeight = this.minHeight - 2 * y;
36564                 
36565                 var scale = 1;
36566                 
36567                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36568                     scale = targetWidth / width;
36569                 }
36570                 
36571                 if(x > 0 && y == 0){
36572                     scale = targetHeight / height;
36573                 }
36574                 
36575                 if(x > 0 && y > 0){
36576                     scale = targetWidth / width;
36577                     
36578                     if(width < height){
36579                         scale = targetHeight / height;
36580                     }
36581                 }
36582                 
36583                 context.scale(scale, scale);
36584                 
36585                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36586                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36587
36588                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36589                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36590
36591                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36592                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36593                 
36594                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36595                 
36596                 break;
36597             case 270 :
36598                 
36599                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36600                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36601                 
36602                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36603                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36604                 
36605                 var targetWidth = this.minWidth - 2 * x;
36606                 var targetHeight = this.minHeight - 2 * y;
36607                 
36608                 var scale = 1;
36609                 
36610                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36611                     scale = targetWidth / width;
36612                 }
36613                 
36614                 if(x > 0 && y == 0){
36615                     scale = targetHeight / height;
36616                 }
36617                 
36618                 if(x > 0 && y > 0){
36619                     scale = targetWidth / width;
36620                     
36621                     if(width < height){
36622                         scale = targetHeight / height;
36623                     }
36624                 }
36625                 
36626                 context.scale(scale, scale);
36627                 
36628                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36629                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36630
36631                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36632                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36633                 
36634                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36635                 
36636                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36637                 
36638                 break;
36639             default : 
36640                 break;
36641         }
36642         
36643         this.cropData = canvas.toDataURL(this.cropType);
36644         
36645         if(this.fireEvent('crop', this, this.cropData) !== false){
36646             this.process(this.file, this.cropData);
36647         }
36648         
36649         return;
36650         
36651     },
36652     
36653     setThumbBoxSize : function()
36654     {
36655         var width, height;
36656         
36657         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36658             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36659             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36660             
36661             this.minWidth = width;
36662             this.minHeight = height;
36663             
36664             if(this.rotate == 90 || this.rotate == 270){
36665                 this.minWidth = height;
36666                 this.minHeight = width;
36667             }
36668         }
36669         
36670         height = 300;
36671         width = Math.ceil(this.minWidth * height / this.minHeight);
36672         
36673         if(this.minWidth > this.minHeight){
36674             width = 300;
36675             height = Math.ceil(this.minHeight * width / this.minWidth);
36676         }
36677         
36678         this.thumbEl.setStyle({
36679             width : width + 'px',
36680             height : height + 'px'
36681         });
36682
36683         return;
36684             
36685     },
36686     
36687     setThumbBoxPosition : function()
36688     {
36689         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36690         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36691         
36692         this.thumbEl.setLeft(x);
36693         this.thumbEl.setTop(y);
36694         
36695     },
36696     
36697     baseRotateLevel : function()
36698     {
36699         this.baseRotate = 1;
36700         
36701         if(
36702                 typeof(this.exif) != 'undefined' &&
36703                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36704                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36705         ){
36706             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36707         }
36708         
36709         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36710         
36711     },
36712     
36713     baseScaleLevel : function()
36714     {
36715         var width, height;
36716         
36717         if(this.isDocument){
36718             
36719             if(this.baseRotate == 6 || this.baseRotate == 8){
36720             
36721                 height = this.thumbEl.getHeight();
36722                 this.baseScale = height / this.imageEl.OriginWidth;
36723
36724                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36725                     width = this.thumbEl.getWidth();
36726                     this.baseScale = width / this.imageEl.OriginHeight;
36727                 }
36728
36729                 return;
36730             }
36731
36732             height = this.thumbEl.getHeight();
36733             this.baseScale = height / this.imageEl.OriginHeight;
36734
36735             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36736                 width = this.thumbEl.getWidth();
36737                 this.baseScale = width / this.imageEl.OriginWidth;
36738             }
36739
36740             return;
36741         }
36742         
36743         if(this.baseRotate == 6 || this.baseRotate == 8){
36744             
36745             width = this.thumbEl.getHeight();
36746             this.baseScale = width / this.imageEl.OriginHeight;
36747             
36748             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36749                 height = this.thumbEl.getWidth();
36750                 this.baseScale = height / this.imageEl.OriginHeight;
36751             }
36752             
36753             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36754                 height = this.thumbEl.getWidth();
36755                 this.baseScale = height / this.imageEl.OriginHeight;
36756                 
36757                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36758                     width = this.thumbEl.getHeight();
36759                     this.baseScale = width / this.imageEl.OriginWidth;
36760                 }
36761             }
36762             
36763             return;
36764         }
36765         
36766         width = this.thumbEl.getWidth();
36767         this.baseScale = width / this.imageEl.OriginWidth;
36768         
36769         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36770             height = this.thumbEl.getHeight();
36771             this.baseScale = height / this.imageEl.OriginHeight;
36772         }
36773         
36774         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36775             
36776             height = this.thumbEl.getHeight();
36777             this.baseScale = height / this.imageEl.OriginHeight;
36778             
36779             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36780                 width = this.thumbEl.getWidth();
36781                 this.baseScale = width / this.imageEl.OriginWidth;
36782             }
36783             
36784         }
36785         
36786         return;
36787     },
36788     
36789     getScaleLevel : function()
36790     {
36791         return this.baseScale * Math.pow(1.1, this.scale);
36792     },
36793     
36794     onTouchStart : function(e)
36795     {
36796         if(!this.canvasLoaded){
36797             this.beforeSelectFile(e);
36798             return;
36799         }
36800         
36801         var touches = e.browserEvent.touches;
36802         
36803         if(!touches){
36804             return;
36805         }
36806         
36807         if(touches.length == 1){
36808             this.onMouseDown(e);
36809             return;
36810         }
36811         
36812         if(touches.length != 2){
36813             return;
36814         }
36815         
36816         var coords = [];
36817         
36818         for(var i = 0, finger; finger = touches[i]; i++){
36819             coords.push(finger.pageX, finger.pageY);
36820         }
36821         
36822         var x = Math.pow(coords[0] - coords[2], 2);
36823         var y = Math.pow(coords[1] - coords[3], 2);
36824         
36825         this.startDistance = Math.sqrt(x + y);
36826         
36827         this.startScale = this.scale;
36828         
36829         this.pinching = true;
36830         this.dragable = false;
36831         
36832     },
36833     
36834     onTouchMove : function(e)
36835     {
36836         if(!this.pinching && !this.dragable){
36837             return;
36838         }
36839         
36840         var touches = e.browserEvent.touches;
36841         
36842         if(!touches){
36843             return;
36844         }
36845         
36846         if(this.dragable){
36847             this.onMouseMove(e);
36848             return;
36849         }
36850         
36851         var coords = [];
36852         
36853         for(var i = 0, finger; finger = touches[i]; i++){
36854             coords.push(finger.pageX, finger.pageY);
36855         }
36856         
36857         var x = Math.pow(coords[0] - coords[2], 2);
36858         var y = Math.pow(coords[1] - coords[3], 2);
36859         
36860         this.endDistance = Math.sqrt(x + y);
36861         
36862         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36863         
36864         if(!this.zoomable()){
36865             this.scale = this.startScale;
36866             return;
36867         }
36868         
36869         this.draw();
36870         
36871     },
36872     
36873     onTouchEnd : function(e)
36874     {
36875         this.pinching = false;
36876         this.dragable = false;
36877         
36878     },
36879     
36880     process : function(file, crop)
36881     {
36882         if(this.loadMask){
36883             this.maskEl.mask(this.loadingText);
36884         }
36885         
36886         this.xhr = new XMLHttpRequest();
36887         
36888         file.xhr = this.xhr;
36889
36890         this.xhr.open(this.method, this.url, true);
36891         
36892         var headers = {
36893             "Accept": "application/json",
36894             "Cache-Control": "no-cache",
36895             "X-Requested-With": "XMLHttpRequest"
36896         };
36897         
36898         for (var headerName in headers) {
36899             var headerValue = headers[headerName];
36900             if (headerValue) {
36901                 this.xhr.setRequestHeader(headerName, headerValue);
36902             }
36903         }
36904         
36905         var _this = this;
36906         
36907         this.xhr.onload = function()
36908         {
36909             _this.xhrOnLoad(_this.xhr);
36910         }
36911         
36912         this.xhr.onerror = function()
36913         {
36914             _this.xhrOnError(_this.xhr);
36915         }
36916         
36917         var formData = new FormData();
36918
36919         formData.append('returnHTML', 'NO');
36920         
36921         if(crop){
36922             formData.append('crop', crop);
36923         }
36924         
36925         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36926             formData.append(this.paramName, file, file.name);
36927         }
36928         
36929         if(typeof(file.filename) != 'undefined'){
36930             formData.append('filename', file.filename);
36931         }
36932         
36933         if(typeof(file.mimetype) != 'undefined'){
36934             formData.append('mimetype', file.mimetype);
36935         }
36936         
36937         if(this.fireEvent('arrange', this, formData) != false){
36938             this.xhr.send(formData);
36939         };
36940     },
36941     
36942     xhrOnLoad : function(xhr)
36943     {
36944         if(this.loadMask){
36945             this.maskEl.unmask();
36946         }
36947         
36948         if (xhr.readyState !== 4) {
36949             this.fireEvent('exception', this, xhr);
36950             return;
36951         }
36952
36953         var response = Roo.decode(xhr.responseText);
36954         
36955         if(!response.success){
36956             this.fireEvent('exception', this, xhr);
36957             return;
36958         }
36959         
36960         var response = Roo.decode(xhr.responseText);
36961         
36962         this.fireEvent('upload', this, response);
36963         
36964     },
36965     
36966     xhrOnError : function()
36967     {
36968         if(this.loadMask){
36969             this.maskEl.unmask();
36970         }
36971         
36972         Roo.log('xhr on error');
36973         
36974         var response = Roo.decode(xhr.responseText);
36975           
36976         Roo.log(response);
36977         
36978     },
36979     
36980     prepare : function(file)
36981     {   
36982         if(this.loadMask){
36983             this.maskEl.mask(this.loadingText);
36984         }
36985         
36986         this.file = false;
36987         this.exif = {};
36988         
36989         if(typeof(file) === 'string'){
36990             this.loadCanvas(file);
36991             return;
36992         }
36993         
36994         if(!file || !this.urlAPI){
36995             return;
36996         }
36997         
36998         this.file = file;
36999         this.cropType = file.type;
37000         
37001         var _this = this;
37002         
37003         if(this.fireEvent('prepare', this, this.file) != false){
37004             
37005             var reader = new FileReader();
37006             
37007             reader.onload = function (e) {
37008                 if (e.target.error) {
37009                     Roo.log(e.target.error);
37010                     return;
37011                 }
37012                 
37013                 var buffer = e.target.result,
37014                     dataView = new DataView(buffer),
37015                     offset = 2,
37016                     maxOffset = dataView.byteLength - 4,
37017                     markerBytes,
37018                     markerLength;
37019                 
37020                 if (dataView.getUint16(0) === 0xffd8) {
37021                     while (offset < maxOffset) {
37022                         markerBytes = dataView.getUint16(offset);
37023                         
37024                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37025                             markerLength = dataView.getUint16(offset + 2) + 2;
37026                             if (offset + markerLength > dataView.byteLength) {
37027                                 Roo.log('Invalid meta data: Invalid segment size.');
37028                                 break;
37029                             }
37030                             
37031                             if(markerBytes == 0xffe1){
37032                                 _this.parseExifData(
37033                                     dataView,
37034                                     offset,
37035                                     markerLength
37036                                 );
37037                             }
37038                             
37039                             offset += markerLength;
37040                             
37041                             continue;
37042                         }
37043                         
37044                         break;
37045                     }
37046                     
37047                 }
37048                 
37049                 var url = _this.urlAPI.createObjectURL(_this.file);
37050                 
37051                 _this.loadCanvas(url);
37052                 
37053                 return;
37054             }
37055             
37056             reader.readAsArrayBuffer(this.file);
37057             
37058         }
37059         
37060     },
37061     
37062     parseExifData : function(dataView, offset, length)
37063     {
37064         var tiffOffset = offset + 10,
37065             littleEndian,
37066             dirOffset;
37067     
37068         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37069             // No Exif data, might be XMP data instead
37070             return;
37071         }
37072         
37073         // Check for the ASCII code for "Exif" (0x45786966):
37074         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37075             // No Exif data, might be XMP data instead
37076             return;
37077         }
37078         if (tiffOffset + 8 > dataView.byteLength) {
37079             Roo.log('Invalid Exif data: Invalid segment size.');
37080             return;
37081         }
37082         // Check for the two null bytes:
37083         if (dataView.getUint16(offset + 8) !== 0x0000) {
37084             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37085             return;
37086         }
37087         // Check the byte alignment:
37088         switch (dataView.getUint16(tiffOffset)) {
37089         case 0x4949:
37090             littleEndian = true;
37091             break;
37092         case 0x4D4D:
37093             littleEndian = false;
37094             break;
37095         default:
37096             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37097             return;
37098         }
37099         // Check for the TIFF tag marker (0x002A):
37100         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37101             Roo.log('Invalid Exif data: Missing TIFF marker.');
37102             return;
37103         }
37104         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37105         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37106         
37107         this.parseExifTags(
37108             dataView,
37109             tiffOffset,
37110             tiffOffset + dirOffset,
37111             littleEndian
37112         );
37113     },
37114     
37115     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37116     {
37117         var tagsNumber,
37118             dirEndOffset,
37119             i;
37120         if (dirOffset + 6 > dataView.byteLength) {
37121             Roo.log('Invalid Exif data: Invalid directory offset.');
37122             return;
37123         }
37124         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37125         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37126         if (dirEndOffset + 4 > dataView.byteLength) {
37127             Roo.log('Invalid Exif data: Invalid directory size.');
37128             return;
37129         }
37130         for (i = 0; i < tagsNumber; i += 1) {
37131             this.parseExifTag(
37132                 dataView,
37133                 tiffOffset,
37134                 dirOffset + 2 + 12 * i, // tag offset
37135                 littleEndian
37136             );
37137         }
37138         // Return the offset to the next directory:
37139         return dataView.getUint32(dirEndOffset, littleEndian);
37140     },
37141     
37142     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37143     {
37144         var tag = dataView.getUint16(offset, littleEndian);
37145         
37146         this.exif[tag] = this.getExifValue(
37147             dataView,
37148             tiffOffset,
37149             offset,
37150             dataView.getUint16(offset + 2, littleEndian), // tag type
37151             dataView.getUint32(offset + 4, littleEndian), // tag length
37152             littleEndian
37153         );
37154     },
37155     
37156     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37157     {
37158         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37159             tagSize,
37160             dataOffset,
37161             values,
37162             i,
37163             str,
37164             c;
37165     
37166         if (!tagType) {
37167             Roo.log('Invalid Exif data: Invalid tag type.');
37168             return;
37169         }
37170         
37171         tagSize = tagType.size * length;
37172         // Determine if the value is contained in the dataOffset bytes,
37173         // or if the value at the dataOffset is a pointer to the actual data:
37174         dataOffset = tagSize > 4 ?
37175                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37176         if (dataOffset + tagSize > dataView.byteLength) {
37177             Roo.log('Invalid Exif data: Invalid data offset.');
37178             return;
37179         }
37180         if (length === 1) {
37181             return tagType.getValue(dataView, dataOffset, littleEndian);
37182         }
37183         values = [];
37184         for (i = 0; i < length; i += 1) {
37185             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37186         }
37187         
37188         if (tagType.ascii) {
37189             str = '';
37190             // Concatenate the chars:
37191             for (i = 0; i < values.length; i += 1) {
37192                 c = values[i];
37193                 // Ignore the terminating NULL byte(s):
37194                 if (c === '\u0000') {
37195                     break;
37196                 }
37197                 str += c;
37198             }
37199             return str;
37200         }
37201         return values;
37202     }
37203     
37204 });
37205
37206 Roo.apply(Roo.bootstrap.UploadCropbox, {
37207     tags : {
37208         'Orientation': 0x0112
37209     },
37210     
37211     Orientation: {
37212             1: 0, //'top-left',
37213 //            2: 'top-right',
37214             3: 180, //'bottom-right',
37215 //            4: 'bottom-left',
37216 //            5: 'left-top',
37217             6: 90, //'right-top',
37218 //            7: 'right-bottom',
37219             8: 270 //'left-bottom'
37220     },
37221     
37222     exifTagTypes : {
37223         // byte, 8-bit unsigned int:
37224         1: {
37225             getValue: function (dataView, dataOffset) {
37226                 return dataView.getUint8(dataOffset);
37227             },
37228             size: 1
37229         },
37230         // ascii, 8-bit byte:
37231         2: {
37232             getValue: function (dataView, dataOffset) {
37233                 return String.fromCharCode(dataView.getUint8(dataOffset));
37234             },
37235             size: 1,
37236             ascii: true
37237         },
37238         // short, 16 bit int:
37239         3: {
37240             getValue: function (dataView, dataOffset, littleEndian) {
37241                 return dataView.getUint16(dataOffset, littleEndian);
37242             },
37243             size: 2
37244         },
37245         // long, 32 bit int:
37246         4: {
37247             getValue: function (dataView, dataOffset, littleEndian) {
37248                 return dataView.getUint32(dataOffset, littleEndian);
37249             },
37250             size: 4
37251         },
37252         // rational = two long values, first is numerator, second is denominator:
37253         5: {
37254             getValue: function (dataView, dataOffset, littleEndian) {
37255                 return dataView.getUint32(dataOffset, littleEndian) /
37256                     dataView.getUint32(dataOffset + 4, littleEndian);
37257             },
37258             size: 8
37259         },
37260         // slong, 32 bit signed int:
37261         9: {
37262             getValue: function (dataView, dataOffset, littleEndian) {
37263                 return dataView.getInt32(dataOffset, littleEndian);
37264             },
37265             size: 4
37266         },
37267         // srational, two slongs, first is numerator, second is denominator:
37268         10: {
37269             getValue: function (dataView, dataOffset, littleEndian) {
37270                 return dataView.getInt32(dataOffset, littleEndian) /
37271                     dataView.getInt32(dataOffset + 4, littleEndian);
37272             },
37273             size: 8
37274         }
37275     },
37276     
37277     footer : {
37278         STANDARD : [
37279             {
37280                 tag : 'div',
37281                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37282                 action : 'rotate-left',
37283                 cn : [
37284                     {
37285                         tag : 'button',
37286                         cls : 'btn btn-default',
37287                         html : '<i class="fa fa-undo"></i>'
37288                     }
37289                 ]
37290             },
37291             {
37292                 tag : 'div',
37293                 cls : 'btn-group roo-upload-cropbox-picture',
37294                 action : 'picture',
37295                 cn : [
37296                     {
37297                         tag : 'button',
37298                         cls : 'btn btn-default',
37299                         html : '<i class="fa fa-picture-o"></i>'
37300                     }
37301                 ]
37302             },
37303             {
37304                 tag : 'div',
37305                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37306                 action : 'rotate-right',
37307                 cn : [
37308                     {
37309                         tag : 'button',
37310                         cls : 'btn btn-default',
37311                         html : '<i class="fa fa-repeat"></i>'
37312                     }
37313                 ]
37314             }
37315         ],
37316         DOCUMENT : [
37317             {
37318                 tag : 'div',
37319                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37320                 action : 'rotate-left',
37321                 cn : [
37322                     {
37323                         tag : 'button',
37324                         cls : 'btn btn-default',
37325                         html : '<i class="fa fa-undo"></i>'
37326                     }
37327                 ]
37328             },
37329             {
37330                 tag : 'div',
37331                 cls : 'btn-group roo-upload-cropbox-download',
37332                 action : 'download',
37333                 cn : [
37334                     {
37335                         tag : 'button',
37336                         cls : 'btn btn-default',
37337                         html : '<i class="fa fa-download"></i>'
37338                     }
37339                 ]
37340             },
37341             {
37342                 tag : 'div',
37343                 cls : 'btn-group roo-upload-cropbox-crop',
37344                 action : 'crop',
37345                 cn : [
37346                     {
37347                         tag : 'button',
37348                         cls : 'btn btn-default',
37349                         html : '<i class="fa fa-crop"></i>'
37350                     }
37351                 ]
37352             },
37353             {
37354                 tag : 'div',
37355                 cls : 'btn-group roo-upload-cropbox-trash',
37356                 action : 'trash',
37357                 cn : [
37358                     {
37359                         tag : 'button',
37360                         cls : 'btn btn-default',
37361                         html : '<i class="fa fa-trash"></i>'
37362                     }
37363                 ]
37364             },
37365             {
37366                 tag : 'div',
37367                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37368                 action : 'rotate-right',
37369                 cn : [
37370                     {
37371                         tag : 'button',
37372                         cls : 'btn btn-default',
37373                         html : '<i class="fa fa-repeat"></i>'
37374                     }
37375                 ]
37376             }
37377         ],
37378         ROTATOR : [
37379             {
37380                 tag : 'div',
37381                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37382                 action : 'rotate-left',
37383                 cn : [
37384                     {
37385                         tag : 'button',
37386                         cls : 'btn btn-default',
37387                         html : '<i class="fa fa-undo"></i>'
37388                     }
37389                 ]
37390             },
37391             {
37392                 tag : 'div',
37393                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37394                 action : 'rotate-right',
37395                 cn : [
37396                     {
37397                         tag : 'button',
37398                         cls : 'btn btn-default',
37399                         html : '<i class="fa fa-repeat"></i>'
37400                     }
37401                 ]
37402             }
37403         ]
37404     }
37405 });
37406
37407 /*
37408 * Licence: LGPL
37409 */
37410
37411 /**
37412  * @class Roo.bootstrap.DocumentManager
37413  * @extends Roo.bootstrap.Component
37414  * Bootstrap DocumentManager class
37415  * @cfg {String} paramName default 'imageUpload'
37416  * @cfg {String} toolTipName default 'filename'
37417  * @cfg {String} method default POST
37418  * @cfg {String} url action url
37419  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37420  * @cfg {Boolean} multiple multiple upload default true
37421  * @cfg {Number} thumbSize default 300
37422  * @cfg {String} fieldLabel
37423  * @cfg {Number} labelWidth default 4
37424  * @cfg {String} labelAlign (left|top) default left
37425  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37426 * @cfg {Number} labellg set the width of label (1-12)
37427  * @cfg {Number} labelmd set the width of label (1-12)
37428  * @cfg {Number} labelsm set the width of label (1-12)
37429  * @cfg {Number} labelxs set the width of label (1-12)
37430  * 
37431  * @constructor
37432  * Create a new DocumentManager
37433  * @param {Object} config The config object
37434  */
37435
37436 Roo.bootstrap.DocumentManager = function(config){
37437     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37438     
37439     this.files = [];
37440     this.delegates = [];
37441     
37442     this.addEvents({
37443         /**
37444          * @event initial
37445          * Fire when initial the DocumentManager
37446          * @param {Roo.bootstrap.DocumentManager} this
37447          */
37448         "initial" : true,
37449         /**
37450          * @event inspect
37451          * inspect selected file
37452          * @param {Roo.bootstrap.DocumentManager} this
37453          * @param {File} file
37454          */
37455         "inspect" : true,
37456         /**
37457          * @event exception
37458          * Fire when xhr load exception
37459          * @param {Roo.bootstrap.DocumentManager} this
37460          * @param {XMLHttpRequest} xhr
37461          */
37462         "exception" : true,
37463         /**
37464          * @event afterupload
37465          * Fire when xhr load exception
37466          * @param {Roo.bootstrap.DocumentManager} this
37467          * @param {XMLHttpRequest} xhr
37468          */
37469         "afterupload" : true,
37470         /**
37471          * @event prepare
37472          * prepare the form data
37473          * @param {Roo.bootstrap.DocumentManager} this
37474          * @param {Object} formData
37475          */
37476         "prepare" : true,
37477         /**
37478          * @event remove
37479          * Fire when remove the file
37480          * @param {Roo.bootstrap.DocumentManager} this
37481          * @param {Object} file
37482          */
37483         "remove" : true,
37484         /**
37485          * @event refresh
37486          * Fire after refresh the file
37487          * @param {Roo.bootstrap.DocumentManager} this
37488          */
37489         "refresh" : true,
37490         /**
37491          * @event click
37492          * Fire after click the image
37493          * @param {Roo.bootstrap.DocumentManager} this
37494          * @param {Object} file
37495          */
37496         "click" : true,
37497         /**
37498          * @event edit
37499          * Fire when upload a image and editable set to true
37500          * @param {Roo.bootstrap.DocumentManager} this
37501          * @param {Object} file
37502          */
37503         "edit" : true,
37504         /**
37505          * @event beforeselectfile
37506          * Fire before select file
37507          * @param {Roo.bootstrap.DocumentManager} this
37508          */
37509         "beforeselectfile" : true,
37510         /**
37511          * @event process
37512          * Fire before process file
37513          * @param {Roo.bootstrap.DocumentManager} this
37514          * @param {Object} file
37515          */
37516         "process" : true,
37517         /**
37518          * @event previewrendered
37519          * Fire when preview rendered
37520          * @param {Roo.bootstrap.DocumentManager} this
37521          * @param {Object} file
37522          */
37523         "previewrendered" : true,
37524         /**
37525          */
37526         "previewResize" : true
37527         
37528     });
37529 };
37530
37531 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37532     
37533     boxes : 0,
37534     inputName : '',
37535     thumbSize : 300,
37536     multiple : true,
37537     files : false,
37538     method : 'POST',
37539     url : '',
37540     paramName : 'imageUpload',
37541     toolTipName : 'filename',
37542     fieldLabel : '',
37543     labelWidth : 4,
37544     labelAlign : 'left',
37545     editable : true,
37546     delegates : false,
37547     xhr : false, 
37548     
37549     labellg : 0,
37550     labelmd : 0,
37551     labelsm : 0,
37552     labelxs : 0,
37553     
37554     getAutoCreate : function()
37555     {   
37556         var managerWidget = {
37557             tag : 'div',
37558             cls : 'roo-document-manager',
37559             cn : [
37560                 {
37561                     tag : 'input',
37562                     cls : 'roo-document-manager-selector',
37563                     type : 'file'
37564                 },
37565                 {
37566                     tag : 'div',
37567                     cls : 'roo-document-manager-uploader',
37568                     cn : [
37569                         {
37570                             tag : 'div',
37571                             cls : 'roo-document-manager-upload-btn',
37572                             html : '<i class="fa fa-plus"></i>'
37573                         }
37574                     ]
37575                     
37576                 }
37577             ]
37578         };
37579         
37580         var content = [
37581             {
37582                 tag : 'div',
37583                 cls : 'column col-md-12',
37584                 cn : managerWidget
37585             }
37586         ];
37587         
37588         if(this.fieldLabel.length){
37589             
37590             content = [
37591                 {
37592                     tag : 'div',
37593                     cls : 'column col-md-12',
37594                     html : this.fieldLabel
37595                 },
37596                 {
37597                     tag : 'div',
37598                     cls : 'column col-md-12',
37599                     cn : managerWidget
37600                 }
37601             ];
37602
37603             if(this.labelAlign == 'left'){
37604                 content = [
37605                     {
37606                         tag : 'div',
37607                         cls : 'column',
37608                         html : this.fieldLabel
37609                     },
37610                     {
37611                         tag : 'div',
37612                         cls : 'column',
37613                         cn : managerWidget
37614                     }
37615                 ];
37616                 
37617                 if(this.labelWidth > 12){
37618                     content[0].style = "width: " + this.labelWidth + 'px';
37619                 }
37620
37621                 if(this.labelWidth < 13 && this.labelmd == 0){
37622                     this.labelmd = this.labelWidth;
37623                 }
37624
37625                 if(this.labellg > 0){
37626                     content[0].cls += ' col-lg-' + this.labellg;
37627                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37628                 }
37629
37630                 if(this.labelmd > 0){
37631                     content[0].cls += ' col-md-' + this.labelmd;
37632                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37633                 }
37634
37635                 if(this.labelsm > 0){
37636                     content[0].cls += ' col-sm-' + this.labelsm;
37637                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37638                 }
37639
37640                 if(this.labelxs > 0){
37641                     content[0].cls += ' col-xs-' + this.labelxs;
37642                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37643                 }
37644                 
37645             }
37646         }
37647         
37648         var cfg = {
37649             tag : 'div',
37650             cls : 'row clearfix',
37651             cn : content
37652         };
37653         
37654         return cfg;
37655         
37656     },
37657     
37658     initEvents : function()
37659     {
37660         this.managerEl = this.el.select('.roo-document-manager', true).first();
37661         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37662         
37663         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37664         this.selectorEl.hide();
37665         
37666         if(this.multiple){
37667             this.selectorEl.attr('multiple', 'multiple');
37668         }
37669         
37670         this.selectorEl.on('change', this.onFileSelected, this);
37671         
37672         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37673         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37674         
37675         this.uploader.on('click', this.onUploaderClick, this);
37676         
37677         this.renderProgressDialog();
37678         
37679         var _this = this;
37680         
37681         window.addEventListener("resize", function() { _this.refresh(); } );
37682         
37683         this.fireEvent('initial', this);
37684     },
37685     
37686     renderProgressDialog : function()
37687     {
37688         var _this = this;
37689         
37690         this.progressDialog = new Roo.bootstrap.Modal({
37691             cls : 'roo-document-manager-progress-dialog',
37692             allow_close : false,
37693             animate : false,
37694             title : '',
37695             buttons : [
37696                 {
37697                     name  :'cancel',
37698                     weight : 'danger',
37699                     html : 'Cancel'
37700                 }
37701             ], 
37702             listeners : { 
37703                 btnclick : function() {
37704                     _this.uploadCancel();
37705                     this.hide();
37706                 }
37707             }
37708         });
37709          
37710         this.progressDialog.render(Roo.get(document.body));
37711          
37712         this.progress = new Roo.bootstrap.Progress({
37713             cls : 'roo-document-manager-progress',
37714             active : true,
37715             striped : true
37716         });
37717         
37718         this.progress.render(this.progressDialog.getChildContainer());
37719         
37720         this.progressBar = new Roo.bootstrap.ProgressBar({
37721             cls : 'roo-document-manager-progress-bar',
37722             aria_valuenow : 0,
37723             aria_valuemin : 0,
37724             aria_valuemax : 12,
37725             panel : 'success'
37726         });
37727         
37728         this.progressBar.render(this.progress.getChildContainer());
37729     },
37730     
37731     onUploaderClick : function(e)
37732     {
37733         e.preventDefault();
37734      
37735         if(this.fireEvent('beforeselectfile', this) != false){
37736             this.selectorEl.dom.click();
37737         }
37738         
37739     },
37740     
37741     onFileSelected : function(e)
37742     {
37743         e.preventDefault();
37744         
37745         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37746             return;
37747         }
37748         
37749         Roo.each(this.selectorEl.dom.files, function(file){
37750             if(this.fireEvent('inspect', this, file) != false){
37751                 this.files.push(file);
37752             }
37753         }, this);
37754         
37755         this.queue();
37756         
37757     },
37758     
37759     queue : function()
37760     {
37761         this.selectorEl.dom.value = '';
37762         
37763         if(!this.files || !this.files.length){
37764             return;
37765         }
37766         
37767         if(this.boxes > 0 && this.files.length > this.boxes){
37768             this.files = this.files.slice(0, this.boxes);
37769         }
37770         
37771         this.uploader.show();
37772         
37773         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37774             this.uploader.hide();
37775         }
37776         
37777         var _this = this;
37778         
37779         var files = [];
37780         
37781         var docs = [];
37782         
37783         Roo.each(this.files, function(file){
37784             
37785             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37786                 var f = this.renderPreview(file);
37787                 files.push(f);
37788                 return;
37789             }
37790             
37791             if(file.type.indexOf('image') != -1){
37792                 this.delegates.push(
37793                     (function(){
37794                         _this.process(file);
37795                     }).createDelegate(this)
37796                 );
37797         
37798                 return;
37799             }
37800             
37801             docs.push(
37802                 (function(){
37803                     _this.process(file);
37804                 }).createDelegate(this)
37805             );
37806             
37807         }, this);
37808         
37809         this.files = files;
37810         
37811         this.delegates = this.delegates.concat(docs);
37812         
37813         if(!this.delegates.length){
37814             this.refresh();
37815             return;
37816         }
37817         
37818         this.progressBar.aria_valuemax = this.delegates.length;
37819         
37820         this.arrange();
37821         
37822         return;
37823     },
37824     
37825     arrange : function()
37826     {
37827         if(!this.delegates.length){
37828             this.progressDialog.hide();
37829             this.refresh();
37830             return;
37831         }
37832         
37833         var delegate = this.delegates.shift();
37834         
37835         this.progressDialog.show();
37836         
37837         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37838         
37839         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37840         
37841         delegate();
37842     },
37843     
37844     refresh : function()
37845     {
37846         this.uploader.show();
37847         
37848         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37849             this.uploader.hide();
37850         }
37851         
37852         Roo.isTouch ? this.closable(false) : this.closable(true);
37853         
37854         this.fireEvent('refresh', this);
37855     },
37856     
37857     onRemove : function(e, el, o)
37858     {
37859         e.preventDefault();
37860         
37861         this.fireEvent('remove', this, o);
37862         
37863     },
37864     
37865     remove : function(o)
37866     {
37867         var files = [];
37868         
37869         Roo.each(this.files, function(file){
37870             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37871                 files.push(file);
37872                 return;
37873             }
37874
37875             o.target.remove();
37876
37877         }, this);
37878         
37879         this.files = files;
37880         
37881         this.refresh();
37882     },
37883     
37884     clear : function()
37885     {
37886         Roo.each(this.files, function(file){
37887             if(!file.target){
37888                 return;
37889             }
37890             
37891             file.target.remove();
37892
37893         }, this);
37894         
37895         this.files = [];
37896         
37897         this.refresh();
37898     },
37899     
37900     onClick : function(e, el, o)
37901     {
37902         e.preventDefault();
37903         
37904         this.fireEvent('click', this, o);
37905         
37906     },
37907     
37908     closable : function(closable)
37909     {
37910         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37911             
37912             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37913             
37914             if(closable){
37915                 el.show();
37916                 return;
37917             }
37918             
37919             el.hide();
37920             
37921         }, this);
37922     },
37923     
37924     xhrOnLoad : function(xhr)
37925     {
37926         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37927             el.remove();
37928         }, this);
37929         
37930         if (xhr.readyState !== 4) {
37931             this.arrange();
37932             this.fireEvent('exception', this, xhr);
37933             return;
37934         }
37935
37936         var response = Roo.decode(xhr.responseText);
37937         
37938         if(!response.success){
37939             this.arrange();
37940             this.fireEvent('exception', this, xhr);
37941             return;
37942         }
37943         
37944         var file = this.renderPreview(response.data);
37945         
37946         this.files.push(file);
37947         
37948         this.arrange();
37949         
37950         this.fireEvent('afterupload', this, xhr);
37951         
37952     },
37953     
37954     xhrOnError : function(xhr)
37955     {
37956         Roo.log('xhr on error');
37957         
37958         var response = Roo.decode(xhr.responseText);
37959           
37960         Roo.log(response);
37961         
37962         this.arrange();
37963     },
37964     
37965     process : function(file)
37966     {
37967         if(this.fireEvent('process', this, file) !== false){
37968             if(this.editable && file.type.indexOf('image') != -1){
37969                 this.fireEvent('edit', this, file);
37970                 return;
37971             }
37972
37973             this.uploadStart(file, false);
37974
37975             return;
37976         }
37977         
37978     },
37979     
37980     uploadStart : function(file, crop)
37981     {
37982         this.xhr = new XMLHttpRequest();
37983         
37984         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37985             this.arrange();
37986             return;
37987         }
37988         
37989         file.xhr = this.xhr;
37990             
37991         this.managerEl.createChild({
37992             tag : 'div',
37993             cls : 'roo-document-manager-loading',
37994             cn : [
37995                 {
37996                     tag : 'div',
37997                     tooltip : file.name,
37998                     cls : 'roo-document-manager-thumb',
37999                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38000                 }
38001             ]
38002
38003         });
38004
38005         this.xhr.open(this.method, this.url, true);
38006         
38007         var headers = {
38008             "Accept": "application/json",
38009             "Cache-Control": "no-cache",
38010             "X-Requested-With": "XMLHttpRequest"
38011         };
38012         
38013         for (var headerName in headers) {
38014             var headerValue = headers[headerName];
38015             if (headerValue) {
38016                 this.xhr.setRequestHeader(headerName, headerValue);
38017             }
38018         }
38019         
38020         var _this = this;
38021         
38022         this.xhr.onload = function()
38023         {
38024             _this.xhrOnLoad(_this.xhr);
38025         }
38026         
38027         this.xhr.onerror = function()
38028         {
38029             _this.xhrOnError(_this.xhr);
38030         }
38031         
38032         var formData = new FormData();
38033
38034         formData.append('returnHTML', 'NO');
38035         
38036         if(crop){
38037             formData.append('crop', crop);
38038         }
38039         
38040         formData.append(this.paramName, file, file.name);
38041         
38042         var options = {
38043             file : file, 
38044             manually : false
38045         };
38046         
38047         if(this.fireEvent('prepare', this, formData, options) != false){
38048             
38049             if(options.manually){
38050                 return;
38051             }
38052             
38053             this.xhr.send(formData);
38054             return;
38055         };
38056         
38057         this.uploadCancel();
38058     },
38059     
38060     uploadCancel : function()
38061     {
38062         if (this.xhr) {
38063             this.xhr.abort();
38064         }
38065         
38066         this.delegates = [];
38067         
38068         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38069             el.remove();
38070         }, this);
38071         
38072         this.arrange();
38073     },
38074     
38075     renderPreview : function(file)
38076     {
38077         if(typeof(file.target) != 'undefined' && file.target){
38078             return file;
38079         }
38080         
38081         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38082         
38083         var previewEl = this.managerEl.createChild({
38084             tag : 'div',
38085             cls : 'roo-document-manager-preview',
38086             cn : [
38087                 {
38088                     tag : 'div',
38089                     tooltip : file[this.toolTipName],
38090                     cls : 'roo-document-manager-thumb',
38091                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38092                 },
38093                 {
38094                     tag : 'button',
38095                     cls : 'close',
38096                     html : '<i class="fa fa-times-circle"></i>'
38097                 }
38098             ]
38099         });
38100
38101         var close = previewEl.select('button.close', true).first();
38102
38103         close.on('click', this.onRemove, this, file);
38104
38105         file.target = previewEl;
38106
38107         var image = previewEl.select('img', true).first();
38108         
38109         var _this = this;
38110         
38111         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38112         
38113         image.on('click', this.onClick, this, file);
38114         
38115         this.fireEvent('previewrendered', this, file);
38116         
38117         return file;
38118         
38119     },
38120     
38121     onPreviewLoad : function(file, image)
38122     {
38123         if(typeof(file.target) == 'undefined' || !file.target){
38124             return;
38125         }
38126         
38127         var width = image.dom.naturalWidth || image.dom.width;
38128         var height = image.dom.naturalHeight || image.dom.height;
38129         
38130         if(!this.previewResize) {
38131             return;
38132         }
38133         
38134         if(width > height){
38135             file.target.addClass('wide');
38136             return;
38137         }
38138         
38139         file.target.addClass('tall');
38140         return;
38141         
38142     },
38143     
38144     uploadFromSource : function(file, crop)
38145     {
38146         this.xhr = new XMLHttpRequest();
38147         
38148         this.managerEl.createChild({
38149             tag : 'div',
38150             cls : 'roo-document-manager-loading',
38151             cn : [
38152                 {
38153                     tag : 'div',
38154                     tooltip : file.name,
38155                     cls : 'roo-document-manager-thumb',
38156                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38157                 }
38158             ]
38159
38160         });
38161
38162         this.xhr.open(this.method, this.url, true);
38163         
38164         var headers = {
38165             "Accept": "application/json",
38166             "Cache-Control": "no-cache",
38167             "X-Requested-With": "XMLHttpRequest"
38168         };
38169         
38170         for (var headerName in headers) {
38171             var headerValue = headers[headerName];
38172             if (headerValue) {
38173                 this.xhr.setRequestHeader(headerName, headerValue);
38174             }
38175         }
38176         
38177         var _this = this;
38178         
38179         this.xhr.onload = function()
38180         {
38181             _this.xhrOnLoad(_this.xhr);
38182         }
38183         
38184         this.xhr.onerror = function()
38185         {
38186             _this.xhrOnError(_this.xhr);
38187         }
38188         
38189         var formData = new FormData();
38190
38191         formData.append('returnHTML', 'NO');
38192         
38193         formData.append('crop', crop);
38194         
38195         if(typeof(file.filename) != 'undefined'){
38196             formData.append('filename', file.filename);
38197         }
38198         
38199         if(typeof(file.mimetype) != 'undefined'){
38200             formData.append('mimetype', file.mimetype);
38201         }
38202         
38203         Roo.log(formData);
38204         
38205         if(this.fireEvent('prepare', this, formData) != false){
38206             this.xhr.send(formData);
38207         };
38208     }
38209 });
38210
38211 /*
38212 * Licence: LGPL
38213 */
38214
38215 /**
38216  * @class Roo.bootstrap.DocumentViewer
38217  * @extends Roo.bootstrap.Component
38218  * Bootstrap DocumentViewer class
38219  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38220  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38221  * 
38222  * @constructor
38223  * Create a new DocumentViewer
38224  * @param {Object} config The config object
38225  */
38226
38227 Roo.bootstrap.DocumentViewer = function(config){
38228     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38229     
38230     this.addEvents({
38231         /**
38232          * @event initial
38233          * Fire after initEvent
38234          * @param {Roo.bootstrap.DocumentViewer} this
38235          */
38236         "initial" : true,
38237         /**
38238          * @event click
38239          * Fire after click
38240          * @param {Roo.bootstrap.DocumentViewer} this
38241          */
38242         "click" : true,
38243         /**
38244          * @event download
38245          * Fire after download button
38246          * @param {Roo.bootstrap.DocumentViewer} this
38247          */
38248         "download" : true,
38249         /**
38250          * @event trash
38251          * Fire after trash button
38252          * @param {Roo.bootstrap.DocumentViewer} this
38253          */
38254         "trash" : true
38255         
38256     });
38257 };
38258
38259 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38260     
38261     showDownload : true,
38262     
38263     showTrash : true,
38264     
38265     getAutoCreate : function()
38266     {
38267         var cfg = {
38268             tag : 'div',
38269             cls : 'roo-document-viewer',
38270             cn : [
38271                 {
38272                     tag : 'div',
38273                     cls : 'roo-document-viewer-body',
38274                     cn : [
38275                         {
38276                             tag : 'div',
38277                             cls : 'roo-document-viewer-thumb',
38278                             cn : [
38279                                 {
38280                                     tag : 'img',
38281                                     cls : 'roo-document-viewer-image'
38282                                 }
38283                             ]
38284                         }
38285                     ]
38286                 },
38287                 {
38288                     tag : 'div',
38289                     cls : 'roo-document-viewer-footer',
38290                     cn : {
38291                         tag : 'div',
38292                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38293                         cn : [
38294                             {
38295                                 tag : 'div',
38296                                 cls : 'btn-group roo-document-viewer-download',
38297                                 cn : [
38298                                     {
38299                                         tag : 'button',
38300                                         cls : 'btn btn-default',
38301                                         html : '<i class="fa fa-download"></i>'
38302                                     }
38303                                 ]
38304                             },
38305                             {
38306                                 tag : 'div',
38307                                 cls : 'btn-group roo-document-viewer-trash',
38308                                 cn : [
38309                                     {
38310                                         tag : 'button',
38311                                         cls : 'btn btn-default',
38312                                         html : '<i class="fa fa-trash"></i>'
38313                                     }
38314                                 ]
38315                             }
38316                         ]
38317                     }
38318                 }
38319             ]
38320         };
38321         
38322         return cfg;
38323     },
38324     
38325     initEvents : function()
38326     {
38327         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38328         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38329         
38330         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38331         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38332         
38333         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38334         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38335         
38336         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38337         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38338         
38339         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38340         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38341         
38342         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38343         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38344         
38345         this.bodyEl.on('click', this.onClick, this);
38346         this.downloadBtn.on('click', this.onDownload, this);
38347         this.trashBtn.on('click', this.onTrash, this);
38348         
38349         this.downloadBtn.hide();
38350         this.trashBtn.hide();
38351         
38352         if(this.showDownload){
38353             this.downloadBtn.show();
38354         }
38355         
38356         if(this.showTrash){
38357             this.trashBtn.show();
38358         }
38359         
38360         if(!this.showDownload && !this.showTrash) {
38361             this.footerEl.hide();
38362         }
38363         
38364     },
38365     
38366     initial : function()
38367     {
38368         this.fireEvent('initial', this);
38369         
38370     },
38371     
38372     onClick : function(e)
38373     {
38374         e.preventDefault();
38375         
38376         this.fireEvent('click', this);
38377     },
38378     
38379     onDownload : function(e)
38380     {
38381         e.preventDefault();
38382         
38383         this.fireEvent('download', this);
38384     },
38385     
38386     onTrash : function(e)
38387     {
38388         e.preventDefault();
38389         
38390         this.fireEvent('trash', this);
38391     }
38392     
38393 });
38394 /*
38395  * - LGPL
38396  *
38397  * FieldLabel
38398  * 
38399  */
38400
38401 /**
38402  * @class Roo.bootstrap.form.FieldLabel
38403  * @extends Roo.bootstrap.Component
38404  * Bootstrap FieldLabel class
38405  * @cfg {String} html contents of the element
38406  * @cfg {String} tag tag of the element default label
38407  * @cfg {String} cls class of the element
38408  * @cfg {String} target label target 
38409  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38410  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38411  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38412  * @cfg {String} iconTooltip default "This field is required"
38413  * @cfg {String} indicatorpos (left|right) default left
38414  * 
38415  * @constructor
38416  * Create a new FieldLabel
38417  * @param {Object} config The config object
38418  */
38419
38420 Roo.bootstrap.form.FieldLabel = function(config){
38421     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38422     
38423     this.addEvents({
38424             /**
38425              * @event invalid
38426              * Fires after the field has been marked as invalid.
38427              * @param {Roo.form.FieldLabel} this
38428              * @param {String} msg The validation message
38429              */
38430             invalid : true,
38431             /**
38432              * @event valid
38433              * Fires after the field has been validated with no errors.
38434              * @param {Roo.form.FieldLabel} this
38435              */
38436             valid : true
38437         });
38438 };
38439
38440 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38441     
38442     tag: 'label',
38443     cls: '',
38444     html: '',
38445     target: '',
38446     allowBlank : true,
38447     invalidClass : 'has-warning',
38448     validClass : 'has-success',
38449     iconTooltip : 'This field is required',
38450     indicatorpos : 'left',
38451     
38452     getAutoCreate : function(){
38453         
38454         var cls = "";
38455         if (!this.allowBlank) {
38456             cls  = "visible";
38457         }
38458         
38459         var cfg = {
38460             tag : this.tag,
38461             cls : 'roo-bootstrap-field-label ' + this.cls,
38462             for : this.target,
38463             cn : [
38464                 {
38465                     tag : 'i',
38466                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38467                     tooltip : this.iconTooltip
38468                 },
38469                 {
38470                     tag : 'span',
38471                     html : this.html
38472                 }
38473             ] 
38474         };
38475         
38476         if(this.indicatorpos == 'right'){
38477             var cfg = {
38478                 tag : this.tag,
38479                 cls : 'roo-bootstrap-field-label ' + this.cls,
38480                 for : this.target,
38481                 cn : [
38482                     {
38483                         tag : 'span',
38484                         html : this.html
38485                     },
38486                     {
38487                         tag : 'i',
38488                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38489                         tooltip : this.iconTooltip
38490                     }
38491                 ] 
38492             };
38493         }
38494         
38495         return cfg;
38496     },
38497     
38498     initEvents: function() 
38499     {
38500         Roo.bootstrap.Element.superclass.initEvents.call(this);
38501         
38502         this.indicator = this.indicatorEl();
38503         
38504         if(this.indicator){
38505             this.indicator.removeClass('visible');
38506             this.indicator.addClass('invisible');
38507         }
38508         
38509         Roo.bootstrap.form.FieldLabel.register(this);
38510     },
38511     
38512     indicatorEl : function()
38513     {
38514         var indicator = this.el.select('i.roo-required-indicator',true).first();
38515         
38516         if(!indicator){
38517             return false;
38518         }
38519         
38520         return indicator;
38521         
38522     },
38523     
38524     /**
38525      * Mark this field as valid
38526      */
38527     markValid : function()
38528     {
38529         if(this.indicator){
38530             this.indicator.removeClass('visible');
38531             this.indicator.addClass('invisible');
38532         }
38533         if (Roo.bootstrap.version == 3) {
38534             this.el.removeClass(this.invalidClass);
38535             this.el.addClass(this.validClass);
38536         } else {
38537             this.el.removeClass('is-invalid');
38538             this.el.addClass('is-valid');
38539         }
38540         
38541         
38542         this.fireEvent('valid', this);
38543     },
38544     
38545     /**
38546      * Mark this field as invalid
38547      * @param {String} msg The validation message
38548      */
38549     markInvalid : function(msg)
38550     {
38551         if(this.indicator){
38552             this.indicator.removeClass('invisible');
38553             this.indicator.addClass('visible');
38554         }
38555           if (Roo.bootstrap.version == 3) {
38556             this.el.removeClass(this.validClass);
38557             this.el.addClass(this.invalidClass);
38558         } else {
38559             this.el.removeClass('is-valid');
38560             this.el.addClass('is-invalid');
38561         }
38562         
38563         
38564         this.fireEvent('invalid', this, msg);
38565     }
38566     
38567    
38568 });
38569
38570 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38571     
38572     groups: {},
38573     
38574      /**
38575     * register a FieldLabel Group
38576     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38577     */
38578     register : function(label)
38579     {
38580         if(this.groups.hasOwnProperty(label.target)){
38581             return;
38582         }
38583      
38584         this.groups[label.target] = label;
38585         
38586     },
38587     /**
38588     * fetch a FieldLabel Group based on the target
38589     * @param {string} target
38590     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38591     */
38592     get: function(target) {
38593         if (typeof(this.groups[target]) == 'undefined') {
38594             return false;
38595         }
38596         
38597         return this.groups[target] ;
38598     }
38599 });
38600
38601  
38602
38603  /*
38604  * - LGPL
38605  *
38606  * page DateSplitField.
38607  * 
38608  */
38609
38610
38611 /**
38612  * @class Roo.bootstrap.form.DateSplitField
38613  * @extends Roo.bootstrap.Component
38614  * Bootstrap DateSplitField class
38615  * @cfg {string} fieldLabel - the label associated
38616  * @cfg {Number} labelWidth set the width of label (0-12)
38617  * @cfg {String} labelAlign (top|left)
38618  * @cfg {Boolean} dayAllowBlank (true|false) default false
38619  * @cfg {Boolean} monthAllowBlank (true|false) default false
38620  * @cfg {Boolean} yearAllowBlank (true|false) default false
38621  * @cfg {string} dayPlaceholder 
38622  * @cfg {string} monthPlaceholder
38623  * @cfg {string} yearPlaceholder
38624  * @cfg {string} dayFormat default 'd'
38625  * @cfg {string} monthFormat default 'm'
38626  * @cfg {string} yearFormat default 'Y'
38627  * @cfg {Number} labellg set the width of label (1-12)
38628  * @cfg {Number} labelmd set the width of label (1-12)
38629  * @cfg {Number} labelsm set the width of label (1-12)
38630  * @cfg {Number} labelxs set the width of label (1-12)
38631
38632  *     
38633  * @constructor
38634  * Create a new DateSplitField
38635  * @param {Object} config The config object
38636  */
38637
38638 Roo.bootstrap.form.DateSplitField = function(config){
38639     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38640     
38641     this.addEvents({
38642         // raw events
38643          /**
38644          * @event years
38645          * getting the data of years
38646          * @param {Roo.bootstrap.form.DateSplitField} this
38647          * @param {Object} years
38648          */
38649         "years" : true,
38650         /**
38651          * @event days
38652          * getting the data of days
38653          * @param {Roo.bootstrap.form.DateSplitField} this
38654          * @param {Object} days
38655          */
38656         "days" : true,
38657         /**
38658          * @event invalid
38659          * Fires after the field has been marked as invalid.
38660          * @param {Roo.form.Field} this
38661          * @param {String} msg The validation message
38662          */
38663         invalid : true,
38664        /**
38665          * @event valid
38666          * Fires after the field has been validated with no errors.
38667          * @param {Roo.form.Field} this
38668          */
38669         valid : true
38670     });
38671 };
38672
38673 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38674     
38675     fieldLabel : '',
38676     labelAlign : 'top',
38677     labelWidth : 3,
38678     dayAllowBlank : false,
38679     monthAllowBlank : false,
38680     yearAllowBlank : false,
38681     dayPlaceholder : '',
38682     monthPlaceholder : '',
38683     yearPlaceholder : '',
38684     dayFormat : 'd',
38685     monthFormat : 'm',
38686     yearFormat : 'Y',
38687     isFormField : true,
38688     labellg : 0,
38689     labelmd : 0,
38690     labelsm : 0,
38691     labelxs : 0,
38692     
38693     getAutoCreate : function()
38694     {
38695         var cfg = {
38696             tag : 'div',
38697             cls : 'row roo-date-split-field-group',
38698             cn : [
38699                 {
38700                     tag : 'input',
38701                     type : 'hidden',
38702                     cls : 'form-hidden-field roo-date-split-field-group-value',
38703                     name : this.name
38704                 }
38705             ]
38706         };
38707         
38708         var labelCls = 'col-md-12';
38709         var contentCls = 'col-md-4';
38710         
38711         if(this.fieldLabel){
38712             
38713             var label = {
38714                 tag : 'div',
38715                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38716                 cn : [
38717                     {
38718                         tag : 'label',
38719                         html : this.fieldLabel
38720                     }
38721                 ]
38722             };
38723             
38724             if(this.labelAlign == 'left'){
38725             
38726                 if(this.labelWidth > 12){
38727                     label.style = "width: " + this.labelWidth + 'px';
38728                 }
38729
38730                 if(this.labelWidth < 13 && this.labelmd == 0){
38731                     this.labelmd = this.labelWidth;
38732                 }
38733
38734                 if(this.labellg > 0){
38735                     labelCls = ' col-lg-' + this.labellg;
38736                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38737                 }
38738
38739                 if(this.labelmd > 0){
38740                     labelCls = ' col-md-' + this.labelmd;
38741                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38742                 }
38743
38744                 if(this.labelsm > 0){
38745                     labelCls = ' col-sm-' + this.labelsm;
38746                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38747                 }
38748
38749                 if(this.labelxs > 0){
38750                     labelCls = ' col-xs-' + this.labelxs;
38751                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38752                 }
38753             }
38754             
38755             label.cls += ' ' + labelCls;
38756             
38757             cfg.cn.push(label);
38758         }
38759         
38760         Roo.each(['day', 'month', 'year'], function(t){
38761             cfg.cn.push({
38762                 tag : 'div',
38763                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38764             });
38765         }, this);
38766         
38767         return cfg;
38768     },
38769     
38770     inputEl: function ()
38771     {
38772         return this.el.select('.roo-date-split-field-group-value', true).first();
38773     },
38774     
38775     onRender : function(ct, position) 
38776     {
38777         var _this = this;
38778         
38779         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38780         
38781         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38782         
38783         this.dayField = new Roo.bootstrap.form.ComboBox({
38784             allowBlank : this.dayAllowBlank,
38785             alwaysQuery : true,
38786             displayField : 'value',
38787             editable : false,
38788             fieldLabel : '',
38789             forceSelection : true,
38790             mode : 'local',
38791             placeholder : this.dayPlaceholder,
38792             selectOnFocus : true,
38793             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38794             triggerAction : 'all',
38795             typeAhead : true,
38796             valueField : 'value',
38797             store : new Roo.data.SimpleStore({
38798                 data : (function() {    
38799                     var days = [];
38800                     _this.fireEvent('days', _this, days);
38801                     return days;
38802                 })(),
38803                 fields : [ 'value' ]
38804             }),
38805             listeners : {
38806                 select : function (_self, record, index)
38807                 {
38808                     _this.setValue(_this.getValue());
38809                 }
38810             }
38811         });
38812
38813         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38814         
38815         this.monthField = new Roo.bootstrap.form.MonthField({
38816             after : '<i class=\"fa fa-calendar\"></i>',
38817             allowBlank : this.monthAllowBlank,
38818             placeholder : this.monthPlaceholder,
38819             readOnly : true,
38820             listeners : {
38821                 render : function (_self)
38822                 {
38823                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38824                         e.preventDefault();
38825                         _self.focus();
38826                     });
38827                 },
38828                 select : function (_self, oldvalue, newvalue)
38829                 {
38830                     _this.setValue(_this.getValue());
38831                 }
38832             }
38833         });
38834         
38835         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38836         
38837         this.yearField = new Roo.bootstrap.form.ComboBox({
38838             allowBlank : this.yearAllowBlank,
38839             alwaysQuery : true,
38840             displayField : 'value',
38841             editable : false,
38842             fieldLabel : '',
38843             forceSelection : true,
38844             mode : 'local',
38845             placeholder : this.yearPlaceholder,
38846             selectOnFocus : true,
38847             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38848             triggerAction : 'all',
38849             typeAhead : true,
38850             valueField : 'value',
38851             store : new Roo.data.SimpleStore({
38852                 data : (function() {
38853                     var years = [];
38854                     _this.fireEvent('years', _this, years);
38855                     return years;
38856                 })(),
38857                 fields : [ 'value' ]
38858             }),
38859             listeners : {
38860                 select : function (_self, record, index)
38861                 {
38862                     _this.setValue(_this.getValue());
38863                 }
38864             }
38865         });
38866
38867         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38868     },
38869     
38870     setValue : function(v, format)
38871     {
38872         this.inputEl.dom.value = v;
38873         
38874         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38875         
38876         var d = Date.parseDate(v, f);
38877         
38878         if(!d){
38879             this.validate();
38880             return;
38881         }
38882         
38883         this.setDay(d.format(this.dayFormat));
38884         this.setMonth(d.format(this.monthFormat));
38885         this.setYear(d.format(this.yearFormat));
38886         
38887         this.validate();
38888         
38889         return;
38890     },
38891     
38892     setDay : function(v)
38893     {
38894         this.dayField.setValue(v);
38895         this.inputEl.dom.value = this.getValue();
38896         this.validate();
38897         return;
38898     },
38899     
38900     setMonth : function(v)
38901     {
38902         this.monthField.setValue(v, true);
38903         this.inputEl.dom.value = this.getValue();
38904         this.validate();
38905         return;
38906     },
38907     
38908     setYear : function(v)
38909     {
38910         this.yearField.setValue(v);
38911         this.inputEl.dom.value = this.getValue();
38912         this.validate();
38913         return;
38914     },
38915     
38916     getDay : function()
38917     {
38918         return this.dayField.getValue();
38919     },
38920     
38921     getMonth : function()
38922     {
38923         return this.monthField.getValue();
38924     },
38925     
38926     getYear : function()
38927     {
38928         return this.yearField.getValue();
38929     },
38930     
38931     getValue : function()
38932     {
38933         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38934         
38935         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38936         
38937         return date;
38938     },
38939     
38940     reset : function()
38941     {
38942         this.setDay('');
38943         this.setMonth('');
38944         this.setYear('');
38945         this.inputEl.dom.value = '';
38946         this.validate();
38947         return;
38948     },
38949     
38950     validate : function()
38951     {
38952         var d = this.dayField.validate();
38953         var m = this.monthField.validate();
38954         var y = this.yearField.validate();
38955         
38956         var valid = true;
38957         
38958         if(
38959                 (!this.dayAllowBlank && !d) ||
38960                 (!this.monthAllowBlank && !m) ||
38961                 (!this.yearAllowBlank && !y)
38962         ){
38963             valid = false;
38964         }
38965         
38966         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38967             return valid;
38968         }
38969         
38970         if(valid){
38971             this.markValid();
38972             return valid;
38973         }
38974         
38975         this.markInvalid();
38976         
38977         return valid;
38978     },
38979     
38980     markValid : function()
38981     {
38982         
38983         var label = this.el.select('label', true).first();
38984         var icon = this.el.select('i.fa-star', true).first();
38985
38986         if(label && icon){
38987             icon.remove();
38988         }
38989         
38990         this.fireEvent('valid', this);
38991     },
38992     
38993      /**
38994      * Mark this field as invalid
38995      * @param {String} msg The validation message
38996      */
38997     markInvalid : function(msg)
38998     {
38999         
39000         var label = this.el.select('label', true).first();
39001         var icon = this.el.select('i.fa-star', true).first();
39002
39003         if(label && !icon){
39004             this.el.select('.roo-date-split-field-label', true).createChild({
39005                 tag : 'i',
39006                 cls : 'text-danger fa fa-lg fa-star',
39007                 tooltip : 'This field is required',
39008                 style : 'margin-right:5px;'
39009             }, label, true);
39010         }
39011         
39012         this.fireEvent('invalid', this, msg);
39013     },
39014     
39015     clearInvalid : function()
39016     {
39017         var label = this.el.select('label', true).first();
39018         var icon = this.el.select('i.fa-star', true).first();
39019
39020         if(label && icon){
39021             icon.remove();
39022         }
39023         
39024         this.fireEvent('valid', this);
39025     },
39026     
39027     getName: function()
39028     {
39029         return this.name;
39030     }
39031     
39032 });
39033
39034  
39035
39036 /**
39037  * @class Roo.bootstrap.LayoutMasonry
39038  * @extends Roo.bootstrap.Component
39039  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39040  * Bootstrap Layout Masonry class
39041  *
39042  * This is based on 
39043  * http://masonry.desandro.com
39044  *
39045  * The idea is to render all the bricks based on vertical width...
39046  *
39047  * The original code extends 'outlayer' - we might need to use that....
39048
39049  * @constructor
39050  * Create a new Element
39051  * @param {Object} config The config object
39052  */
39053
39054 Roo.bootstrap.LayoutMasonry = function(config){
39055     
39056     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39057     
39058     this.bricks = [];
39059     
39060     Roo.bootstrap.LayoutMasonry.register(this);
39061     
39062     this.addEvents({
39063         // raw events
39064         /**
39065          * @event layout
39066          * Fire after layout the items
39067          * @param {Roo.bootstrap.LayoutMasonry} this
39068          * @param {Roo.EventObject} e
39069          */
39070         "layout" : true
39071     });
39072     
39073 };
39074
39075 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39076     
39077     /**
39078      * @cfg {Boolean} isLayoutInstant = no animation?
39079      */   
39080     isLayoutInstant : false, // needed?
39081    
39082     /**
39083      * @cfg {Number} boxWidth  width of the columns
39084      */   
39085     boxWidth : 450,
39086     
39087       /**
39088      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39089      */   
39090     boxHeight : 0,
39091     
39092     /**
39093      * @cfg {Number} padWidth padding below box..
39094      */   
39095     padWidth : 10, 
39096     
39097     /**
39098      * @cfg {Number} gutter gutter width..
39099      */   
39100     gutter : 10,
39101     
39102      /**
39103      * @cfg {Number} maxCols maximum number of columns
39104      */   
39105     
39106     maxCols: 0,
39107     
39108     /**
39109      * @cfg {Boolean} isAutoInitial defalut true
39110      */   
39111     isAutoInitial : true, 
39112     
39113     containerWidth: 0,
39114     
39115     /**
39116      * @cfg {Boolean} isHorizontal defalut false
39117      */   
39118     isHorizontal : false, 
39119
39120     currentSize : null,
39121     
39122     tag: 'div',
39123     
39124     cls: '',
39125     
39126     bricks: null, //CompositeElement
39127     
39128     cols : 1,
39129     
39130     _isLayoutInited : false,
39131     
39132 //    isAlternative : false, // only use for vertical layout...
39133     
39134     /**
39135      * @cfg {Number} alternativePadWidth padding below box..
39136      */   
39137     alternativePadWidth : 50,
39138     
39139     selectedBrick : [],
39140     
39141     getAutoCreate : function(){
39142         
39143         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39144         
39145         var cfg = {
39146             tag: this.tag,
39147             cls: 'blog-masonary-wrapper ' + this.cls,
39148             cn : {
39149                 cls : 'mas-boxes masonary'
39150             }
39151         };
39152         
39153         return cfg;
39154     },
39155     
39156     getChildContainer: function( )
39157     {
39158         if (this.boxesEl) {
39159             return this.boxesEl;
39160         }
39161         
39162         this.boxesEl = this.el.select('.mas-boxes').first();
39163         
39164         return this.boxesEl;
39165     },
39166     
39167     
39168     initEvents : function()
39169     {
39170         var _this = this;
39171         
39172         if(this.isAutoInitial){
39173             Roo.log('hook children rendered');
39174             this.on('childrenrendered', function() {
39175                 Roo.log('children rendered');
39176                 _this.initial();
39177             } ,this);
39178         }
39179     },
39180     
39181     initial : function()
39182     {
39183         this.selectedBrick = [];
39184         
39185         this.currentSize = this.el.getBox(true);
39186         
39187         Roo.EventManager.onWindowResize(this.resize, this); 
39188
39189         if(!this.isAutoInitial){
39190             this.layout();
39191             return;
39192         }
39193         
39194         this.layout();
39195         
39196         return;
39197         //this.layout.defer(500,this);
39198         
39199     },
39200     
39201     resize : function()
39202     {
39203         var cs = this.el.getBox(true);
39204         
39205         if (
39206                 this.currentSize.width == cs.width && 
39207                 this.currentSize.x == cs.x && 
39208                 this.currentSize.height == cs.height && 
39209                 this.currentSize.y == cs.y 
39210         ) {
39211             Roo.log("no change in with or X or Y");
39212             return;
39213         }
39214         
39215         this.currentSize = cs;
39216         
39217         this.layout();
39218         
39219     },
39220     
39221     layout : function()
39222     {   
39223         this._resetLayout();
39224         
39225         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39226         
39227         this.layoutItems( isInstant );
39228       
39229         this._isLayoutInited = true;
39230         
39231         this.fireEvent('layout', this);
39232         
39233     },
39234     
39235     _resetLayout : function()
39236     {
39237         if(this.isHorizontal){
39238             this.horizontalMeasureColumns();
39239             return;
39240         }
39241         
39242         this.verticalMeasureColumns();
39243         
39244     },
39245     
39246     verticalMeasureColumns : function()
39247     {
39248         this.getContainerWidth();
39249         
39250 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39251 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39252 //            return;
39253 //        }
39254         
39255         var boxWidth = this.boxWidth + this.padWidth;
39256         
39257         if(this.containerWidth < this.boxWidth){
39258             boxWidth = this.containerWidth
39259         }
39260         
39261         var containerWidth = this.containerWidth;
39262         
39263         var cols = Math.floor(containerWidth / boxWidth);
39264         
39265         this.cols = Math.max( cols, 1 );
39266         
39267         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39268         
39269         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39270         
39271         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39272         
39273         this.colWidth = boxWidth + avail - this.padWidth;
39274         
39275         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39276         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39277     },
39278     
39279     horizontalMeasureColumns : function()
39280     {
39281         this.getContainerWidth();
39282         
39283         var boxWidth = this.boxWidth;
39284         
39285         if(this.containerWidth < boxWidth){
39286             boxWidth = this.containerWidth;
39287         }
39288         
39289         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39290         
39291         this.el.setHeight(boxWidth);
39292         
39293     },
39294     
39295     getContainerWidth : function()
39296     {
39297         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39298     },
39299     
39300     layoutItems : function( isInstant )
39301     {
39302         Roo.log(this.bricks);
39303         
39304         var items = Roo.apply([], this.bricks);
39305         
39306         if(this.isHorizontal){
39307             this._horizontalLayoutItems( items , isInstant );
39308             return;
39309         }
39310         
39311 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39312 //            this._verticalAlternativeLayoutItems( items , isInstant );
39313 //            return;
39314 //        }
39315         
39316         this._verticalLayoutItems( items , isInstant );
39317         
39318     },
39319     
39320     _verticalLayoutItems : function ( items , isInstant)
39321     {
39322         if ( !items || !items.length ) {
39323             return;
39324         }
39325         
39326         var standard = [
39327             ['xs', 'xs', 'xs', 'tall'],
39328             ['xs', 'xs', 'tall'],
39329             ['xs', 'xs', 'sm'],
39330             ['xs', 'xs', 'xs'],
39331             ['xs', 'tall'],
39332             ['xs', 'sm'],
39333             ['xs', 'xs'],
39334             ['xs'],
39335             
39336             ['sm', 'xs', 'xs'],
39337             ['sm', 'xs'],
39338             ['sm'],
39339             
39340             ['tall', 'xs', 'xs', 'xs'],
39341             ['tall', 'xs', 'xs'],
39342             ['tall', 'xs'],
39343             ['tall']
39344             
39345         ];
39346         
39347         var queue = [];
39348         
39349         var boxes = [];
39350         
39351         var box = [];
39352         
39353         Roo.each(items, function(item, k){
39354             
39355             switch (item.size) {
39356                 // these layouts take up a full box,
39357                 case 'md' :
39358                 case 'md-left' :
39359                 case 'md-right' :
39360                 case 'wide' :
39361                     
39362                     if(box.length){
39363                         boxes.push(box);
39364                         box = [];
39365                     }
39366                     
39367                     boxes.push([item]);
39368                     
39369                     break;
39370                     
39371                 case 'xs' :
39372                 case 'sm' :
39373                 case 'tall' :
39374                     
39375                     box.push(item);
39376                     
39377                     break;
39378                 default :
39379                     break;
39380                     
39381             }
39382             
39383         }, this);
39384         
39385         if(box.length){
39386             boxes.push(box);
39387             box = [];
39388         }
39389         
39390         var filterPattern = function(box, length)
39391         {
39392             if(!box.length){
39393                 return;
39394             }
39395             
39396             var match = false;
39397             
39398             var pattern = box.slice(0, length);
39399             
39400             var format = [];
39401             
39402             Roo.each(pattern, function(i){
39403                 format.push(i.size);
39404             }, this);
39405             
39406             Roo.each(standard, function(s){
39407                 
39408                 if(String(s) != String(format)){
39409                     return;
39410                 }
39411                 
39412                 match = true;
39413                 return false;
39414                 
39415             }, this);
39416             
39417             if(!match && length == 1){
39418                 return;
39419             }
39420             
39421             if(!match){
39422                 filterPattern(box, length - 1);
39423                 return;
39424             }
39425                 
39426             queue.push(pattern);
39427
39428             box = box.slice(length, box.length);
39429
39430             filterPattern(box, 4);
39431
39432             return;
39433             
39434         }
39435         
39436         Roo.each(boxes, function(box, k){
39437             
39438             if(!box.length){
39439                 return;
39440             }
39441             
39442             if(box.length == 1){
39443                 queue.push(box);
39444                 return;
39445             }
39446             
39447             filterPattern(box, 4);
39448             
39449         }, this);
39450         
39451         this._processVerticalLayoutQueue( queue, isInstant );
39452         
39453     },
39454     
39455 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39456 //    {
39457 //        if ( !items || !items.length ) {
39458 //            return;
39459 //        }
39460 //
39461 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39462 //        
39463 //    },
39464     
39465     _horizontalLayoutItems : function ( items , isInstant)
39466     {
39467         if ( !items || !items.length || items.length < 3) {
39468             return;
39469         }
39470         
39471         items.reverse();
39472         
39473         var eItems = items.slice(0, 3);
39474         
39475         items = items.slice(3, items.length);
39476         
39477         var standard = [
39478             ['xs', 'xs', 'xs', 'wide'],
39479             ['xs', 'xs', 'wide'],
39480             ['xs', 'xs', 'sm'],
39481             ['xs', 'xs', 'xs'],
39482             ['xs', 'wide'],
39483             ['xs', 'sm'],
39484             ['xs', 'xs'],
39485             ['xs'],
39486             
39487             ['sm', 'xs', 'xs'],
39488             ['sm', 'xs'],
39489             ['sm'],
39490             
39491             ['wide', 'xs', 'xs', 'xs'],
39492             ['wide', 'xs', 'xs'],
39493             ['wide', 'xs'],
39494             ['wide'],
39495             
39496             ['wide-thin']
39497         ];
39498         
39499         var queue = [];
39500         
39501         var boxes = [];
39502         
39503         var box = [];
39504         
39505         Roo.each(items, function(item, k){
39506             
39507             switch (item.size) {
39508                 case 'md' :
39509                 case 'md-left' :
39510                 case 'md-right' :
39511                 case 'tall' :
39512                     
39513                     if(box.length){
39514                         boxes.push(box);
39515                         box = [];
39516                     }
39517                     
39518                     boxes.push([item]);
39519                     
39520                     break;
39521                     
39522                 case 'xs' :
39523                 case 'sm' :
39524                 case 'wide' :
39525                 case 'wide-thin' :
39526                     
39527                     box.push(item);
39528                     
39529                     break;
39530                 default :
39531                     break;
39532                     
39533             }
39534             
39535         }, this);
39536         
39537         if(box.length){
39538             boxes.push(box);
39539             box = [];
39540         }
39541         
39542         var filterPattern = function(box, length)
39543         {
39544             if(!box.length){
39545                 return;
39546             }
39547             
39548             var match = false;
39549             
39550             var pattern = box.slice(0, length);
39551             
39552             var format = [];
39553             
39554             Roo.each(pattern, function(i){
39555                 format.push(i.size);
39556             }, this);
39557             
39558             Roo.each(standard, function(s){
39559                 
39560                 if(String(s) != String(format)){
39561                     return;
39562                 }
39563                 
39564                 match = true;
39565                 return false;
39566                 
39567             }, this);
39568             
39569             if(!match && length == 1){
39570                 return;
39571             }
39572             
39573             if(!match){
39574                 filterPattern(box, length - 1);
39575                 return;
39576             }
39577                 
39578             queue.push(pattern);
39579
39580             box = box.slice(length, box.length);
39581
39582             filterPattern(box, 4);
39583
39584             return;
39585             
39586         }
39587         
39588         Roo.each(boxes, function(box, k){
39589             
39590             if(!box.length){
39591                 return;
39592             }
39593             
39594             if(box.length == 1){
39595                 queue.push(box);
39596                 return;
39597             }
39598             
39599             filterPattern(box, 4);
39600             
39601         }, this);
39602         
39603         
39604         var prune = [];
39605         
39606         var pos = this.el.getBox(true);
39607         
39608         var minX = pos.x;
39609         
39610         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39611         
39612         var hit_end = false;
39613         
39614         Roo.each(queue, function(box){
39615             
39616             if(hit_end){
39617                 
39618                 Roo.each(box, function(b){
39619                 
39620                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39621                     b.el.hide();
39622
39623                 }, this);
39624
39625                 return;
39626             }
39627             
39628             var mx = 0;
39629             
39630             Roo.each(box, function(b){
39631                 
39632                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39633                 b.el.show();
39634
39635                 mx = Math.max(mx, b.x);
39636                 
39637             }, this);
39638             
39639             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39640             
39641             if(maxX < minX){
39642                 
39643                 Roo.each(box, function(b){
39644                 
39645                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39646                     b.el.hide();
39647                     
39648                 }, this);
39649                 
39650                 hit_end = true;
39651                 
39652                 return;
39653             }
39654             
39655             prune.push(box);
39656             
39657         }, this);
39658         
39659         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39660     },
39661     
39662     /** Sets position of item in DOM
39663     * @param {Element} item
39664     * @param {Number} x - horizontal position
39665     * @param {Number} y - vertical position
39666     * @param {Boolean} isInstant - disables transitions
39667     */
39668     _processVerticalLayoutQueue : function( queue, isInstant )
39669     {
39670         var pos = this.el.getBox(true);
39671         var x = pos.x;
39672         var y = pos.y;
39673         var maxY = [];
39674         
39675         for (var i = 0; i < this.cols; i++){
39676             maxY[i] = pos.y;
39677         }
39678         
39679         Roo.each(queue, function(box, k){
39680             
39681             var col = k % this.cols;
39682             
39683             Roo.each(box, function(b,kk){
39684                 
39685                 b.el.position('absolute');
39686                 
39687                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39688                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39689                 
39690                 if(b.size == 'md-left' || b.size == 'md-right'){
39691                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39692                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39693                 }
39694                 
39695                 b.el.setWidth(width);
39696                 b.el.setHeight(height);
39697                 // iframe?
39698                 b.el.select('iframe',true).setSize(width,height);
39699                 
39700             }, this);
39701             
39702             for (var i = 0; i < this.cols; i++){
39703                 
39704                 if(maxY[i] < maxY[col]){
39705                     col = i;
39706                     continue;
39707                 }
39708                 
39709                 col = Math.min(col, i);
39710                 
39711             }
39712             
39713             x = pos.x + col * (this.colWidth + this.padWidth);
39714             
39715             y = maxY[col];
39716             
39717             var positions = [];
39718             
39719             switch (box.length){
39720                 case 1 :
39721                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39722                     break;
39723                 case 2 :
39724                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39725                     break;
39726                 case 3 :
39727                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39728                     break;
39729                 case 4 :
39730                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39731                     break;
39732                 default :
39733                     break;
39734             }
39735             
39736             Roo.each(box, function(b,kk){
39737                 
39738                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39739                 
39740                 var sz = b.el.getSize();
39741                 
39742                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39743                 
39744             }, this);
39745             
39746         }, this);
39747         
39748         var mY = 0;
39749         
39750         for (var i = 0; i < this.cols; i++){
39751             mY = Math.max(mY, maxY[i]);
39752         }
39753         
39754         this.el.setHeight(mY - pos.y);
39755         
39756     },
39757     
39758 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39759 //    {
39760 //        var pos = this.el.getBox(true);
39761 //        var x = pos.x;
39762 //        var y = pos.y;
39763 //        var maxX = pos.right;
39764 //        
39765 //        var maxHeight = 0;
39766 //        
39767 //        Roo.each(items, function(item, k){
39768 //            
39769 //            var c = k % 2;
39770 //            
39771 //            item.el.position('absolute');
39772 //                
39773 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39774 //
39775 //            item.el.setWidth(width);
39776 //
39777 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39778 //
39779 //            item.el.setHeight(height);
39780 //            
39781 //            if(c == 0){
39782 //                item.el.setXY([x, y], isInstant ? false : true);
39783 //            } else {
39784 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39785 //            }
39786 //            
39787 //            y = y + height + this.alternativePadWidth;
39788 //            
39789 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39790 //            
39791 //        }, this);
39792 //        
39793 //        this.el.setHeight(maxHeight);
39794 //        
39795 //    },
39796     
39797     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39798     {
39799         var pos = this.el.getBox(true);
39800         
39801         var minX = pos.x;
39802         var minY = pos.y;
39803         
39804         var maxX = pos.right;
39805         
39806         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39807         
39808         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39809         
39810         Roo.each(queue, function(box, k){
39811             
39812             Roo.each(box, function(b, kk){
39813                 
39814                 b.el.position('absolute');
39815                 
39816                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39817                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39818                 
39819                 if(b.size == 'md-left' || b.size == 'md-right'){
39820                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39821                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39822                 }
39823                 
39824                 b.el.setWidth(width);
39825                 b.el.setHeight(height);
39826                 
39827             }, this);
39828             
39829             if(!box.length){
39830                 return;
39831             }
39832             
39833             var positions = [];
39834             
39835             switch (box.length){
39836                 case 1 :
39837                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39838                     break;
39839                 case 2 :
39840                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39841                     break;
39842                 case 3 :
39843                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39844                     break;
39845                 case 4 :
39846                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39847                     break;
39848                 default :
39849                     break;
39850             }
39851             
39852             Roo.each(box, function(b,kk){
39853                 
39854                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39855                 
39856                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39857                 
39858             }, this);
39859             
39860         }, this);
39861         
39862     },
39863     
39864     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39865     {
39866         Roo.each(eItems, function(b,k){
39867             
39868             b.size = (k == 0) ? 'sm' : 'xs';
39869             b.x = (k == 0) ? 2 : 1;
39870             b.y = (k == 0) ? 2 : 1;
39871             
39872             b.el.position('absolute');
39873             
39874             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39875                 
39876             b.el.setWidth(width);
39877             
39878             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39879             
39880             b.el.setHeight(height);
39881             
39882         }, this);
39883
39884         var positions = [];
39885         
39886         positions.push({
39887             x : maxX - this.unitWidth * 2 - this.gutter,
39888             y : minY
39889         });
39890         
39891         positions.push({
39892             x : maxX - this.unitWidth,
39893             y : minY + (this.unitWidth + this.gutter) * 2
39894         });
39895         
39896         positions.push({
39897             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39898             y : minY
39899         });
39900         
39901         Roo.each(eItems, function(b,k){
39902             
39903             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39904
39905         }, this);
39906         
39907     },
39908     
39909     getVerticalOneBoxColPositions : function(x, y, box)
39910     {
39911         var pos = [];
39912         
39913         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39914         
39915         if(box[0].size == 'md-left'){
39916             rand = 0;
39917         }
39918         
39919         if(box[0].size == 'md-right'){
39920             rand = 1;
39921         }
39922         
39923         pos.push({
39924             x : x + (this.unitWidth + this.gutter) * rand,
39925             y : y
39926         });
39927         
39928         return pos;
39929     },
39930     
39931     getVerticalTwoBoxColPositions : function(x, y, box)
39932     {
39933         var pos = [];
39934         
39935         if(box[0].size == 'xs'){
39936             
39937             pos.push({
39938                 x : x,
39939                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39940             });
39941
39942             pos.push({
39943                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39944                 y : y
39945             });
39946             
39947             return pos;
39948             
39949         }
39950         
39951         pos.push({
39952             x : x,
39953             y : y
39954         });
39955
39956         pos.push({
39957             x : x + (this.unitWidth + this.gutter) * 2,
39958             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39959         });
39960         
39961         return pos;
39962         
39963     },
39964     
39965     getVerticalThreeBoxColPositions : function(x, y, box)
39966     {
39967         var pos = [];
39968         
39969         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39970             
39971             pos.push({
39972                 x : x,
39973                 y : y
39974             });
39975
39976             pos.push({
39977                 x : x + (this.unitWidth + this.gutter) * 1,
39978                 y : y
39979             });
39980             
39981             pos.push({
39982                 x : x + (this.unitWidth + this.gutter) * 2,
39983                 y : y
39984             });
39985             
39986             return pos;
39987             
39988         }
39989         
39990         if(box[0].size == 'xs' && box[1].size == 'xs'){
39991             
39992             pos.push({
39993                 x : x,
39994                 y : y
39995             });
39996
39997             pos.push({
39998                 x : x,
39999                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40000             });
40001             
40002             pos.push({
40003                 x : x + (this.unitWidth + this.gutter) * 1,
40004                 y : y
40005             });
40006             
40007             return pos;
40008             
40009         }
40010         
40011         pos.push({
40012             x : x,
40013             y : y
40014         });
40015
40016         pos.push({
40017             x : x + (this.unitWidth + this.gutter) * 2,
40018             y : y
40019         });
40020
40021         pos.push({
40022             x : x + (this.unitWidth + this.gutter) * 2,
40023             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40024         });
40025             
40026         return pos;
40027         
40028     },
40029     
40030     getVerticalFourBoxColPositions : function(x, y, box)
40031     {
40032         var pos = [];
40033         
40034         if(box[0].size == 'xs'){
40035             
40036             pos.push({
40037                 x : x,
40038                 y : y
40039             });
40040
40041             pos.push({
40042                 x : x,
40043                 y : y + (this.unitHeight + this.gutter) * 1
40044             });
40045             
40046             pos.push({
40047                 x : x,
40048                 y : y + (this.unitHeight + this.gutter) * 2
40049             });
40050             
40051             pos.push({
40052                 x : x + (this.unitWidth + this.gutter) * 1,
40053                 y : y
40054             });
40055             
40056             return pos;
40057             
40058         }
40059         
40060         pos.push({
40061             x : x,
40062             y : y
40063         });
40064
40065         pos.push({
40066             x : x + (this.unitWidth + this.gutter) * 2,
40067             y : y
40068         });
40069
40070         pos.push({
40071             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40072             y : y + (this.unitHeight + this.gutter) * 1
40073         });
40074
40075         pos.push({
40076             x : x + (this.unitWidth + this.gutter) * 2,
40077             y : y + (this.unitWidth + this.gutter) * 2
40078         });
40079
40080         return pos;
40081         
40082     },
40083     
40084     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40085     {
40086         var pos = [];
40087         
40088         if(box[0].size == 'md-left'){
40089             pos.push({
40090                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40091                 y : minY
40092             });
40093             
40094             return pos;
40095         }
40096         
40097         if(box[0].size == 'md-right'){
40098             pos.push({
40099                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40100                 y : minY + (this.unitWidth + this.gutter) * 1
40101             });
40102             
40103             return pos;
40104         }
40105         
40106         var rand = Math.floor(Math.random() * (4 - box[0].y));
40107         
40108         pos.push({
40109             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40110             y : minY + (this.unitWidth + this.gutter) * rand
40111         });
40112         
40113         return pos;
40114         
40115     },
40116     
40117     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40118     {
40119         var pos = [];
40120         
40121         if(box[0].size == 'xs'){
40122             
40123             pos.push({
40124                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40125                 y : minY
40126             });
40127
40128             pos.push({
40129                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40130                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40131             });
40132             
40133             return pos;
40134             
40135         }
40136         
40137         pos.push({
40138             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40139             y : minY
40140         });
40141
40142         pos.push({
40143             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40144             y : minY + (this.unitWidth + this.gutter) * 2
40145         });
40146         
40147         return pos;
40148         
40149     },
40150     
40151     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40152     {
40153         var pos = [];
40154         
40155         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40156             
40157             pos.push({
40158                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40159                 y : minY
40160             });
40161
40162             pos.push({
40163                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40164                 y : minY + (this.unitWidth + this.gutter) * 1
40165             });
40166             
40167             pos.push({
40168                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40169                 y : minY + (this.unitWidth + this.gutter) * 2
40170             });
40171             
40172             return pos;
40173             
40174         }
40175         
40176         if(box[0].size == 'xs' && box[1].size == 'xs'){
40177             
40178             pos.push({
40179                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40180                 y : minY
40181             });
40182
40183             pos.push({
40184                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40185                 y : minY
40186             });
40187             
40188             pos.push({
40189                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40190                 y : minY + (this.unitWidth + this.gutter) * 1
40191             });
40192             
40193             return pos;
40194             
40195         }
40196         
40197         pos.push({
40198             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40199             y : minY
40200         });
40201
40202         pos.push({
40203             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40204             y : minY + (this.unitWidth + this.gutter) * 2
40205         });
40206
40207         pos.push({
40208             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40209             y : minY + (this.unitWidth + this.gutter) * 2
40210         });
40211             
40212         return pos;
40213         
40214     },
40215     
40216     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40217     {
40218         var pos = [];
40219         
40220         if(box[0].size == 'xs'){
40221             
40222             pos.push({
40223                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40224                 y : minY
40225             });
40226
40227             pos.push({
40228                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40229                 y : minY
40230             });
40231             
40232             pos.push({
40233                 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),
40234                 y : minY
40235             });
40236             
40237             pos.push({
40238                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40239                 y : minY + (this.unitWidth + this.gutter) * 1
40240             });
40241             
40242             return pos;
40243             
40244         }
40245         
40246         pos.push({
40247             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40248             y : minY
40249         });
40250         
40251         pos.push({
40252             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40253             y : minY + (this.unitWidth + this.gutter) * 2
40254         });
40255         
40256         pos.push({
40257             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40258             y : minY + (this.unitWidth + this.gutter) * 2
40259         });
40260         
40261         pos.push({
40262             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),
40263             y : minY + (this.unitWidth + this.gutter) * 2
40264         });
40265
40266         return pos;
40267         
40268     },
40269     
40270     /**
40271     * remove a Masonry Brick
40272     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40273     */
40274     removeBrick : function(brick_id)
40275     {
40276         if (!brick_id) {
40277             return;
40278         }
40279         
40280         for (var i = 0; i<this.bricks.length; i++) {
40281             if (this.bricks[i].id == brick_id) {
40282                 this.bricks.splice(i,1);
40283                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40284                 this.initial();
40285             }
40286         }
40287     },
40288     
40289     /**
40290     * adds a Masonry Brick
40291     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40292     */
40293     addBrick : function(cfg)
40294     {
40295         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40296         //this.register(cn);
40297         cn.parentId = this.id;
40298         cn.render(this.el);
40299         return cn;
40300     },
40301     
40302     /**
40303     * register a Masonry Brick
40304     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40305     */
40306     
40307     register : function(brick)
40308     {
40309         this.bricks.push(brick);
40310         brick.masonryId = this.id;
40311     },
40312     
40313     /**
40314     * clear all the Masonry Brick
40315     */
40316     clearAll : function()
40317     {
40318         this.bricks = [];
40319         //this.getChildContainer().dom.innerHTML = "";
40320         this.el.dom.innerHTML = '';
40321     },
40322     
40323     getSelected : function()
40324     {
40325         if (!this.selectedBrick) {
40326             return false;
40327         }
40328         
40329         return this.selectedBrick;
40330     }
40331 });
40332
40333 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40334     
40335     groups: {},
40336      /**
40337     * register a Masonry Layout
40338     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40339     */
40340     
40341     register : function(layout)
40342     {
40343         this.groups[layout.id] = layout;
40344     },
40345     /**
40346     * fetch a  Masonry Layout based on the masonry layout ID
40347     * @param {string} the masonry layout to add
40348     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40349     */
40350     
40351     get: function(layout_id) {
40352         if (typeof(this.groups[layout_id]) == 'undefined') {
40353             return false;
40354         }
40355         return this.groups[layout_id] ;
40356     }
40357     
40358     
40359     
40360 });
40361
40362  
40363
40364  /**
40365  *
40366  * This is based on 
40367  * http://masonry.desandro.com
40368  *
40369  * The idea is to render all the bricks based on vertical width...
40370  *
40371  * The original code extends 'outlayer' - we might need to use that....
40372  * 
40373  */
40374
40375
40376 /**
40377  * @class Roo.bootstrap.LayoutMasonryAuto
40378  * @extends Roo.bootstrap.Component
40379  * Bootstrap Layout Masonry class
40380  * 
40381  * @constructor
40382  * Create a new Element
40383  * @param {Object} config The config object
40384  */
40385
40386 Roo.bootstrap.LayoutMasonryAuto = function(config){
40387     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40388 };
40389
40390 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40391     
40392       /**
40393      * @cfg {Boolean} isFitWidth  - resize the width..
40394      */   
40395     isFitWidth : false,  // options..
40396     /**
40397      * @cfg {Boolean} isOriginLeft = left align?
40398      */   
40399     isOriginLeft : true,
40400     /**
40401      * @cfg {Boolean} isOriginTop = top align?
40402      */   
40403     isOriginTop : false,
40404     /**
40405      * @cfg {Boolean} isLayoutInstant = no animation?
40406      */   
40407     isLayoutInstant : false, // needed?
40408     /**
40409      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40410      */   
40411     isResizingContainer : true,
40412     /**
40413      * @cfg {Number} columnWidth  width of the columns 
40414      */   
40415     
40416     columnWidth : 0,
40417     
40418     /**
40419      * @cfg {Number} maxCols maximum number of columns
40420      */   
40421     
40422     maxCols: 0,
40423     /**
40424      * @cfg {Number} padHeight padding below box..
40425      */   
40426     
40427     padHeight : 10, 
40428     
40429     /**
40430      * @cfg {Boolean} isAutoInitial defalut true
40431      */   
40432     
40433     isAutoInitial : true, 
40434     
40435     // private?
40436     gutter : 0,
40437     
40438     containerWidth: 0,
40439     initialColumnWidth : 0,
40440     currentSize : null,
40441     
40442     colYs : null, // array.
40443     maxY : 0,
40444     padWidth: 10,
40445     
40446     
40447     tag: 'div',
40448     cls: '',
40449     bricks: null, //CompositeElement
40450     cols : 0, // array?
40451     // element : null, // wrapped now this.el
40452     _isLayoutInited : null, 
40453     
40454     
40455     getAutoCreate : function(){
40456         
40457         var cfg = {
40458             tag: this.tag,
40459             cls: 'blog-masonary-wrapper ' + this.cls,
40460             cn : {
40461                 cls : 'mas-boxes masonary'
40462             }
40463         };
40464         
40465         return cfg;
40466     },
40467     
40468     getChildContainer: function( )
40469     {
40470         if (this.boxesEl) {
40471             return this.boxesEl;
40472         }
40473         
40474         this.boxesEl = this.el.select('.mas-boxes').first();
40475         
40476         return this.boxesEl;
40477     },
40478     
40479     
40480     initEvents : function()
40481     {
40482         var _this = this;
40483         
40484         if(this.isAutoInitial){
40485             Roo.log('hook children rendered');
40486             this.on('childrenrendered', function() {
40487                 Roo.log('children rendered');
40488                 _this.initial();
40489             } ,this);
40490         }
40491         
40492     },
40493     
40494     initial : function()
40495     {
40496         this.reloadItems();
40497
40498         this.currentSize = this.el.getBox(true);
40499
40500         /// was window resize... - let's see if this works..
40501         Roo.EventManager.onWindowResize(this.resize, this); 
40502
40503         if(!this.isAutoInitial){
40504             this.layout();
40505             return;
40506         }
40507         
40508         this.layout.defer(500,this);
40509     },
40510     
40511     reloadItems: function()
40512     {
40513         this.bricks = this.el.select('.masonry-brick', true);
40514         
40515         this.bricks.each(function(b) {
40516             //Roo.log(b.getSize());
40517             if (!b.attr('originalwidth')) {
40518                 b.attr('originalwidth',  b.getSize().width);
40519             }
40520             
40521         });
40522         
40523         Roo.log(this.bricks.elements.length);
40524     },
40525     
40526     resize : function()
40527     {
40528         Roo.log('resize');
40529         var cs = this.el.getBox(true);
40530         
40531         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40532             Roo.log("no change in with or X");
40533             return;
40534         }
40535         this.currentSize = cs;
40536         this.layout();
40537     },
40538     
40539     layout : function()
40540     {
40541          Roo.log('layout');
40542         this._resetLayout();
40543         //this._manageStamps();
40544       
40545         // don't animate first layout
40546         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40547         this.layoutItems( isInstant );
40548       
40549         // flag for initalized
40550         this._isLayoutInited = true;
40551     },
40552     
40553     layoutItems : function( isInstant )
40554     {
40555         //var items = this._getItemsForLayout( this.items );
40556         // original code supports filtering layout items.. we just ignore it..
40557         
40558         this._layoutItems( this.bricks , isInstant );
40559       
40560         this._postLayout();
40561     },
40562     _layoutItems : function ( items , isInstant)
40563     {
40564        //this.fireEvent( 'layout', this, items );
40565     
40566
40567         if ( !items || !items.elements.length ) {
40568           // no items, emit event with empty array
40569             return;
40570         }
40571
40572         var queue = [];
40573         items.each(function(item) {
40574             Roo.log("layout item");
40575             Roo.log(item);
40576             // get x/y object from method
40577             var position = this._getItemLayoutPosition( item );
40578             // enqueue
40579             position.item = item;
40580             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40581             queue.push( position );
40582         }, this);
40583       
40584         this._processLayoutQueue( queue );
40585     },
40586     /** Sets position of item in DOM
40587     * @param {Element} item
40588     * @param {Number} x - horizontal position
40589     * @param {Number} y - vertical position
40590     * @param {Boolean} isInstant - disables transitions
40591     */
40592     _processLayoutQueue : function( queue )
40593     {
40594         for ( var i=0, len = queue.length; i < len; i++ ) {
40595             var obj = queue[i];
40596             obj.item.position('absolute');
40597             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40598         }
40599     },
40600       
40601     
40602     /**
40603     * Any logic you want to do after each layout,
40604     * i.e. size the container
40605     */
40606     _postLayout : function()
40607     {
40608         this.resizeContainer();
40609     },
40610     
40611     resizeContainer : function()
40612     {
40613         if ( !this.isResizingContainer ) {
40614             return;
40615         }
40616         var size = this._getContainerSize();
40617         if ( size ) {
40618             this.el.setSize(size.width,size.height);
40619             this.boxesEl.setSize(size.width,size.height);
40620         }
40621     },
40622     
40623     
40624     
40625     _resetLayout : function()
40626     {
40627         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40628         this.colWidth = this.el.getWidth();
40629         //this.gutter = this.el.getWidth(); 
40630         
40631         this.measureColumns();
40632
40633         // reset column Y
40634         var i = this.cols;
40635         this.colYs = [];
40636         while (i--) {
40637             this.colYs.push( 0 );
40638         }
40639     
40640         this.maxY = 0;
40641     },
40642
40643     measureColumns : function()
40644     {
40645         this.getContainerWidth();
40646       // if columnWidth is 0, default to outerWidth of first item
40647         if ( !this.columnWidth ) {
40648             var firstItem = this.bricks.first();
40649             Roo.log(firstItem);
40650             this.columnWidth  = this.containerWidth;
40651             if (firstItem && firstItem.attr('originalwidth') ) {
40652                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40653             }
40654             // columnWidth fall back to item of first element
40655             Roo.log("set column width?");
40656                         this.initialColumnWidth = this.columnWidth  ;
40657
40658             // if first elem has no width, default to size of container
40659             
40660         }
40661         
40662         
40663         if (this.initialColumnWidth) {
40664             this.columnWidth = this.initialColumnWidth;
40665         }
40666         
40667         
40668             
40669         // column width is fixed at the top - however if container width get's smaller we should
40670         // reduce it...
40671         
40672         // this bit calcs how man columns..
40673             
40674         var columnWidth = this.columnWidth += this.gutter;
40675       
40676         // calculate columns
40677         var containerWidth = this.containerWidth + this.gutter;
40678         
40679         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40680         // fix rounding errors, typically with gutters
40681         var excess = columnWidth - containerWidth % columnWidth;
40682         
40683         
40684         // if overshoot is less than a pixel, round up, otherwise floor it
40685         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40686         cols = Math[ mathMethod ]( cols );
40687         this.cols = Math.max( cols, 1 );
40688         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40689         
40690          // padding positioning..
40691         var totalColWidth = this.cols * this.columnWidth;
40692         var padavail = this.containerWidth - totalColWidth;
40693         // so for 2 columns - we need 3 'pads'
40694         
40695         var padNeeded = (1+this.cols) * this.padWidth;
40696         
40697         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40698         
40699         this.columnWidth += padExtra
40700         //this.padWidth = Math.floor(padavail /  ( this.cols));
40701         
40702         // adjust colum width so that padding is fixed??
40703         
40704         // we have 3 columns ... total = width * 3
40705         // we have X left over... that should be used by 
40706         
40707         //if (this.expandC) {
40708             
40709         //}
40710         
40711         
40712         
40713     },
40714     
40715     getContainerWidth : function()
40716     {
40717        /* // container is parent if fit width
40718         var container = this.isFitWidth ? this.element.parentNode : this.element;
40719         // check that this.size and size are there
40720         // IE8 triggers resize on body size change, so they might not be
40721         
40722         var size = getSize( container );  //FIXME
40723         this.containerWidth = size && size.innerWidth; //FIXME
40724         */
40725          
40726         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40727         
40728     },
40729     
40730     _getItemLayoutPosition : function( item )  // what is item?
40731     {
40732         // we resize the item to our columnWidth..
40733       
40734         item.setWidth(this.columnWidth);
40735         item.autoBoxAdjust  = false;
40736         
40737         var sz = item.getSize();
40738  
40739         // how many columns does this brick span
40740         var remainder = this.containerWidth % this.columnWidth;
40741         
40742         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40743         // round if off by 1 pixel, otherwise use ceil
40744         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40745         colSpan = Math.min( colSpan, this.cols );
40746         
40747         // normally this should be '1' as we dont' currently allow multi width columns..
40748         
40749         var colGroup = this._getColGroup( colSpan );
40750         // get the minimum Y value from the columns
40751         var minimumY = Math.min.apply( Math, colGroup );
40752         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40753         
40754         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40755          
40756         // position the brick
40757         var position = {
40758             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40759             y: this.currentSize.y + minimumY + this.padHeight
40760         };
40761         
40762         Roo.log(position);
40763         // apply setHeight to necessary columns
40764         var setHeight = minimumY + sz.height + this.padHeight;
40765         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40766         
40767         var setSpan = this.cols + 1 - colGroup.length;
40768         for ( var i = 0; i < setSpan; i++ ) {
40769           this.colYs[ shortColIndex + i ] = setHeight ;
40770         }
40771       
40772         return position;
40773     },
40774     
40775     /**
40776      * @param {Number} colSpan - number of columns the element spans
40777      * @returns {Array} colGroup
40778      */
40779     _getColGroup : function( colSpan )
40780     {
40781         if ( colSpan < 2 ) {
40782           // if brick spans only one column, use all the column Ys
40783           return this.colYs;
40784         }
40785       
40786         var colGroup = [];
40787         // how many different places could this brick fit horizontally
40788         var groupCount = this.cols + 1 - colSpan;
40789         // for each group potential horizontal position
40790         for ( var i = 0; i < groupCount; i++ ) {
40791           // make an array of colY values for that one group
40792           var groupColYs = this.colYs.slice( i, i + colSpan );
40793           // and get the max value of the array
40794           colGroup[i] = Math.max.apply( Math, groupColYs );
40795         }
40796         return colGroup;
40797     },
40798     /*
40799     _manageStamp : function( stamp )
40800     {
40801         var stampSize =  stamp.getSize();
40802         var offset = stamp.getBox();
40803         // get the columns that this stamp affects
40804         var firstX = this.isOriginLeft ? offset.x : offset.right;
40805         var lastX = firstX + stampSize.width;
40806         var firstCol = Math.floor( firstX / this.columnWidth );
40807         firstCol = Math.max( 0, firstCol );
40808         
40809         var lastCol = Math.floor( lastX / this.columnWidth );
40810         // lastCol should not go over if multiple of columnWidth #425
40811         lastCol -= lastX % this.columnWidth ? 0 : 1;
40812         lastCol = Math.min( this.cols - 1, lastCol );
40813         
40814         // set colYs to bottom of the stamp
40815         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40816             stampSize.height;
40817             
40818         for ( var i = firstCol; i <= lastCol; i++ ) {
40819           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40820         }
40821     },
40822     */
40823     
40824     _getContainerSize : function()
40825     {
40826         this.maxY = Math.max.apply( Math, this.colYs );
40827         var size = {
40828             height: this.maxY
40829         };
40830       
40831         if ( this.isFitWidth ) {
40832             size.width = this._getContainerFitWidth();
40833         }
40834       
40835         return size;
40836     },
40837     
40838     _getContainerFitWidth : function()
40839     {
40840         var unusedCols = 0;
40841         // count unused columns
40842         var i = this.cols;
40843         while ( --i ) {
40844           if ( this.colYs[i] !== 0 ) {
40845             break;
40846           }
40847           unusedCols++;
40848         }
40849         // fit container to columns that have been used
40850         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40851     },
40852     
40853     needsResizeLayout : function()
40854     {
40855         var previousWidth = this.containerWidth;
40856         this.getContainerWidth();
40857         return previousWidth !== this.containerWidth;
40858     }
40859  
40860 });
40861
40862  
40863
40864  /*
40865  * - LGPL
40866  *
40867  * element
40868  * 
40869  */
40870
40871 /**
40872  * @class Roo.bootstrap.MasonryBrick
40873  * @extends Roo.bootstrap.Component
40874  * Bootstrap MasonryBrick class
40875  * 
40876  * @constructor
40877  * Create a new MasonryBrick
40878  * @param {Object} config The config object
40879  */
40880
40881 Roo.bootstrap.MasonryBrick = function(config){
40882     
40883     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40884     
40885     Roo.bootstrap.MasonryBrick.register(this);
40886     
40887     this.addEvents({
40888         // raw events
40889         /**
40890          * @event click
40891          * When a MasonryBrick is clcik
40892          * @param {Roo.bootstrap.MasonryBrick} this
40893          * @param {Roo.EventObject} e
40894          */
40895         "click" : true
40896     });
40897 };
40898
40899 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40900     
40901     /**
40902      * @cfg {String} title
40903      */   
40904     title : '',
40905     /**
40906      * @cfg {String} html
40907      */   
40908     html : '',
40909     /**
40910      * @cfg {String} bgimage
40911      */   
40912     bgimage : '',
40913     /**
40914      * @cfg {String} videourl
40915      */   
40916     videourl : '',
40917     /**
40918      * @cfg {String} cls
40919      */   
40920     cls : '',
40921     /**
40922      * @cfg {String} href
40923      */   
40924     href : '',
40925     /**
40926      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40927      */   
40928     size : 'xs',
40929     
40930     /**
40931      * @cfg {String} placetitle (center|bottom)
40932      */   
40933     placetitle : '',
40934     
40935     /**
40936      * @cfg {Boolean} isFitContainer defalut true
40937      */   
40938     isFitContainer : true, 
40939     
40940     /**
40941      * @cfg {Boolean} preventDefault defalut false
40942      */   
40943     preventDefault : false, 
40944     
40945     /**
40946      * @cfg {Boolean} inverse defalut false
40947      */   
40948     maskInverse : false, 
40949     
40950     getAutoCreate : function()
40951     {
40952         if(!this.isFitContainer){
40953             return this.getSplitAutoCreate();
40954         }
40955         
40956         var cls = 'masonry-brick masonry-brick-full';
40957         
40958         if(this.href.length){
40959             cls += ' masonry-brick-link';
40960         }
40961         
40962         if(this.bgimage.length){
40963             cls += ' masonry-brick-image';
40964         }
40965         
40966         if(this.maskInverse){
40967             cls += ' mask-inverse';
40968         }
40969         
40970         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40971             cls += ' enable-mask';
40972         }
40973         
40974         if(this.size){
40975             cls += ' masonry-' + this.size + '-brick';
40976         }
40977         
40978         if(this.placetitle.length){
40979             
40980             switch (this.placetitle) {
40981                 case 'center' :
40982                     cls += ' masonry-center-title';
40983                     break;
40984                 case 'bottom' :
40985                     cls += ' masonry-bottom-title';
40986                     break;
40987                 default:
40988                     break;
40989             }
40990             
40991         } else {
40992             if(!this.html.length && !this.bgimage.length){
40993                 cls += ' masonry-center-title';
40994             }
40995
40996             if(!this.html.length && this.bgimage.length){
40997                 cls += ' masonry-bottom-title';
40998             }
40999         }
41000         
41001         if(this.cls){
41002             cls += ' ' + this.cls;
41003         }
41004         
41005         var cfg = {
41006             tag: (this.href.length) ? 'a' : 'div',
41007             cls: cls,
41008             cn: [
41009                 {
41010                     tag: 'div',
41011                     cls: 'masonry-brick-mask'
41012                 },
41013                 {
41014                     tag: 'div',
41015                     cls: 'masonry-brick-paragraph',
41016                     cn: []
41017                 }
41018             ]
41019         };
41020         
41021         if(this.href.length){
41022             cfg.href = this.href;
41023         }
41024         
41025         var cn = cfg.cn[1].cn;
41026         
41027         if(this.title.length){
41028             cn.push({
41029                 tag: 'h4',
41030                 cls: 'masonry-brick-title',
41031                 html: this.title
41032             });
41033         }
41034         
41035         if(this.html.length){
41036             cn.push({
41037                 tag: 'p',
41038                 cls: 'masonry-brick-text',
41039                 html: this.html
41040             });
41041         }
41042         
41043         if (!this.title.length && !this.html.length) {
41044             cfg.cn[1].cls += ' hide';
41045         }
41046         
41047         if(this.bgimage.length){
41048             cfg.cn.push({
41049                 tag: 'img',
41050                 cls: 'masonry-brick-image-view',
41051                 src: this.bgimage
41052             });
41053         }
41054         
41055         if(this.videourl.length){
41056             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41057             // youtube support only?
41058             cfg.cn.push({
41059                 tag: 'iframe',
41060                 cls: 'masonry-brick-image-view',
41061                 src: vurl,
41062                 frameborder : 0,
41063                 allowfullscreen : true
41064             });
41065         }
41066         
41067         return cfg;
41068         
41069     },
41070     
41071     getSplitAutoCreate : function()
41072     {
41073         var cls = 'masonry-brick masonry-brick-split';
41074         
41075         if(this.href.length){
41076             cls += ' masonry-brick-link';
41077         }
41078         
41079         if(this.bgimage.length){
41080             cls += ' masonry-brick-image';
41081         }
41082         
41083         if(this.size){
41084             cls += ' masonry-' + this.size + '-brick';
41085         }
41086         
41087         switch (this.placetitle) {
41088             case 'center' :
41089                 cls += ' masonry-center-title';
41090                 break;
41091             case 'bottom' :
41092                 cls += ' masonry-bottom-title';
41093                 break;
41094             default:
41095                 if(!this.bgimage.length){
41096                     cls += ' masonry-center-title';
41097                 }
41098
41099                 if(this.bgimage.length){
41100                     cls += ' masonry-bottom-title';
41101                 }
41102                 break;
41103         }
41104         
41105         if(this.cls){
41106             cls += ' ' + this.cls;
41107         }
41108         
41109         var cfg = {
41110             tag: (this.href.length) ? 'a' : 'div',
41111             cls: cls,
41112             cn: [
41113                 {
41114                     tag: 'div',
41115                     cls: 'masonry-brick-split-head',
41116                     cn: [
41117                         {
41118                             tag: 'div',
41119                             cls: 'masonry-brick-paragraph',
41120                             cn: []
41121                         }
41122                     ]
41123                 },
41124                 {
41125                     tag: 'div',
41126                     cls: 'masonry-brick-split-body',
41127                     cn: []
41128                 }
41129             ]
41130         };
41131         
41132         if(this.href.length){
41133             cfg.href = this.href;
41134         }
41135         
41136         if(this.title.length){
41137             cfg.cn[0].cn[0].cn.push({
41138                 tag: 'h4',
41139                 cls: 'masonry-brick-title',
41140                 html: this.title
41141             });
41142         }
41143         
41144         if(this.html.length){
41145             cfg.cn[1].cn.push({
41146                 tag: 'p',
41147                 cls: 'masonry-brick-text',
41148                 html: this.html
41149             });
41150         }
41151
41152         if(this.bgimage.length){
41153             cfg.cn[0].cn.push({
41154                 tag: 'img',
41155                 cls: 'masonry-brick-image-view',
41156                 src: this.bgimage
41157             });
41158         }
41159         
41160         if(this.videourl.length){
41161             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41162             // youtube support only?
41163             cfg.cn[0].cn.cn.push({
41164                 tag: 'iframe',
41165                 cls: 'masonry-brick-image-view',
41166                 src: vurl,
41167                 frameborder : 0,
41168                 allowfullscreen : true
41169             });
41170         }
41171         
41172         return cfg;
41173     },
41174     
41175     initEvents: function() 
41176     {
41177         switch (this.size) {
41178             case 'xs' :
41179                 this.x = 1;
41180                 this.y = 1;
41181                 break;
41182             case 'sm' :
41183                 this.x = 2;
41184                 this.y = 2;
41185                 break;
41186             case 'md' :
41187             case 'md-left' :
41188             case 'md-right' :
41189                 this.x = 3;
41190                 this.y = 3;
41191                 break;
41192             case 'tall' :
41193                 this.x = 2;
41194                 this.y = 3;
41195                 break;
41196             case 'wide' :
41197                 this.x = 3;
41198                 this.y = 2;
41199                 break;
41200             case 'wide-thin' :
41201                 this.x = 3;
41202                 this.y = 1;
41203                 break;
41204                         
41205             default :
41206                 break;
41207         }
41208         
41209         if(Roo.isTouch){
41210             this.el.on('touchstart', this.onTouchStart, this);
41211             this.el.on('touchmove', this.onTouchMove, this);
41212             this.el.on('touchend', this.onTouchEnd, this);
41213             this.el.on('contextmenu', this.onContextMenu, this);
41214         } else {
41215             this.el.on('mouseenter'  ,this.enter, this);
41216             this.el.on('mouseleave', this.leave, this);
41217             this.el.on('click', this.onClick, this);
41218         }
41219         
41220         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41221             this.parent().bricks.push(this);   
41222         }
41223         
41224     },
41225     
41226     onClick: function(e, el)
41227     {
41228         var time = this.endTimer - this.startTimer;
41229         // Roo.log(e.preventDefault());
41230         if(Roo.isTouch){
41231             if(time > 1000){
41232                 e.preventDefault();
41233                 return;
41234             }
41235         }
41236         
41237         if(!this.preventDefault){
41238             return;
41239         }
41240         
41241         e.preventDefault();
41242         
41243         if (this.activeClass != '') {
41244             this.selectBrick();
41245         }
41246         
41247         this.fireEvent('click', this, e);
41248     },
41249     
41250     enter: function(e, el)
41251     {
41252         e.preventDefault();
41253         
41254         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41255             return;
41256         }
41257         
41258         if(this.bgimage.length && this.html.length){
41259             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41260         }
41261     },
41262     
41263     leave: function(e, el)
41264     {
41265         e.preventDefault();
41266         
41267         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41268             return;
41269         }
41270         
41271         if(this.bgimage.length && this.html.length){
41272             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41273         }
41274     },
41275     
41276     onTouchStart: function(e, el)
41277     {
41278 //        e.preventDefault();
41279         
41280         this.touchmoved = false;
41281         
41282         if(!this.isFitContainer){
41283             return;
41284         }
41285         
41286         if(!this.bgimage.length || !this.html.length){
41287             return;
41288         }
41289         
41290         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41291         
41292         this.timer = new Date().getTime();
41293         
41294     },
41295     
41296     onTouchMove: function(e, el)
41297     {
41298         this.touchmoved = true;
41299     },
41300     
41301     onContextMenu : function(e,el)
41302     {
41303         e.preventDefault();
41304         e.stopPropagation();
41305         return false;
41306     },
41307     
41308     onTouchEnd: function(e, el)
41309     {
41310 //        e.preventDefault();
41311         
41312         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41313         
41314             this.leave(e,el);
41315             
41316             return;
41317         }
41318         
41319         if(!this.bgimage.length || !this.html.length){
41320             
41321             if(this.href.length){
41322                 window.location.href = this.href;
41323             }
41324             
41325             return;
41326         }
41327         
41328         if(!this.isFitContainer){
41329             return;
41330         }
41331         
41332         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41333         
41334         window.location.href = this.href;
41335     },
41336     
41337     //selection on single brick only
41338     selectBrick : function() {
41339         
41340         if (!this.parentId) {
41341             return;
41342         }
41343         
41344         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41345         var index = m.selectedBrick.indexOf(this.id);
41346         
41347         if ( index > -1) {
41348             m.selectedBrick.splice(index,1);
41349             this.el.removeClass(this.activeClass);
41350             return;
41351         }
41352         
41353         for(var i = 0; i < m.selectedBrick.length; i++) {
41354             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41355             b.el.removeClass(b.activeClass);
41356         }
41357         
41358         m.selectedBrick = [];
41359         
41360         m.selectedBrick.push(this.id);
41361         this.el.addClass(this.activeClass);
41362         return;
41363     },
41364     
41365     isSelected : function(){
41366         return this.el.hasClass(this.activeClass);
41367         
41368     }
41369 });
41370
41371 Roo.apply(Roo.bootstrap.MasonryBrick, {
41372     
41373     //groups: {},
41374     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41375      /**
41376     * register a Masonry Brick
41377     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41378     */
41379     
41380     register : function(brick)
41381     {
41382         //this.groups[brick.id] = brick;
41383         this.groups.add(brick.id, brick);
41384     },
41385     /**
41386     * fetch a  masonry brick based on the masonry brick ID
41387     * @param {string} the masonry brick to add
41388     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41389     */
41390     
41391     get: function(brick_id) 
41392     {
41393         // if (typeof(this.groups[brick_id]) == 'undefined') {
41394         //     return false;
41395         // }
41396         // return this.groups[brick_id] ;
41397         
41398         if(this.groups.key(brick_id)) {
41399             return this.groups.key(brick_id);
41400         }
41401         
41402         return false;
41403     }
41404     
41405     
41406     
41407 });
41408
41409  /*
41410  * - LGPL
41411  *
41412  * element
41413  * 
41414  */
41415
41416 /**
41417  * @class Roo.bootstrap.Brick
41418  * @extends Roo.bootstrap.Component
41419  * Bootstrap Brick class
41420  * 
41421  * @constructor
41422  * Create a new Brick
41423  * @param {Object} config The config object
41424  */
41425
41426 Roo.bootstrap.Brick = function(config){
41427     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41428     
41429     this.addEvents({
41430         // raw events
41431         /**
41432          * @event click
41433          * When a Brick is click
41434          * @param {Roo.bootstrap.Brick} this
41435          * @param {Roo.EventObject} e
41436          */
41437         "click" : true
41438     });
41439 };
41440
41441 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41442     
41443     /**
41444      * @cfg {String} title
41445      */   
41446     title : '',
41447     /**
41448      * @cfg {String} html
41449      */   
41450     html : '',
41451     /**
41452      * @cfg {String} bgimage
41453      */   
41454     bgimage : '',
41455     /**
41456      * @cfg {String} cls
41457      */   
41458     cls : '',
41459     /**
41460      * @cfg {String} href
41461      */   
41462     href : '',
41463     /**
41464      * @cfg {String} video
41465      */   
41466     video : '',
41467     /**
41468      * @cfg {Boolean} square
41469      */   
41470     square : true,
41471     
41472     getAutoCreate : function()
41473     {
41474         var cls = 'roo-brick';
41475         
41476         if(this.href.length){
41477             cls += ' roo-brick-link';
41478         }
41479         
41480         if(this.bgimage.length){
41481             cls += ' roo-brick-image';
41482         }
41483         
41484         if(!this.html.length && !this.bgimage.length){
41485             cls += ' roo-brick-center-title';
41486         }
41487         
41488         if(!this.html.length && this.bgimage.length){
41489             cls += ' roo-brick-bottom-title';
41490         }
41491         
41492         if(this.cls){
41493             cls += ' ' + this.cls;
41494         }
41495         
41496         var cfg = {
41497             tag: (this.href.length) ? 'a' : 'div',
41498             cls: cls,
41499             cn: [
41500                 {
41501                     tag: 'div',
41502                     cls: 'roo-brick-paragraph',
41503                     cn: []
41504                 }
41505             ]
41506         };
41507         
41508         if(this.href.length){
41509             cfg.href = this.href;
41510         }
41511         
41512         var cn = cfg.cn[0].cn;
41513         
41514         if(this.title.length){
41515             cn.push({
41516                 tag: 'h4',
41517                 cls: 'roo-brick-title',
41518                 html: this.title
41519             });
41520         }
41521         
41522         if(this.html.length){
41523             cn.push({
41524                 tag: 'p',
41525                 cls: 'roo-brick-text',
41526                 html: this.html
41527             });
41528         } else {
41529             cn.cls += ' hide';
41530         }
41531         
41532         if(this.bgimage.length){
41533             cfg.cn.push({
41534                 tag: 'img',
41535                 cls: 'roo-brick-image-view',
41536                 src: this.bgimage
41537             });
41538         }
41539         
41540         return cfg;
41541     },
41542     
41543     initEvents: function() 
41544     {
41545         if(this.title.length || this.html.length){
41546             this.el.on('mouseenter'  ,this.enter, this);
41547             this.el.on('mouseleave', this.leave, this);
41548         }
41549         
41550         Roo.EventManager.onWindowResize(this.resize, this); 
41551         
41552         if(this.bgimage.length){
41553             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41554             this.imageEl.on('load', this.onImageLoad, this);
41555             return;
41556         }
41557         
41558         this.resize();
41559     },
41560     
41561     onImageLoad : function()
41562     {
41563         this.resize();
41564     },
41565     
41566     resize : function()
41567     {
41568         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41569         
41570         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41571         
41572         if(this.bgimage.length){
41573             var image = this.el.select('.roo-brick-image-view', true).first();
41574             
41575             image.setWidth(paragraph.getWidth());
41576             
41577             if(this.square){
41578                 image.setHeight(paragraph.getWidth());
41579             }
41580             
41581             this.el.setHeight(image.getHeight());
41582             paragraph.setHeight(image.getHeight());
41583             
41584         }
41585         
41586     },
41587     
41588     enter: function(e, el)
41589     {
41590         e.preventDefault();
41591         
41592         if(this.bgimage.length){
41593             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41594             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41595         }
41596     },
41597     
41598     leave: function(e, el)
41599     {
41600         e.preventDefault();
41601         
41602         if(this.bgimage.length){
41603             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41604             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41605         }
41606     }
41607     
41608 });
41609
41610  
41611
41612  /*
41613  * - LGPL
41614  *
41615  * Number field 
41616  */
41617
41618 /**
41619  * @class Roo.bootstrap.form.NumberField
41620  * @extends Roo.bootstrap.form.Input
41621  * Bootstrap NumberField class
41622  * 
41623  * 
41624  * 
41625  * 
41626  * @constructor
41627  * Create a new NumberField
41628  * @param {Object} config The config object
41629  */
41630
41631 Roo.bootstrap.form.NumberField = function(config){
41632     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41633 };
41634
41635 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41636     
41637     /**
41638      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41639      */
41640     allowDecimals : true,
41641     /**
41642      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41643      */
41644     decimalSeparator : ".",
41645     /**
41646      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41647      */
41648     decimalPrecision : 2,
41649     /**
41650      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41651      */
41652     allowNegative : true,
41653     
41654     /**
41655      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41656      */
41657     allowZero: true,
41658     /**
41659      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41660      */
41661     minValue : Number.NEGATIVE_INFINITY,
41662     /**
41663      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41664      */
41665     maxValue : Number.MAX_VALUE,
41666     /**
41667      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41668      */
41669     minText : "The minimum value for this field is {0}",
41670     /**
41671      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41672      */
41673     maxText : "The maximum value for this field is {0}",
41674     /**
41675      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41676      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41677      */
41678     nanText : "{0} is not a valid number",
41679     /**
41680      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41681      */
41682     thousandsDelimiter : false,
41683     /**
41684      * @cfg {String} valueAlign alignment of value
41685      */
41686     valueAlign : "left",
41687
41688     getAutoCreate : function()
41689     {
41690         var hiddenInput = {
41691             tag: 'input',
41692             type: 'hidden',
41693             id: Roo.id(),
41694             cls: 'hidden-number-input'
41695         };
41696         
41697         if (this.name) {
41698             hiddenInput.name = this.name;
41699         }
41700         
41701         this.name = '';
41702         
41703         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41704         
41705         this.name = hiddenInput.name;
41706         
41707         if(cfg.cn.length > 0) {
41708             cfg.cn.push(hiddenInput);
41709         }
41710         
41711         return cfg;
41712     },
41713
41714     // private
41715     initEvents : function()
41716     {   
41717         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41718         
41719         var allowed = "0123456789";
41720         
41721         if(this.allowDecimals){
41722             allowed += this.decimalSeparator;
41723         }
41724         
41725         if(this.allowNegative){
41726             allowed += "-";
41727         }
41728         
41729         if(this.thousandsDelimiter) {
41730             allowed += ",";
41731         }
41732         
41733         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41734         
41735         var keyPress = function(e){
41736             
41737             var k = e.getKey();
41738             
41739             var c = e.getCharCode();
41740             
41741             if(
41742                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41743                     allowed.indexOf(String.fromCharCode(c)) === -1
41744             ){
41745                 e.stopEvent();
41746                 return;
41747             }
41748             
41749             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41750                 return;
41751             }
41752             
41753             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41754                 e.stopEvent();
41755             }
41756         };
41757         
41758         this.el.on("keypress", keyPress, this);
41759     },
41760     
41761     validateValue : function(value)
41762     {
41763         
41764         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41765             return false;
41766         }
41767         
41768         var num = this.parseValue(value);
41769         
41770         if(isNaN(num)){
41771             this.markInvalid(String.format(this.nanText, value));
41772             return false;
41773         }
41774         
41775         if(num < this.minValue){
41776             this.markInvalid(String.format(this.minText, this.minValue));
41777             return false;
41778         }
41779         
41780         if(num > this.maxValue){
41781             this.markInvalid(String.format(this.maxText, this.maxValue));
41782             return false;
41783         }
41784         
41785         return true;
41786     },
41787
41788     getValue : function()
41789     {
41790         var v = this.hiddenEl().getValue();
41791         
41792         return this.fixPrecision(this.parseValue(v));
41793     },
41794
41795     parseValue : function(value)
41796     {
41797         if(this.thousandsDelimiter) {
41798             value += "";
41799             r = new RegExp(",", "g");
41800             value = value.replace(r, "");
41801         }
41802         
41803         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41804         return isNaN(value) ? '' : value;
41805     },
41806
41807     fixPrecision : function(value)
41808     {
41809         if(this.thousandsDelimiter) {
41810             value += "";
41811             r = new RegExp(",", "g");
41812             value = value.replace(r, "");
41813         }
41814         
41815         var nan = isNaN(value);
41816         
41817         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41818             return nan ? '' : value;
41819         }
41820         return parseFloat(value).toFixed(this.decimalPrecision);
41821     },
41822
41823     setValue : function(v)
41824     {
41825         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41826         
41827         this.value = v;
41828         
41829         if(this.rendered){
41830             
41831             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41832             
41833             this.inputEl().dom.value = (v == '') ? '' :
41834                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41835             
41836             if(!this.allowZero && v === '0') {
41837                 this.hiddenEl().dom.value = '';
41838                 this.inputEl().dom.value = '';
41839             }
41840             
41841             this.validate();
41842         }
41843     },
41844
41845     decimalPrecisionFcn : function(v)
41846     {
41847         return Math.floor(v);
41848     },
41849
41850     beforeBlur : function()
41851     {
41852         var v = this.parseValue(this.getRawValue());
41853         
41854         if(v || v === 0 || v === ''){
41855             this.setValue(v);
41856         }
41857     },
41858     
41859     hiddenEl : function()
41860     {
41861         return this.el.select('input.hidden-number-input',true).first();
41862     }
41863     
41864 });
41865
41866  
41867
41868 /*
41869 * Licence: LGPL
41870 */
41871
41872 /**
41873  * @class Roo.bootstrap.DocumentSlider
41874  * @extends Roo.bootstrap.Component
41875  * Bootstrap DocumentSlider class
41876  * 
41877  * @constructor
41878  * Create a new DocumentViewer
41879  * @param {Object} config The config object
41880  */
41881
41882 Roo.bootstrap.DocumentSlider = function(config){
41883     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41884     
41885     this.files = [];
41886     
41887     this.addEvents({
41888         /**
41889          * @event initial
41890          * Fire after initEvent
41891          * @param {Roo.bootstrap.DocumentSlider} this
41892          */
41893         "initial" : true,
41894         /**
41895          * @event update
41896          * Fire after update
41897          * @param {Roo.bootstrap.DocumentSlider} this
41898          */
41899         "update" : true,
41900         /**
41901          * @event click
41902          * Fire after click
41903          * @param {Roo.bootstrap.DocumentSlider} this
41904          */
41905         "click" : true
41906     });
41907 };
41908
41909 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41910     
41911     files : false,
41912     
41913     indicator : 0,
41914     
41915     getAutoCreate : function()
41916     {
41917         var cfg = {
41918             tag : 'div',
41919             cls : 'roo-document-slider',
41920             cn : [
41921                 {
41922                     tag : 'div',
41923                     cls : 'roo-document-slider-header',
41924                     cn : [
41925                         {
41926                             tag : 'div',
41927                             cls : 'roo-document-slider-header-title'
41928                         }
41929                     ]
41930                 },
41931                 {
41932                     tag : 'div',
41933                     cls : 'roo-document-slider-body',
41934                     cn : [
41935                         {
41936                             tag : 'div',
41937                             cls : 'roo-document-slider-prev',
41938                             cn : [
41939                                 {
41940                                     tag : 'i',
41941                                     cls : 'fa fa-chevron-left'
41942                                 }
41943                             ]
41944                         },
41945                         {
41946                             tag : 'div',
41947                             cls : 'roo-document-slider-thumb',
41948                             cn : [
41949                                 {
41950                                     tag : 'img',
41951                                     cls : 'roo-document-slider-image'
41952                                 }
41953                             ]
41954                         },
41955                         {
41956                             tag : 'div',
41957                             cls : 'roo-document-slider-next',
41958                             cn : [
41959                                 {
41960                                     tag : 'i',
41961                                     cls : 'fa fa-chevron-right'
41962                                 }
41963                             ]
41964                         }
41965                     ]
41966                 }
41967             ]
41968         };
41969         
41970         return cfg;
41971     },
41972     
41973     initEvents : function()
41974     {
41975         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41976         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41977         
41978         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41979         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41980         
41981         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41982         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41983         
41984         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41985         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41986         
41987         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41988         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41989         
41990         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41991         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41992         
41993         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41994         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41995         
41996         this.thumbEl.on('click', this.onClick, this);
41997         
41998         this.prevIndicator.on('click', this.prev, this);
41999         
42000         this.nextIndicator.on('click', this.next, this);
42001         
42002     },
42003     
42004     initial : function()
42005     {
42006         if(this.files.length){
42007             this.indicator = 1;
42008             this.update()
42009         }
42010         
42011         this.fireEvent('initial', this);
42012     },
42013     
42014     update : function()
42015     {
42016         this.imageEl.attr('src', this.files[this.indicator - 1]);
42017         
42018         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42019         
42020         this.prevIndicator.show();
42021         
42022         if(this.indicator == 1){
42023             this.prevIndicator.hide();
42024         }
42025         
42026         this.nextIndicator.show();
42027         
42028         if(this.indicator == this.files.length){
42029             this.nextIndicator.hide();
42030         }
42031         
42032         this.thumbEl.scrollTo('top');
42033         
42034         this.fireEvent('update', this);
42035     },
42036     
42037     onClick : function(e)
42038     {
42039         e.preventDefault();
42040         
42041         this.fireEvent('click', this);
42042     },
42043     
42044     prev : function(e)
42045     {
42046         e.preventDefault();
42047         
42048         this.indicator = Math.max(1, this.indicator - 1);
42049         
42050         this.update();
42051     },
42052     
42053     next : function(e)
42054     {
42055         e.preventDefault();
42056         
42057         this.indicator = Math.min(this.files.length, this.indicator + 1);
42058         
42059         this.update();
42060     }
42061 });
42062 /*
42063  * - LGPL
42064  *
42065  * RadioSet
42066  *
42067  *
42068  */
42069
42070 /**
42071  * @class Roo.bootstrap.form.RadioSet
42072  * @extends Roo.bootstrap.form.Input
42073  * @children Roo.bootstrap.form.Radio
42074  * Bootstrap RadioSet class
42075  * @cfg {String} indicatorpos (left|right) default left
42076  * @cfg {Boolean} inline (true|false) inline the element (default true)
42077  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42078  * @constructor
42079  * Create a new RadioSet
42080  * @param {Object} config The config object
42081  */
42082
42083 Roo.bootstrap.form.RadioSet = function(config){
42084     
42085     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42086     
42087     this.radioes = [];
42088     
42089     Roo.bootstrap.form.RadioSet.register(this);
42090     
42091     this.addEvents({
42092         /**
42093         * @event check
42094         * Fires when the element is checked or unchecked.
42095         * @param {Roo.bootstrap.form.RadioSet} this This radio
42096         * @param {Roo.bootstrap.form.Radio} item The checked item
42097         */
42098        check : true,
42099        /**
42100         * @event click
42101         * Fires when the element is click.
42102         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42103         * @param {Roo.bootstrap.form.Radio} item The checked item
42104         * @param {Roo.EventObject} e The event object
42105         */
42106        click : true
42107     });
42108     
42109 };
42110
42111 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42112
42113     radioes : false,
42114     
42115     inline : true,
42116     
42117     weight : '',
42118     
42119     indicatorpos : 'left',
42120     
42121     getAutoCreate : function()
42122     {
42123         var label = {
42124             tag : 'label',
42125             cls : 'roo-radio-set-label',
42126             cn : [
42127                 {
42128                     tag : 'span',
42129                     html : this.fieldLabel
42130                 }
42131             ]
42132         };
42133         if (Roo.bootstrap.version == 3) {
42134             
42135             
42136             if(this.indicatorpos == 'left'){
42137                 label.cn.unshift({
42138                     tag : 'i',
42139                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42140                     tooltip : 'This field is required'
42141                 });
42142             } else {
42143                 label.cn.push({
42144                     tag : 'i',
42145                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42146                     tooltip : 'This field is required'
42147                 });
42148             }
42149         }
42150         var items = {
42151             tag : 'div',
42152             cls : 'roo-radio-set-items'
42153         };
42154         
42155         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42156         
42157         if (align === 'left' && this.fieldLabel.length) {
42158             
42159             items = {
42160                 cls : "roo-radio-set-right", 
42161                 cn: [
42162                     items
42163                 ]
42164             };
42165             
42166             if(this.labelWidth > 12){
42167                 label.style = "width: " + this.labelWidth + 'px';
42168             }
42169             
42170             if(this.labelWidth < 13 && this.labelmd == 0){
42171                 this.labelmd = this.labelWidth;
42172             }
42173             
42174             if(this.labellg > 0){
42175                 label.cls += ' col-lg-' + this.labellg;
42176                 items.cls += ' col-lg-' + (12 - this.labellg);
42177             }
42178             
42179             if(this.labelmd > 0){
42180                 label.cls += ' col-md-' + this.labelmd;
42181                 items.cls += ' col-md-' + (12 - this.labelmd);
42182             }
42183             
42184             if(this.labelsm > 0){
42185                 label.cls += ' col-sm-' + this.labelsm;
42186                 items.cls += ' col-sm-' + (12 - this.labelsm);
42187             }
42188             
42189             if(this.labelxs > 0){
42190                 label.cls += ' col-xs-' + this.labelxs;
42191                 items.cls += ' col-xs-' + (12 - this.labelxs);
42192             }
42193         }
42194         
42195         var cfg = {
42196             tag : 'div',
42197             cls : 'roo-radio-set',
42198             cn : [
42199                 {
42200                     tag : 'input',
42201                     cls : 'roo-radio-set-input',
42202                     type : 'hidden',
42203                     name : this.name,
42204                     value : this.value ? this.value :  ''
42205                 },
42206                 label,
42207                 items
42208             ]
42209         };
42210         
42211         if(this.weight.length){
42212             cfg.cls += ' roo-radio-' + this.weight;
42213         }
42214         
42215         if(this.inline) {
42216             cfg.cls += ' roo-radio-set-inline';
42217         }
42218         
42219         var settings=this;
42220         ['xs','sm','md','lg'].map(function(size){
42221             if (settings[size]) {
42222                 cfg.cls += ' col-' + size + '-' + settings[size];
42223             }
42224         });
42225         
42226         return cfg;
42227         
42228     },
42229
42230     initEvents : function()
42231     {
42232         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42233         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42234         
42235         if(!this.fieldLabel.length){
42236             this.labelEl.hide();
42237         }
42238         
42239         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42240         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42241         
42242         this.indicator = this.indicatorEl();
42243         
42244         if(this.indicator){
42245             this.indicator.addClass('invisible');
42246         }
42247         
42248         this.originalValue = this.getValue();
42249         
42250     },
42251     
42252     inputEl: function ()
42253     {
42254         return this.el.select('.roo-radio-set-input', true).first();
42255     },
42256     
42257     getChildContainer : function()
42258     {
42259         return this.itemsEl;
42260     },
42261     
42262     register : function(item)
42263     {
42264         this.radioes.push(item);
42265         
42266     },
42267     
42268     validate : function()
42269     {   
42270         if(this.getVisibilityEl().hasClass('hidden')){
42271             return true;
42272         }
42273         
42274         var valid = false;
42275         
42276         Roo.each(this.radioes, function(i){
42277             if(!i.checked){
42278                 return;
42279             }
42280             
42281             valid = true;
42282             return false;
42283         });
42284         
42285         if(this.allowBlank) {
42286             return true;
42287         }
42288         
42289         if(this.disabled || valid){
42290             this.markValid();
42291             return true;
42292         }
42293         
42294         this.markInvalid();
42295         return false;
42296         
42297     },
42298     
42299     markValid : function()
42300     {
42301         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42302             this.indicatorEl().removeClass('visible');
42303             this.indicatorEl().addClass('invisible');
42304         }
42305         
42306         
42307         if (Roo.bootstrap.version == 3) {
42308             this.el.removeClass([this.invalidClass, this.validClass]);
42309             this.el.addClass(this.validClass);
42310         } else {
42311             this.el.removeClass(['is-invalid','is-valid']);
42312             this.el.addClass(['is-valid']);
42313         }
42314         this.fireEvent('valid', this);
42315     },
42316     
42317     markInvalid : function(msg)
42318     {
42319         if(this.allowBlank || this.disabled){
42320             return;
42321         }
42322         
42323         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42324             this.indicatorEl().removeClass('invisible');
42325             this.indicatorEl().addClass('visible');
42326         }
42327         if (Roo.bootstrap.version == 3) {
42328             this.el.removeClass([this.invalidClass, this.validClass]);
42329             this.el.addClass(this.invalidClass);
42330         } else {
42331             this.el.removeClass(['is-invalid','is-valid']);
42332             this.el.addClass(['is-invalid']);
42333         }
42334         
42335         this.fireEvent('invalid', this, msg);
42336         
42337     },
42338     
42339     setValue : function(v, suppressEvent)
42340     {   
42341         if(this.value === v){
42342             return;
42343         }
42344         
42345         this.value = v;
42346         
42347         if(this.rendered){
42348             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42349         }
42350         
42351         Roo.each(this.radioes, function(i){
42352             i.checked = false;
42353             i.el.removeClass('checked');
42354         });
42355         
42356         Roo.each(this.radioes, function(i){
42357             
42358             if(i.value === v || i.value.toString() === v.toString()){
42359                 i.checked = true;
42360                 i.el.addClass('checked');
42361                 
42362                 if(suppressEvent !== true){
42363                     this.fireEvent('check', this, i);
42364                 }
42365                 
42366                 return false;
42367             }
42368             
42369         }, this);
42370         
42371         this.validate();
42372     },
42373     
42374     clearInvalid : function(){
42375         
42376         if(!this.el || this.preventMark){
42377             return;
42378         }
42379         
42380         this.el.removeClass([this.invalidClass]);
42381         
42382         this.fireEvent('valid', this);
42383     }
42384     
42385 });
42386
42387 Roo.apply(Roo.bootstrap.form.RadioSet, {
42388     
42389     groups: {},
42390     
42391     register : function(set)
42392     {
42393         this.groups[set.name] = set;
42394     },
42395     
42396     get: function(name) 
42397     {
42398         if (typeof(this.groups[name]) == 'undefined') {
42399             return false;
42400         }
42401         
42402         return this.groups[name] ;
42403     }
42404     
42405 });
42406 /*
42407  * Based on:
42408  * Ext JS Library 1.1.1
42409  * Copyright(c) 2006-2007, Ext JS, LLC.
42410  *
42411  * Originally Released Under LGPL - original licence link has changed is not relivant.
42412  *
42413  * Fork - LGPL
42414  * <script type="text/javascript">
42415  */
42416
42417
42418 /**
42419  * @class Roo.bootstrap.SplitBar
42420  * @extends Roo.util.Observable
42421  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42422  * <br><br>
42423  * Usage:
42424  * <pre><code>
42425 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42426                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42427 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42428 split.minSize = 100;
42429 split.maxSize = 600;
42430 split.animate = true;
42431 split.on('moved', splitterMoved);
42432 </code></pre>
42433  * @constructor
42434  * Create a new SplitBar
42435  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42436  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42437  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42438  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42439                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42440                         position of the SplitBar).
42441  */
42442 Roo.bootstrap.SplitBar = function(cfg){
42443     
42444     /** @private */
42445     
42446     //{
42447     //  dragElement : elm
42448     //  resizingElement: el,
42449         // optional..
42450     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42451     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42452         // existingProxy ???
42453     //}
42454     
42455     this.el = Roo.get(cfg.dragElement, true);
42456     this.el.dom.unselectable = "on";
42457     /** @private */
42458     this.resizingEl = Roo.get(cfg.resizingElement, true);
42459
42460     /**
42461      * @private
42462      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42463      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42464      * @type Number
42465      */
42466     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42467     
42468     /**
42469      * The minimum size of the resizing element. (Defaults to 0)
42470      * @type Number
42471      */
42472     this.minSize = 0;
42473     
42474     /**
42475      * The maximum size of the resizing element. (Defaults to 2000)
42476      * @type Number
42477      */
42478     this.maxSize = 2000;
42479     
42480     /**
42481      * Whether to animate the transition to the new size
42482      * @type Boolean
42483      */
42484     this.animate = false;
42485     
42486     /**
42487      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42488      * @type Boolean
42489      */
42490     this.useShim = false;
42491     
42492     /** @private */
42493     this.shim = null;
42494     
42495     if(!cfg.existingProxy){
42496         /** @private */
42497         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42498     }else{
42499         this.proxy = Roo.get(cfg.existingProxy).dom;
42500     }
42501     /** @private */
42502     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42503     
42504     /** @private */
42505     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42506     
42507     /** @private */
42508     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42509     
42510     /** @private */
42511     this.dragSpecs = {};
42512     
42513     /**
42514      * @private The adapter to use to positon and resize elements
42515      */
42516     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42517     this.adapter.init(this);
42518     
42519     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42520         /** @private */
42521         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42522         this.el.addClass("roo-splitbar-h");
42523     }else{
42524         /** @private */
42525         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42526         this.el.addClass("roo-splitbar-v");
42527     }
42528     
42529     this.addEvents({
42530         /**
42531          * @event resize
42532          * Fires when the splitter is moved (alias for {@link #event-moved})
42533          * @param {Roo.bootstrap.SplitBar} this
42534          * @param {Number} newSize the new width or height
42535          */
42536         "resize" : true,
42537         /**
42538          * @event moved
42539          * Fires when the splitter is moved
42540          * @param {Roo.bootstrap.SplitBar} this
42541          * @param {Number} newSize the new width or height
42542          */
42543         "moved" : true,
42544         /**
42545          * @event beforeresize
42546          * Fires before the splitter is dragged
42547          * @param {Roo.bootstrap.SplitBar} this
42548          */
42549         "beforeresize" : true,
42550
42551         "beforeapply" : true
42552     });
42553
42554     Roo.util.Observable.call(this);
42555 };
42556
42557 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42558     onStartProxyDrag : function(x, y){
42559         this.fireEvent("beforeresize", this);
42560         if(!this.overlay){
42561             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42562             o.unselectable();
42563             o.enableDisplayMode("block");
42564             // all splitbars share the same overlay
42565             Roo.bootstrap.SplitBar.prototype.overlay = o;
42566         }
42567         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42568         this.overlay.show();
42569         Roo.get(this.proxy).setDisplayed("block");
42570         var size = this.adapter.getElementSize(this);
42571         this.activeMinSize = this.getMinimumSize();;
42572         this.activeMaxSize = this.getMaximumSize();;
42573         var c1 = size - this.activeMinSize;
42574         var c2 = Math.max(this.activeMaxSize - size, 0);
42575         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42576             this.dd.resetConstraints();
42577             this.dd.setXConstraint(
42578                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42579                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42580             );
42581             this.dd.setYConstraint(0, 0);
42582         }else{
42583             this.dd.resetConstraints();
42584             this.dd.setXConstraint(0, 0);
42585             this.dd.setYConstraint(
42586                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42587                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42588             );
42589          }
42590         this.dragSpecs.startSize = size;
42591         this.dragSpecs.startPoint = [x, y];
42592         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42593     },
42594     
42595     /** 
42596      * @private Called after the drag operation by the DDProxy
42597      */
42598     onEndProxyDrag : function(e){
42599         Roo.get(this.proxy).setDisplayed(false);
42600         var endPoint = Roo.lib.Event.getXY(e);
42601         if(this.overlay){
42602             this.overlay.hide();
42603         }
42604         var newSize;
42605         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42606             newSize = this.dragSpecs.startSize + 
42607                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42608                     endPoint[0] - this.dragSpecs.startPoint[0] :
42609                     this.dragSpecs.startPoint[0] - endPoint[0]
42610                 );
42611         }else{
42612             newSize = this.dragSpecs.startSize + 
42613                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42614                     endPoint[1] - this.dragSpecs.startPoint[1] :
42615                     this.dragSpecs.startPoint[1] - endPoint[1]
42616                 );
42617         }
42618         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42619         if(newSize != this.dragSpecs.startSize){
42620             if(this.fireEvent('beforeapply', this, newSize) !== false){
42621                 this.adapter.setElementSize(this, newSize);
42622                 this.fireEvent("moved", this, newSize);
42623                 this.fireEvent("resize", this, newSize);
42624             }
42625         }
42626     },
42627     
42628     /**
42629      * Get the adapter this SplitBar uses
42630      * @return The adapter object
42631      */
42632     getAdapter : function(){
42633         return this.adapter;
42634     },
42635     
42636     /**
42637      * Set the adapter this SplitBar uses
42638      * @param {Object} adapter A SplitBar adapter object
42639      */
42640     setAdapter : function(adapter){
42641         this.adapter = adapter;
42642         this.adapter.init(this);
42643     },
42644     
42645     /**
42646      * Gets the minimum size for the resizing element
42647      * @return {Number} The minimum size
42648      */
42649     getMinimumSize : function(){
42650         return this.minSize;
42651     },
42652     
42653     /**
42654      * Sets the minimum size for the resizing element
42655      * @param {Number} minSize The minimum size
42656      */
42657     setMinimumSize : function(minSize){
42658         this.minSize = minSize;
42659     },
42660     
42661     /**
42662      * Gets the maximum size for the resizing element
42663      * @return {Number} The maximum size
42664      */
42665     getMaximumSize : function(){
42666         return this.maxSize;
42667     },
42668     
42669     /**
42670      * Sets the maximum size for the resizing element
42671      * @param {Number} maxSize The maximum size
42672      */
42673     setMaximumSize : function(maxSize){
42674         this.maxSize = maxSize;
42675     },
42676     
42677     /**
42678      * Sets the initialize size for the resizing element
42679      * @param {Number} size The initial size
42680      */
42681     setCurrentSize : function(size){
42682         var oldAnimate = this.animate;
42683         this.animate = false;
42684         this.adapter.setElementSize(this, size);
42685         this.animate = oldAnimate;
42686     },
42687     
42688     /**
42689      * Destroy this splitbar. 
42690      * @param {Boolean} removeEl True to remove the element
42691      */
42692     destroy : function(removeEl){
42693         if(this.shim){
42694             this.shim.remove();
42695         }
42696         this.dd.unreg();
42697         this.proxy.parentNode.removeChild(this.proxy);
42698         if(removeEl){
42699             this.el.remove();
42700         }
42701     }
42702 });
42703
42704 /**
42705  * @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.
42706  */
42707 Roo.bootstrap.SplitBar.createProxy = function(dir){
42708     var proxy = new Roo.Element(document.createElement("div"));
42709     proxy.unselectable();
42710     var cls = 'roo-splitbar-proxy';
42711     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42712     document.body.appendChild(proxy.dom);
42713     return proxy.dom;
42714 };
42715
42716 /** 
42717  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42718  * Default Adapter. It assumes the splitter and resizing element are not positioned
42719  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42720  */
42721 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42722 };
42723
42724 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42725     // do nothing for now
42726     init : function(s){
42727     
42728     },
42729     /**
42730      * Called before drag operations to get the current size of the resizing element. 
42731      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42732      */
42733      getElementSize : function(s){
42734         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42735             return s.resizingEl.getWidth();
42736         }else{
42737             return s.resizingEl.getHeight();
42738         }
42739     },
42740     
42741     /**
42742      * Called after drag operations to set the size of the resizing element.
42743      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42744      * @param {Number} newSize The new size to set
42745      * @param {Function} onComplete A function to be invoked when resizing is complete
42746      */
42747     setElementSize : function(s, newSize, onComplete){
42748         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42749             if(!s.animate){
42750                 s.resizingEl.setWidth(newSize);
42751                 if(onComplete){
42752                     onComplete(s, newSize);
42753                 }
42754             }else{
42755                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42756             }
42757         }else{
42758             
42759             if(!s.animate){
42760                 s.resizingEl.setHeight(newSize);
42761                 if(onComplete){
42762                     onComplete(s, newSize);
42763                 }
42764             }else{
42765                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42766             }
42767         }
42768     }
42769 };
42770
42771 /** 
42772  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42773  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42774  * Adapter that  moves the splitter element to align with the resized sizing element. 
42775  * Used with an absolute positioned SplitBar.
42776  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42777  * document.body, make sure you assign an id to the body element.
42778  */
42779 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42780     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42781     this.container = Roo.get(container);
42782 };
42783
42784 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42785     init : function(s){
42786         this.basic.init(s);
42787     },
42788     
42789     getElementSize : function(s){
42790         return this.basic.getElementSize(s);
42791     },
42792     
42793     setElementSize : function(s, newSize, onComplete){
42794         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42795     },
42796     
42797     moveSplitter : function(s){
42798         var yes = Roo.bootstrap.SplitBar;
42799         switch(s.placement){
42800             case yes.LEFT:
42801                 s.el.setX(s.resizingEl.getRight());
42802                 break;
42803             case yes.RIGHT:
42804                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42805                 break;
42806             case yes.TOP:
42807                 s.el.setY(s.resizingEl.getBottom());
42808                 break;
42809             case yes.BOTTOM:
42810                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42811                 break;
42812         }
42813     }
42814 };
42815
42816 /**
42817  * Orientation constant - Create a vertical SplitBar
42818  * @static
42819  * @type Number
42820  */
42821 Roo.bootstrap.SplitBar.VERTICAL = 1;
42822
42823 /**
42824  * Orientation constant - Create a horizontal SplitBar
42825  * @static
42826  * @type Number
42827  */
42828 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42829
42830 /**
42831  * Placement constant - The resizing element is to the left of the splitter element
42832  * @static
42833  * @type Number
42834  */
42835 Roo.bootstrap.SplitBar.LEFT = 1;
42836
42837 /**
42838  * Placement constant - The resizing element is to the right of the splitter element
42839  * @static
42840  * @type Number
42841  */
42842 Roo.bootstrap.SplitBar.RIGHT = 2;
42843
42844 /**
42845  * Placement constant - The resizing element is positioned above the splitter element
42846  * @static
42847  * @type Number
42848  */
42849 Roo.bootstrap.SplitBar.TOP = 3;
42850
42851 /**
42852  * Placement constant - The resizing element is positioned under splitter element
42853  * @static
42854  * @type Number
42855  */
42856 Roo.bootstrap.SplitBar.BOTTOM = 4;
42857 /*
42858  * Based on:
42859  * Ext JS Library 1.1.1
42860  * Copyright(c) 2006-2007, Ext JS, LLC.
42861  *
42862  * Originally Released Under LGPL - original licence link has changed is not relivant.
42863  *
42864  * Fork - LGPL
42865  * <script type="text/javascript">
42866  */
42867
42868 /**
42869  * @class Roo.bootstrap.layout.Manager
42870  * @extends Roo.bootstrap.Component
42871  * @abstract
42872  * Base class for layout managers.
42873  */
42874 Roo.bootstrap.layout.Manager = function(config)
42875 {
42876     this.monitorWindowResize = true; // do this before we apply configuration.
42877     
42878     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42879
42880
42881
42882
42883
42884     /** false to disable window resize monitoring @type Boolean */
42885     
42886     this.regions = {};
42887     this.addEvents({
42888         /**
42889          * @event layout
42890          * Fires when a layout is performed.
42891          * @param {Roo.LayoutManager} this
42892          */
42893         "layout" : true,
42894         /**
42895          * @event regionresized
42896          * Fires when the user resizes a region.
42897          * @param {Roo.LayoutRegion} region The resized region
42898          * @param {Number} newSize The new size (width for east/west, height for north/south)
42899          */
42900         "regionresized" : true,
42901         /**
42902          * @event regioncollapsed
42903          * Fires when a region is collapsed.
42904          * @param {Roo.LayoutRegion} region The collapsed region
42905          */
42906         "regioncollapsed" : true,
42907         /**
42908          * @event regionexpanded
42909          * Fires when a region is expanded.
42910          * @param {Roo.LayoutRegion} region The expanded region
42911          */
42912         "regionexpanded" : true
42913     });
42914     this.updating = false;
42915
42916     if (config.el) {
42917         this.el = Roo.get(config.el);
42918         this.initEvents();
42919     }
42920
42921 };
42922
42923 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42924
42925
42926     regions : null,
42927
42928     monitorWindowResize : true,
42929
42930
42931     updating : false,
42932
42933
42934     onRender : function(ct, position)
42935     {
42936         if(!this.el){
42937             this.el = Roo.get(ct);
42938             this.initEvents();
42939         }
42940         //this.fireEvent('render',this);
42941     },
42942
42943
42944     initEvents: function()
42945     {
42946
42947
42948         // ie scrollbar fix
42949         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42950             document.body.scroll = "no";
42951         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42952             this.el.position('relative');
42953         }
42954         this.id = this.el.id;
42955         this.el.addClass("roo-layout-container");
42956         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42957         if(this.el.dom != document.body ) {
42958             this.el.on('resize', this.layout,this);
42959             this.el.on('show', this.layout,this);
42960         }
42961
42962     },
42963
42964     /**
42965      * Returns true if this layout is currently being updated
42966      * @return {Boolean}
42967      */
42968     isUpdating : function(){
42969         return this.updating;
42970     },
42971
42972     /**
42973      * Suspend the LayoutManager from doing auto-layouts while
42974      * making multiple add or remove calls
42975      */
42976     beginUpdate : function(){
42977         this.updating = true;
42978     },
42979
42980     /**
42981      * Restore auto-layouts and optionally disable the manager from performing a layout
42982      * @param {Boolean} noLayout true to disable a layout update
42983      */
42984     endUpdate : function(noLayout){
42985         this.updating = false;
42986         if(!noLayout){
42987             this.layout();
42988         }
42989     },
42990
42991     layout: function(){
42992         // abstract...
42993     },
42994
42995     onRegionResized : function(region, newSize){
42996         this.fireEvent("regionresized", region, newSize);
42997         this.layout();
42998     },
42999
43000     onRegionCollapsed : function(region){
43001         this.fireEvent("regioncollapsed", region);
43002     },
43003
43004     onRegionExpanded : function(region){
43005         this.fireEvent("regionexpanded", region);
43006     },
43007
43008     /**
43009      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43010      * performs box-model adjustments.
43011      * @return {Object} The size as an object {width: (the width), height: (the height)}
43012      */
43013     getViewSize : function()
43014     {
43015         var size;
43016         if(this.el.dom != document.body){
43017             size = this.el.getSize();
43018         }else{
43019             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43020         }
43021         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43022         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43023         return size;
43024     },
43025
43026     /**
43027      * Returns the Element this layout is bound to.
43028      * @return {Roo.Element}
43029      */
43030     getEl : function(){
43031         return this.el;
43032     },
43033
43034     /**
43035      * Returns the specified region.
43036      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43037      * @return {Roo.LayoutRegion}
43038      */
43039     getRegion : function(target){
43040         return this.regions[target.toLowerCase()];
43041     },
43042
43043     onWindowResize : function(){
43044         if(this.monitorWindowResize){
43045             this.layout();
43046         }
43047     }
43048 });
43049 /*
43050  * Based on:
43051  * Ext JS Library 1.1.1
43052  * Copyright(c) 2006-2007, Ext JS, LLC.
43053  *
43054  * Originally Released Under LGPL - original licence link has changed is not relivant.
43055  *
43056  * Fork - LGPL
43057  * <script type="text/javascript">
43058  */
43059 /**
43060  * @class Roo.bootstrap.layout.Border
43061  * @extends Roo.bootstrap.layout.Manager
43062  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43063  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43064  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43065  * please see: examples/bootstrap/nested.html<br><br>
43066  
43067 <b>The container the layout is rendered into can be either the body element or any other element.
43068 If it is not the body element, the container needs to either be an absolute positioned element,
43069 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43070 the container size if it is not the body element.</b>
43071
43072 * @constructor
43073 * Create a new Border
43074 * @param {Object} config Configuration options
43075  */
43076 Roo.bootstrap.layout.Border = function(config){
43077     config = config || {};
43078     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43079     
43080     
43081     
43082     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43083         if(config[region]){
43084             config[region].region = region;
43085             this.addRegion(config[region]);
43086         }
43087     },this);
43088     
43089 };
43090
43091 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43092
43093 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43094     
43095         /**
43096          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43097          */
43098         /**
43099          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43100          */
43101         /**
43102          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43103          */
43104         /**
43105          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43106          */
43107         /**
43108          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43109          */
43110         
43111         
43112         
43113         
43114     parent : false, // this might point to a 'nest' or a ???
43115     
43116     /**
43117      * Creates and adds a new region if it doesn't already exist.
43118      * @param {String} target The target region key (north, south, east, west or center).
43119      * @param {Object} config The regions config object
43120      * @return {BorderLayoutRegion} The new region
43121      */
43122     addRegion : function(config)
43123     {
43124         if(!this.regions[config.region]){
43125             var r = this.factory(config);
43126             this.bindRegion(r);
43127         }
43128         return this.regions[config.region];
43129     },
43130
43131     // private (kinda)
43132     bindRegion : function(r){
43133         this.regions[r.config.region] = r;
43134         
43135         r.on("visibilitychange",    this.layout, this);
43136         r.on("paneladded",          this.layout, this);
43137         r.on("panelremoved",        this.layout, this);
43138         r.on("invalidated",         this.layout, this);
43139         r.on("resized",             this.onRegionResized, this);
43140         r.on("collapsed",           this.onRegionCollapsed, this);
43141         r.on("expanded",            this.onRegionExpanded, this);
43142     },
43143
43144     /**
43145      * Performs a layout update.
43146      */
43147     layout : function()
43148     {
43149         if(this.updating) {
43150             return;
43151         }
43152         
43153         // render all the rebions if they have not been done alreayd?
43154         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43155             if(this.regions[region] && !this.regions[region].bodyEl){
43156                 this.regions[region].onRender(this.el)
43157             }
43158         },this);
43159         
43160         var size = this.getViewSize();
43161         var w = size.width;
43162         var h = size.height;
43163         var centerW = w;
43164         var centerH = h;
43165         var centerY = 0;
43166         var centerX = 0;
43167         //var x = 0, y = 0;
43168
43169         var rs = this.regions;
43170         var north = rs["north"];
43171         var south = rs["south"]; 
43172         var west = rs["west"];
43173         var east = rs["east"];
43174         var center = rs["center"];
43175         //if(this.hideOnLayout){ // not supported anymore
43176             //c.el.setStyle("display", "none");
43177         //}
43178         if(north && north.isVisible()){
43179             var b = north.getBox();
43180             var m = north.getMargins();
43181             b.width = w - (m.left+m.right);
43182             b.x = m.left;
43183             b.y = m.top;
43184             centerY = b.height + b.y + m.bottom;
43185             centerH -= centerY;
43186             north.updateBox(this.safeBox(b));
43187         }
43188         if(south && south.isVisible()){
43189             var b = south.getBox();
43190             var m = south.getMargins();
43191             b.width = w - (m.left+m.right);
43192             b.x = m.left;
43193             var totalHeight = (b.height + m.top + m.bottom);
43194             b.y = h - totalHeight + m.top;
43195             centerH -= totalHeight;
43196             south.updateBox(this.safeBox(b));
43197         }
43198         if(west && west.isVisible()){
43199             var b = west.getBox();
43200             var m = west.getMargins();
43201             b.height = centerH - (m.top+m.bottom);
43202             b.x = m.left;
43203             b.y = centerY + m.top;
43204             var totalWidth = (b.width + m.left + m.right);
43205             centerX += totalWidth;
43206             centerW -= totalWidth;
43207             west.updateBox(this.safeBox(b));
43208         }
43209         if(east && east.isVisible()){
43210             var b = east.getBox();
43211             var m = east.getMargins();
43212             b.height = centerH - (m.top+m.bottom);
43213             var totalWidth = (b.width + m.left + m.right);
43214             b.x = w - totalWidth + m.left;
43215             b.y = centerY + m.top;
43216             centerW -= totalWidth;
43217             east.updateBox(this.safeBox(b));
43218         }
43219         if(center){
43220             var m = center.getMargins();
43221             var centerBox = {
43222                 x: centerX + m.left,
43223                 y: centerY + m.top,
43224                 width: centerW - (m.left+m.right),
43225                 height: centerH - (m.top+m.bottom)
43226             };
43227             //if(this.hideOnLayout){
43228                 //center.el.setStyle("display", "block");
43229             //}
43230             center.updateBox(this.safeBox(centerBox));
43231         }
43232         this.el.repaint();
43233         this.fireEvent("layout", this);
43234     },
43235
43236     // private
43237     safeBox : function(box){
43238         box.width = Math.max(0, box.width);
43239         box.height = Math.max(0, box.height);
43240         return box;
43241     },
43242
43243     /**
43244      * Adds a ContentPanel (or subclass) to this layout.
43245      * @param {String} target The target region key (north, south, east, west or center).
43246      * @param {Roo.ContentPanel} panel The panel to add
43247      * @return {Roo.ContentPanel} The added panel
43248      */
43249     add : function(target, panel){
43250          
43251         target = target.toLowerCase();
43252         return this.regions[target].add(panel);
43253     },
43254
43255     /**
43256      * Remove a ContentPanel (or subclass) to this layout.
43257      * @param {String} target The target region key (north, south, east, west or center).
43258      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43259      * @return {Roo.ContentPanel} The removed panel
43260      */
43261     remove : function(target, panel){
43262         target = target.toLowerCase();
43263         return this.regions[target].remove(panel);
43264     },
43265
43266     /**
43267      * Searches all regions for a panel with the specified id
43268      * @param {String} panelId
43269      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43270      */
43271     findPanel : function(panelId){
43272         var rs = this.regions;
43273         for(var target in rs){
43274             if(typeof rs[target] != "function"){
43275                 var p = rs[target].getPanel(panelId);
43276                 if(p){
43277                     return p;
43278                 }
43279             }
43280         }
43281         return null;
43282     },
43283
43284     /**
43285      * Searches all regions for a panel with the specified id and activates (shows) it.
43286      * @param {String/ContentPanel} panelId The panels id or the panel itself
43287      * @return {Roo.ContentPanel} The shown panel or null
43288      */
43289     showPanel : function(panelId) {
43290       var rs = this.regions;
43291       for(var target in rs){
43292          var r = rs[target];
43293          if(typeof r != "function"){
43294             if(r.hasPanel(panelId)){
43295                return r.showPanel(panelId);
43296             }
43297          }
43298       }
43299       return null;
43300    },
43301
43302    /**
43303      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43304      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43305      */
43306    /*
43307     restoreState : function(provider){
43308         if(!provider){
43309             provider = Roo.state.Manager;
43310         }
43311         var sm = new Roo.LayoutStateManager();
43312         sm.init(this, provider);
43313     },
43314 */
43315  
43316  
43317     /**
43318      * Adds a xtype elements to the layout.
43319      * <pre><code>
43320
43321 layout.addxtype({
43322        xtype : 'ContentPanel',
43323        region: 'west',
43324        items: [ .... ]
43325    }
43326 );
43327
43328 layout.addxtype({
43329         xtype : 'NestedLayoutPanel',
43330         region: 'west',
43331         layout: {
43332            center: { },
43333            west: { }   
43334         },
43335         items : [ ... list of content panels or nested layout panels.. ]
43336    }
43337 );
43338 </code></pre>
43339      * @param {Object} cfg Xtype definition of item to add.
43340      */
43341     addxtype : function(cfg)
43342     {
43343         // basically accepts a pannel...
43344         // can accept a layout region..!?!?
43345         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43346         
43347         
43348         // theory?  children can only be panels??
43349         
43350         //if (!cfg.xtype.match(/Panel$/)) {
43351         //    return false;
43352         //}
43353         var ret = false;
43354         
43355         if (typeof(cfg.region) == 'undefined') {
43356             Roo.log("Failed to add Panel, region was not set");
43357             Roo.log(cfg);
43358             return false;
43359         }
43360         var region = cfg.region;
43361         delete cfg.region;
43362         
43363           
43364         var xitems = [];
43365         if (cfg.items) {
43366             xitems = cfg.items;
43367             delete cfg.items;
43368         }
43369         var nb = false;
43370         
43371         if ( region == 'center') {
43372             Roo.log("Center: " + cfg.title);
43373         }
43374         
43375         
43376         switch(cfg.xtype) 
43377         {
43378             case 'Content':  // ContentPanel (el, cfg)
43379             case 'Scroll':  // ContentPanel (el, cfg)
43380             case 'View': 
43381                 cfg.autoCreate = cfg.autoCreate || true;
43382                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43383                 //} else {
43384                 //    var el = this.el.createChild();
43385                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43386                 //}
43387                 
43388                 this.add(region, ret);
43389                 break;
43390             
43391             /*
43392             case 'TreePanel': // our new panel!
43393                 cfg.el = this.el.createChild();
43394                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43395                 this.add(region, ret);
43396                 break;
43397             */
43398             
43399             case 'Nest': 
43400                 // create a new Layout (which is  a Border Layout...
43401                 
43402                 var clayout = cfg.layout;
43403                 clayout.el  = this.el.createChild();
43404                 clayout.items   = clayout.items  || [];
43405                 
43406                 delete cfg.layout;
43407                 
43408                 // replace this exitems with the clayout ones..
43409                 xitems = clayout.items;
43410                  
43411                 // force background off if it's in center...
43412                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43413                     cfg.background = false;
43414                 }
43415                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43416                 
43417                 
43418                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43419                 //console.log('adding nested layout panel '  + cfg.toSource());
43420                 this.add(region, ret);
43421                 nb = {}; /// find first...
43422                 break;
43423             
43424             case 'Grid':
43425                 
43426                 // needs grid and region
43427                 
43428                 //var el = this.getRegion(region).el.createChild();
43429                 /*
43430                  *var el = this.el.createChild();
43431                 // create the grid first...
43432                 cfg.grid.container = el;
43433                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43434                 */
43435                 
43436                 if (region == 'center' && this.active ) {
43437                     cfg.background = false;
43438                 }
43439                 
43440                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43441                 
43442                 this.add(region, ret);
43443                 /*
43444                 if (cfg.background) {
43445                     // render grid on panel activation (if panel background)
43446                     ret.on('activate', function(gp) {
43447                         if (!gp.grid.rendered) {
43448                     //        gp.grid.render(el);
43449                         }
43450                     });
43451                 } else {
43452                   //  cfg.grid.render(el);
43453                 }
43454                 */
43455                 break;
43456            
43457            
43458             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43459                 // it was the old xcomponent building that caused this before.
43460                 // espeically if border is the top element in the tree.
43461                 ret = this;
43462                 break; 
43463                 
43464                     
43465                 
43466                 
43467                 
43468             default:
43469                 /*
43470                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43471                     
43472                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43473                     this.add(region, ret);
43474                 } else {
43475                 */
43476                     Roo.log(cfg);
43477                     throw "Can not add '" + cfg.xtype + "' to Border";
43478                     return null;
43479              
43480                                 
43481              
43482         }
43483         this.beginUpdate();
43484         // add children..
43485         var region = '';
43486         var abn = {};
43487         Roo.each(xitems, function(i)  {
43488             region = nb && i.region ? i.region : false;
43489             
43490             var add = ret.addxtype(i);
43491            
43492             if (region) {
43493                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43494                 if (!i.background) {
43495                     abn[region] = nb[region] ;
43496                 }
43497             }
43498             
43499         });
43500         this.endUpdate();
43501
43502         // make the last non-background panel active..
43503         //if (nb) { Roo.log(abn); }
43504         if (nb) {
43505             
43506             for(var r in abn) {
43507                 region = this.getRegion(r);
43508                 if (region) {
43509                     // tried using nb[r], but it does not work..
43510                      
43511                     region.showPanel(abn[r]);
43512                    
43513                 }
43514             }
43515         }
43516         return ret;
43517         
43518     },
43519     
43520     
43521 // private
43522     factory : function(cfg)
43523     {
43524         
43525         var validRegions = Roo.bootstrap.layout.Border.regions;
43526
43527         var target = cfg.region;
43528         cfg.mgr = this;
43529         
43530         var r = Roo.bootstrap.layout;
43531         Roo.log(target);
43532         switch(target){
43533             case "north":
43534                 return new r.North(cfg);
43535             case "south":
43536                 return new r.South(cfg);
43537             case "east":
43538                 return new r.East(cfg);
43539             case "west":
43540                 return new r.West(cfg);
43541             case "center":
43542                 return new r.Center(cfg);
43543         }
43544         throw 'Layout region "'+target+'" not supported.';
43545     }
43546     
43547     
43548 });
43549  /*
43550  * Based on:
43551  * Ext JS Library 1.1.1
43552  * Copyright(c) 2006-2007, Ext JS, LLC.
43553  *
43554  * Originally Released Under LGPL - original licence link has changed is not relivant.
43555  *
43556  * Fork - LGPL
43557  * <script type="text/javascript">
43558  */
43559  
43560 /**
43561  * @class Roo.bootstrap.layout.Basic
43562  * @extends Roo.util.Observable
43563  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43564  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43565  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43566  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43567  * @cfg {string}   region  the region that it inhabits..
43568  * @cfg {bool}   skipConfig skip config?
43569  * 
43570
43571  */
43572 Roo.bootstrap.layout.Basic = function(config){
43573     
43574     this.mgr = config.mgr;
43575     
43576     this.position = config.region;
43577     
43578     var skipConfig = config.skipConfig;
43579     
43580     this.events = {
43581         /**
43582          * @scope Roo.BasicLayoutRegion
43583          */
43584         
43585         /**
43586          * @event beforeremove
43587          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43588          * @param {Roo.LayoutRegion} this
43589          * @param {Roo.ContentPanel} panel The panel
43590          * @param {Object} e The cancel event object
43591          */
43592         "beforeremove" : true,
43593         /**
43594          * @event invalidated
43595          * Fires when the layout for this region is changed.
43596          * @param {Roo.LayoutRegion} this
43597          */
43598         "invalidated" : true,
43599         /**
43600          * @event visibilitychange
43601          * Fires when this region is shown or hidden 
43602          * @param {Roo.LayoutRegion} this
43603          * @param {Boolean} visibility true or false
43604          */
43605         "visibilitychange" : true,
43606         /**
43607          * @event paneladded
43608          * Fires when a panel is added. 
43609          * @param {Roo.LayoutRegion} this
43610          * @param {Roo.ContentPanel} panel The panel
43611          */
43612         "paneladded" : true,
43613         /**
43614          * @event panelremoved
43615          * Fires when a panel is removed. 
43616          * @param {Roo.LayoutRegion} this
43617          * @param {Roo.ContentPanel} panel The panel
43618          */
43619         "panelremoved" : true,
43620         /**
43621          * @event beforecollapse
43622          * Fires when this region before collapse.
43623          * @param {Roo.LayoutRegion} this
43624          */
43625         "beforecollapse" : true,
43626         /**
43627          * @event collapsed
43628          * Fires when this region is collapsed.
43629          * @param {Roo.LayoutRegion} this
43630          */
43631         "collapsed" : true,
43632         /**
43633          * @event expanded
43634          * Fires when this region is expanded.
43635          * @param {Roo.LayoutRegion} this
43636          */
43637         "expanded" : true,
43638         /**
43639          * @event slideshow
43640          * Fires when this region is slid into view.
43641          * @param {Roo.LayoutRegion} this
43642          */
43643         "slideshow" : true,
43644         /**
43645          * @event slidehide
43646          * Fires when this region slides out of view. 
43647          * @param {Roo.LayoutRegion} this
43648          */
43649         "slidehide" : true,
43650         /**
43651          * @event panelactivated
43652          * Fires when a panel is activated. 
43653          * @param {Roo.LayoutRegion} this
43654          * @param {Roo.ContentPanel} panel The activated panel
43655          */
43656         "panelactivated" : true,
43657         /**
43658          * @event resized
43659          * Fires when the user resizes this region. 
43660          * @param {Roo.LayoutRegion} this
43661          * @param {Number} newSize The new size (width for east/west, height for north/south)
43662          */
43663         "resized" : true
43664     };
43665     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43666     this.panels = new Roo.util.MixedCollection();
43667     this.panels.getKey = this.getPanelId.createDelegate(this);
43668     this.box = null;
43669     this.activePanel = null;
43670     // ensure listeners are added...
43671     
43672     if (config.listeners || config.events) {
43673         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43674             listeners : config.listeners || {},
43675             events : config.events || {}
43676         });
43677     }
43678     
43679     if(skipConfig !== true){
43680         this.applyConfig(config);
43681     }
43682 };
43683
43684 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43685 {
43686     getPanelId : function(p){
43687         return p.getId();
43688     },
43689     
43690     applyConfig : function(config){
43691         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43692         this.config = config;
43693         
43694     },
43695     
43696     /**
43697      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43698      * the width, for horizontal (north, south) the height.
43699      * @param {Number} newSize The new width or height
43700      */
43701     resizeTo : function(newSize){
43702         var el = this.el ? this.el :
43703                  (this.activePanel ? this.activePanel.getEl() : null);
43704         if(el){
43705             switch(this.position){
43706                 case "east":
43707                 case "west":
43708                     el.setWidth(newSize);
43709                     this.fireEvent("resized", this, newSize);
43710                 break;
43711                 case "north":
43712                 case "south":
43713                     el.setHeight(newSize);
43714                     this.fireEvent("resized", this, newSize);
43715                 break;                
43716             }
43717         }
43718     },
43719     
43720     getBox : function(){
43721         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43722     },
43723     
43724     getMargins : function(){
43725         return this.margins;
43726     },
43727     
43728     updateBox : function(box){
43729         this.box = box;
43730         var el = this.activePanel.getEl();
43731         el.dom.style.left = box.x + "px";
43732         el.dom.style.top = box.y + "px";
43733         this.activePanel.setSize(box.width, box.height);
43734     },
43735     
43736     /**
43737      * Returns the container element for this region.
43738      * @return {Roo.Element}
43739      */
43740     getEl : function(){
43741         return this.activePanel;
43742     },
43743     
43744     /**
43745      * Returns true if this region is currently visible.
43746      * @return {Boolean}
43747      */
43748     isVisible : function(){
43749         return this.activePanel ? true : false;
43750     },
43751     
43752     setActivePanel : function(panel){
43753         panel = this.getPanel(panel);
43754         if(this.activePanel && this.activePanel != panel){
43755             this.activePanel.setActiveState(false);
43756             this.activePanel.getEl().setLeftTop(-10000,-10000);
43757         }
43758         this.activePanel = panel;
43759         panel.setActiveState(true);
43760         if(this.box){
43761             panel.setSize(this.box.width, this.box.height);
43762         }
43763         this.fireEvent("panelactivated", this, panel);
43764         this.fireEvent("invalidated");
43765     },
43766     
43767     /**
43768      * Show the specified panel.
43769      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43770      * @return {Roo.ContentPanel} The shown panel or null
43771      */
43772     showPanel : function(panel){
43773         panel = this.getPanel(panel);
43774         if(panel){
43775             this.setActivePanel(panel);
43776         }
43777         return panel;
43778     },
43779     
43780     /**
43781      * Get the active panel for this region.
43782      * @return {Roo.ContentPanel} The active panel or null
43783      */
43784     getActivePanel : function(){
43785         return this.activePanel;
43786     },
43787     
43788     /**
43789      * Add the passed ContentPanel(s)
43790      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43791      * @return {Roo.ContentPanel} The panel added (if only one was added)
43792      */
43793     add : function(panel){
43794         if(arguments.length > 1){
43795             for(var i = 0, len = arguments.length; i < len; i++) {
43796                 this.add(arguments[i]);
43797             }
43798             return null;
43799         }
43800         if(this.hasPanel(panel)){
43801             this.showPanel(panel);
43802             return panel;
43803         }
43804         var el = panel.getEl();
43805         if(el.dom.parentNode != this.mgr.el.dom){
43806             this.mgr.el.dom.appendChild(el.dom);
43807         }
43808         if(panel.setRegion){
43809             panel.setRegion(this);
43810         }
43811         this.panels.add(panel);
43812         el.setStyle("position", "absolute");
43813         if(!panel.background){
43814             this.setActivePanel(panel);
43815             if(this.config.initialSize && this.panels.getCount()==1){
43816                 this.resizeTo(this.config.initialSize);
43817             }
43818         }
43819         this.fireEvent("paneladded", this, panel);
43820         return panel;
43821     },
43822     
43823     /**
43824      * Returns true if the panel is in this region.
43825      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43826      * @return {Boolean}
43827      */
43828     hasPanel : function(panel){
43829         if(typeof panel == "object"){ // must be panel obj
43830             panel = panel.getId();
43831         }
43832         return this.getPanel(panel) ? true : false;
43833     },
43834     
43835     /**
43836      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43837      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43838      * @param {Boolean} preservePanel Overrides the config preservePanel option
43839      * @return {Roo.ContentPanel} The panel that was removed
43840      */
43841     remove : function(panel, preservePanel){
43842         panel = this.getPanel(panel);
43843         if(!panel){
43844             return null;
43845         }
43846         var e = {};
43847         this.fireEvent("beforeremove", this, panel, e);
43848         if(e.cancel === true){
43849             return null;
43850         }
43851         var panelId = panel.getId();
43852         this.panels.removeKey(panelId);
43853         return panel;
43854     },
43855     
43856     /**
43857      * Returns the panel specified or null if it's not in this region.
43858      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43859      * @return {Roo.ContentPanel}
43860      */
43861     getPanel : function(id){
43862         if(typeof id == "object"){ // must be panel obj
43863             return id;
43864         }
43865         return this.panels.get(id);
43866     },
43867     
43868     /**
43869      * Returns this regions position (north/south/east/west/center).
43870      * @return {String} 
43871      */
43872     getPosition: function(){
43873         return this.position;    
43874     }
43875 });/*
43876  * Based on:
43877  * Ext JS Library 1.1.1
43878  * Copyright(c) 2006-2007, Ext JS, LLC.
43879  *
43880  * Originally Released Under LGPL - original licence link has changed is not relivant.
43881  *
43882  * Fork - LGPL
43883  * <script type="text/javascript">
43884  */
43885  
43886 /**
43887  * @class Roo.bootstrap.layout.Region
43888  * @extends Roo.bootstrap.layout.Basic
43889  * This class represents a region in a layout manager.
43890  
43891  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43892  * @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})
43893  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43894  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43895  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43896  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43897  * @cfg {String}    title           The title for the region (overrides panel titles)
43898  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43899  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43900  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43901  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43902  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43903  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43904  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43905  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43906  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43907  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43908
43909  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43910  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43911  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43912  * @cfg {Number}    width           For East/West panels
43913  * @cfg {Number}    height          For North/South panels
43914  * @cfg {Boolean}   split           To show the splitter
43915  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43916  * 
43917  * @cfg {string}   cls             Extra CSS classes to add to region
43918  * 
43919  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43920  * @cfg {string}   region  the region that it inhabits..
43921  *
43922
43923  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43924  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43925
43926  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43927  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43928  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43929  */
43930 Roo.bootstrap.layout.Region = function(config)
43931 {
43932     this.applyConfig(config);
43933
43934     var mgr = config.mgr;
43935     var pos = config.region;
43936     config.skipConfig = true;
43937     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43938     
43939     if (mgr.el) {
43940         this.onRender(mgr.el);   
43941     }
43942      
43943     this.visible = true;
43944     this.collapsed = false;
43945     this.unrendered_panels = [];
43946 };
43947
43948 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43949
43950     position: '', // set by wrapper (eg. north/south etc..)
43951     unrendered_panels : null,  // unrendered panels.
43952     
43953     tabPosition : false,
43954     
43955     mgr: false, // points to 'Border'
43956     
43957     
43958     createBody : function(){
43959         /** This region's body element 
43960         * @type Roo.Element */
43961         this.bodyEl = this.el.createChild({
43962                 tag: "div",
43963                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43964         });
43965     },
43966
43967     onRender: function(ctr, pos)
43968     {
43969         var dh = Roo.DomHelper;
43970         /** This region's container element 
43971         * @type Roo.Element */
43972         this.el = dh.append(ctr.dom, {
43973                 tag: "div",
43974                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43975             }, true);
43976         /** This region's title element 
43977         * @type Roo.Element */
43978     
43979         this.titleEl = dh.append(this.el.dom,  {
43980                 tag: "div",
43981                 unselectable: "on",
43982                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43983                 children:[
43984                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43985                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43986                 ]
43987             }, true);
43988         
43989         this.titleEl.enableDisplayMode();
43990         /** This region's title text element 
43991         * @type HTMLElement */
43992         this.titleTextEl = this.titleEl.dom.firstChild;
43993         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43994         /*
43995         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43996         this.closeBtn.enableDisplayMode();
43997         this.closeBtn.on("click", this.closeClicked, this);
43998         this.closeBtn.hide();
43999     */
44000         this.createBody(this.config);
44001         if(this.config.hideWhenEmpty){
44002             this.hide();
44003             this.on("paneladded", this.validateVisibility, this);
44004             this.on("panelremoved", this.validateVisibility, this);
44005         }
44006         if(this.autoScroll){
44007             this.bodyEl.setStyle("overflow", "auto");
44008         }else{
44009             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44010         }
44011         //if(c.titlebar !== false){
44012             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44013                 this.titleEl.hide();
44014             }else{
44015                 this.titleEl.show();
44016                 if(this.config.title){
44017                     this.titleTextEl.innerHTML = this.config.title;
44018                 }
44019             }
44020         //}
44021         if(this.config.collapsed){
44022             this.collapse(true);
44023         }
44024         if(this.config.hidden){
44025             this.hide();
44026         }
44027         
44028         if (this.unrendered_panels && this.unrendered_panels.length) {
44029             for (var i =0;i< this.unrendered_panels.length; i++) {
44030                 this.add(this.unrendered_panels[i]);
44031             }
44032             this.unrendered_panels = null;
44033             
44034         }
44035         
44036     },
44037     
44038     applyConfig : function(c)
44039     {
44040         /*
44041          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44042             var dh = Roo.DomHelper;
44043             if(c.titlebar !== false){
44044                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44045                 this.collapseBtn.on("click", this.collapse, this);
44046                 this.collapseBtn.enableDisplayMode();
44047                 /*
44048                 if(c.showPin === true || this.showPin){
44049                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44050                     this.stickBtn.enableDisplayMode();
44051                     this.stickBtn.on("click", this.expand, this);
44052                     this.stickBtn.hide();
44053                 }
44054                 
44055             }
44056             */
44057             /** This region's collapsed element
44058             * @type Roo.Element */
44059             /*
44060              *
44061             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44062                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44063             ]}, true);
44064             
44065             if(c.floatable !== false){
44066                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44067                this.collapsedEl.on("click", this.collapseClick, this);
44068             }
44069
44070             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44071                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44072                    id: "message", unselectable: "on", style:{"float":"left"}});
44073                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44074              }
44075             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44076             this.expandBtn.on("click", this.expand, this);
44077             
44078         }
44079         
44080         if(this.collapseBtn){
44081             this.collapseBtn.setVisible(c.collapsible == true);
44082         }
44083         
44084         this.cmargins = c.cmargins || this.cmargins ||
44085                          (this.position == "west" || this.position == "east" ?
44086                              {top: 0, left: 2, right:2, bottom: 0} :
44087                              {top: 2, left: 0, right:0, bottom: 2});
44088         */
44089         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44090         
44091         
44092         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44093         
44094         this.autoScroll = c.autoScroll || false;
44095         
44096         
44097        
44098         
44099         this.duration = c.duration || .30;
44100         this.slideDuration = c.slideDuration || .45;
44101         this.config = c;
44102        
44103     },
44104     /**
44105      * Returns true if this region is currently visible.
44106      * @return {Boolean}
44107      */
44108     isVisible : function(){
44109         return this.visible;
44110     },
44111
44112     /**
44113      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44114      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44115      */
44116     //setCollapsedTitle : function(title){
44117     //    title = title || "&#160;";
44118      //   if(this.collapsedTitleTextEl){
44119       //      this.collapsedTitleTextEl.innerHTML = title;
44120        // }
44121     //},
44122
44123     getBox : function(){
44124         var b;
44125       //  if(!this.collapsed){
44126             b = this.el.getBox(false, true);
44127        // }else{
44128           //  b = this.collapsedEl.getBox(false, true);
44129         //}
44130         return b;
44131     },
44132
44133     getMargins : function(){
44134         return this.margins;
44135         //return this.collapsed ? this.cmargins : this.margins;
44136     },
44137 /*
44138     highlight : function(){
44139         this.el.addClass("x-layout-panel-dragover");
44140     },
44141
44142     unhighlight : function(){
44143         this.el.removeClass("x-layout-panel-dragover");
44144     },
44145 */
44146     updateBox : function(box)
44147     {
44148         if (!this.bodyEl) {
44149             return; // not rendered yet..
44150         }
44151         
44152         this.box = box;
44153         if(!this.collapsed){
44154             this.el.dom.style.left = box.x + "px";
44155             this.el.dom.style.top = box.y + "px";
44156             this.updateBody(box.width, box.height);
44157         }else{
44158             this.collapsedEl.dom.style.left = box.x + "px";
44159             this.collapsedEl.dom.style.top = box.y + "px";
44160             this.collapsedEl.setSize(box.width, box.height);
44161         }
44162         if(this.tabs){
44163             this.tabs.autoSizeTabs();
44164         }
44165     },
44166
44167     updateBody : function(w, h)
44168     {
44169         if(w !== null){
44170             this.el.setWidth(w);
44171             w -= this.el.getBorderWidth("rl");
44172             if(this.config.adjustments){
44173                 w += this.config.adjustments[0];
44174             }
44175         }
44176         if(h !== null && h > 0){
44177             this.el.setHeight(h);
44178             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44179             h -= this.el.getBorderWidth("tb");
44180             if(this.config.adjustments){
44181                 h += this.config.adjustments[1];
44182             }
44183             this.bodyEl.setHeight(h);
44184             if(this.tabs){
44185                 h = this.tabs.syncHeight(h);
44186             }
44187         }
44188         if(this.panelSize){
44189             w = w !== null ? w : this.panelSize.width;
44190             h = h !== null ? h : this.panelSize.height;
44191         }
44192         if(this.activePanel){
44193             var el = this.activePanel.getEl();
44194             w = w !== null ? w : el.getWidth();
44195             h = h !== null ? h : el.getHeight();
44196             this.panelSize = {width: w, height: h};
44197             this.activePanel.setSize(w, h);
44198         }
44199         if(Roo.isIE && this.tabs){
44200             this.tabs.el.repaint();
44201         }
44202     },
44203
44204     /**
44205      * Returns the container element for this region.
44206      * @return {Roo.Element}
44207      */
44208     getEl : function(){
44209         return this.el;
44210     },
44211
44212     /**
44213      * Hides this region.
44214      */
44215     hide : function(){
44216         //if(!this.collapsed){
44217             this.el.dom.style.left = "-2000px";
44218             this.el.hide();
44219         //}else{
44220          //   this.collapsedEl.dom.style.left = "-2000px";
44221          //   this.collapsedEl.hide();
44222        // }
44223         this.visible = false;
44224         this.fireEvent("visibilitychange", this, false);
44225     },
44226
44227     /**
44228      * Shows this region if it was previously hidden.
44229      */
44230     show : function(){
44231         //if(!this.collapsed){
44232             this.el.show();
44233         //}else{
44234         //    this.collapsedEl.show();
44235        // }
44236         this.visible = true;
44237         this.fireEvent("visibilitychange", this, true);
44238     },
44239 /*
44240     closeClicked : function(){
44241         if(this.activePanel){
44242             this.remove(this.activePanel);
44243         }
44244     },
44245
44246     collapseClick : function(e){
44247         if(this.isSlid){
44248            e.stopPropagation();
44249            this.slideIn();
44250         }else{
44251            e.stopPropagation();
44252            this.slideOut();
44253         }
44254     },
44255 */
44256     /**
44257      * Collapses this region.
44258      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44259      */
44260     /*
44261     collapse : function(skipAnim, skipCheck = false){
44262         if(this.collapsed) {
44263             return;
44264         }
44265         
44266         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44267             
44268             this.collapsed = true;
44269             if(this.split){
44270                 this.split.el.hide();
44271             }
44272             if(this.config.animate && skipAnim !== true){
44273                 this.fireEvent("invalidated", this);
44274                 this.animateCollapse();
44275             }else{
44276                 this.el.setLocation(-20000,-20000);
44277                 this.el.hide();
44278                 this.collapsedEl.show();
44279                 this.fireEvent("collapsed", this);
44280                 this.fireEvent("invalidated", this);
44281             }
44282         }
44283         
44284     },
44285 */
44286     animateCollapse : function(){
44287         // overridden
44288     },
44289
44290     /**
44291      * Expands this region if it was previously collapsed.
44292      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44293      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44294      */
44295     /*
44296     expand : function(e, skipAnim){
44297         if(e) {
44298             e.stopPropagation();
44299         }
44300         if(!this.collapsed || this.el.hasActiveFx()) {
44301             return;
44302         }
44303         if(this.isSlid){
44304             this.afterSlideIn();
44305             skipAnim = true;
44306         }
44307         this.collapsed = false;
44308         if(this.config.animate && skipAnim !== true){
44309             this.animateExpand();
44310         }else{
44311             this.el.show();
44312             if(this.split){
44313                 this.split.el.show();
44314             }
44315             this.collapsedEl.setLocation(-2000,-2000);
44316             this.collapsedEl.hide();
44317             this.fireEvent("invalidated", this);
44318             this.fireEvent("expanded", this);
44319         }
44320     },
44321 */
44322     animateExpand : function(){
44323         // overridden
44324     },
44325
44326     initTabs : function()
44327     {
44328         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44329         
44330         var ts = new Roo.bootstrap.panel.Tabs({
44331             el: this.bodyEl.dom,
44332             region : this,
44333             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44334             disableTooltips: this.config.disableTabTips,
44335             toolbar : this.config.toolbar
44336         });
44337         
44338         if(this.config.hideTabs){
44339             ts.stripWrap.setDisplayed(false);
44340         }
44341         this.tabs = ts;
44342         ts.resizeTabs = this.config.resizeTabs === true;
44343         ts.minTabWidth = this.config.minTabWidth || 40;
44344         ts.maxTabWidth = this.config.maxTabWidth || 250;
44345         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44346         ts.monitorResize = false;
44347         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44348         ts.bodyEl.addClass('roo-layout-tabs-body');
44349         this.panels.each(this.initPanelAsTab, this);
44350     },
44351
44352     initPanelAsTab : function(panel){
44353         var ti = this.tabs.addTab(
44354             panel.getEl().id,
44355             panel.getTitle(),
44356             null,
44357             this.config.closeOnTab && panel.isClosable(),
44358             panel.tpl
44359         );
44360         if(panel.tabTip !== undefined){
44361             ti.setTooltip(panel.tabTip);
44362         }
44363         ti.on("activate", function(){
44364               this.setActivePanel(panel);
44365         }, this);
44366         
44367         if(this.config.closeOnTab){
44368             ti.on("beforeclose", function(t, e){
44369                 e.cancel = true;
44370                 this.remove(panel);
44371             }, this);
44372         }
44373         
44374         panel.tabItem = ti;
44375         
44376         return ti;
44377     },
44378
44379     updatePanelTitle : function(panel, title)
44380     {
44381         if(this.activePanel == panel){
44382             this.updateTitle(title);
44383         }
44384         if(this.tabs){
44385             var ti = this.tabs.getTab(panel.getEl().id);
44386             ti.setText(title);
44387             if(panel.tabTip !== undefined){
44388                 ti.setTooltip(panel.tabTip);
44389             }
44390         }
44391     },
44392
44393     updateTitle : function(title){
44394         if(this.titleTextEl && !this.config.title){
44395             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44396         }
44397     },
44398
44399     setActivePanel : function(panel)
44400     {
44401         panel = this.getPanel(panel);
44402         if(this.activePanel && this.activePanel != panel){
44403             if(this.activePanel.setActiveState(false) === false){
44404                 return;
44405             }
44406         }
44407         this.activePanel = panel;
44408         panel.setActiveState(true);
44409         if(this.panelSize){
44410             panel.setSize(this.panelSize.width, this.panelSize.height);
44411         }
44412         if(this.closeBtn){
44413             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44414         }
44415         this.updateTitle(panel.getTitle());
44416         if(this.tabs){
44417             this.fireEvent("invalidated", this);
44418         }
44419         this.fireEvent("panelactivated", this, panel);
44420     },
44421
44422     /**
44423      * Shows the specified panel.
44424      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44425      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44426      */
44427     showPanel : function(panel)
44428     {
44429         panel = this.getPanel(panel);
44430         if(panel){
44431             if(this.tabs){
44432                 var tab = this.tabs.getTab(panel.getEl().id);
44433                 if(tab.isHidden()){
44434                     this.tabs.unhideTab(tab.id);
44435                 }
44436                 tab.activate();
44437             }else{
44438                 this.setActivePanel(panel);
44439             }
44440         }
44441         return panel;
44442     },
44443
44444     /**
44445      * Get the active panel for this region.
44446      * @return {Roo.ContentPanel} The active panel or null
44447      */
44448     getActivePanel : function(){
44449         return this.activePanel;
44450     },
44451
44452     validateVisibility : function(){
44453         if(this.panels.getCount() < 1){
44454             this.updateTitle("&#160;");
44455             this.closeBtn.hide();
44456             this.hide();
44457         }else{
44458             if(!this.isVisible()){
44459                 this.show();
44460             }
44461         }
44462     },
44463
44464     /**
44465      * Adds the passed ContentPanel(s) to this region.
44466      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44467      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44468      */
44469     add : function(panel)
44470     {
44471         if(arguments.length > 1){
44472             for(var i = 0, len = arguments.length; i < len; i++) {
44473                 this.add(arguments[i]);
44474             }
44475             return null;
44476         }
44477         
44478         // if we have not been rendered yet, then we can not really do much of this..
44479         if (!this.bodyEl) {
44480             this.unrendered_panels.push(panel);
44481             return panel;
44482         }
44483         
44484         
44485         
44486         
44487         if(this.hasPanel(panel)){
44488             this.showPanel(panel);
44489             return panel;
44490         }
44491         panel.setRegion(this);
44492         this.panels.add(panel);
44493        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44494             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44495             // and hide them... ???
44496             this.bodyEl.dom.appendChild(panel.getEl().dom);
44497             if(panel.background !== true){
44498                 this.setActivePanel(panel);
44499             }
44500             this.fireEvent("paneladded", this, panel);
44501             return panel;
44502         }
44503         */
44504         if(!this.tabs){
44505             this.initTabs();
44506         }else{
44507             this.initPanelAsTab(panel);
44508         }
44509         
44510         
44511         if(panel.background !== true){
44512             this.tabs.activate(panel.getEl().id);
44513         }
44514         this.fireEvent("paneladded", this, panel);
44515         return panel;
44516     },
44517
44518     /**
44519      * Hides the tab for the specified panel.
44520      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44521      */
44522     hidePanel : function(panel){
44523         if(this.tabs && (panel = this.getPanel(panel))){
44524             this.tabs.hideTab(panel.getEl().id);
44525         }
44526     },
44527
44528     /**
44529      * Unhides the tab for a previously hidden panel.
44530      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44531      */
44532     unhidePanel : function(panel){
44533         if(this.tabs && (panel = this.getPanel(panel))){
44534             this.tabs.unhideTab(panel.getEl().id);
44535         }
44536     },
44537
44538     clearPanels : function(){
44539         while(this.panels.getCount() > 0){
44540              this.remove(this.panels.first());
44541         }
44542     },
44543
44544     /**
44545      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44546      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44547      * @param {Boolean} preservePanel Overrides the config preservePanel option
44548      * @return {Roo.ContentPanel} The panel that was removed
44549      */
44550     remove : function(panel, preservePanel)
44551     {
44552         panel = this.getPanel(panel);
44553         if(!panel){
44554             return null;
44555         }
44556         var e = {};
44557         this.fireEvent("beforeremove", this, panel, e);
44558         if(e.cancel === true){
44559             return null;
44560         }
44561         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44562         var panelId = panel.getId();
44563         this.panels.removeKey(panelId);
44564         if(preservePanel){
44565             document.body.appendChild(panel.getEl().dom);
44566         }
44567         if(this.tabs){
44568             this.tabs.removeTab(panel.getEl().id);
44569         }else if (!preservePanel){
44570             this.bodyEl.dom.removeChild(panel.getEl().dom);
44571         }
44572         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44573             var p = this.panels.first();
44574             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44575             tempEl.appendChild(p.getEl().dom);
44576             this.bodyEl.update("");
44577             this.bodyEl.dom.appendChild(p.getEl().dom);
44578             tempEl = null;
44579             this.updateTitle(p.getTitle());
44580             this.tabs = null;
44581             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44582             this.setActivePanel(p);
44583         }
44584         panel.setRegion(null);
44585         if(this.activePanel == panel){
44586             this.activePanel = null;
44587         }
44588         if(this.config.autoDestroy !== false && preservePanel !== true){
44589             try{panel.destroy();}catch(e){}
44590         }
44591         this.fireEvent("panelremoved", this, panel);
44592         return panel;
44593     },
44594
44595     /**
44596      * Returns the TabPanel component used by this region
44597      * @return {Roo.TabPanel}
44598      */
44599     getTabs : function(){
44600         return this.tabs;
44601     },
44602
44603     createTool : function(parentEl, className){
44604         var btn = Roo.DomHelper.append(parentEl, {
44605             tag: "div",
44606             cls: "x-layout-tools-button",
44607             children: [ {
44608                 tag: "div",
44609                 cls: "roo-layout-tools-button-inner " + className,
44610                 html: "&#160;"
44611             }]
44612         }, true);
44613         btn.addClassOnOver("roo-layout-tools-button-over");
44614         return btn;
44615     }
44616 });/*
44617  * Based on:
44618  * Ext JS Library 1.1.1
44619  * Copyright(c) 2006-2007, Ext JS, LLC.
44620  *
44621  * Originally Released Under LGPL - original licence link has changed is not relivant.
44622  *
44623  * Fork - LGPL
44624  * <script type="text/javascript">
44625  */
44626  
44627
44628
44629 /**
44630  * @class Roo.SplitLayoutRegion
44631  * @extends Roo.LayoutRegion
44632  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44633  */
44634 Roo.bootstrap.layout.Split = function(config){
44635     this.cursor = config.cursor;
44636     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44637 };
44638
44639 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44640 {
44641     splitTip : "Drag to resize.",
44642     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44643     useSplitTips : false,
44644
44645     applyConfig : function(config){
44646         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44647     },
44648     
44649     onRender : function(ctr,pos) {
44650         
44651         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44652         if(!this.config.split){
44653             return;
44654         }
44655         if(!this.split){
44656             
44657             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44658                             tag: "div",
44659                             id: this.el.id + "-split",
44660                             cls: "roo-layout-split roo-layout-split-"+this.position,
44661                             html: "&#160;"
44662             });
44663             /** The SplitBar for this region 
44664             * @type Roo.SplitBar */
44665             // does not exist yet...
44666             Roo.log([this.position, this.orientation]);
44667             
44668             this.split = new Roo.bootstrap.SplitBar({
44669                 dragElement : splitEl,
44670                 resizingElement: this.el,
44671                 orientation : this.orientation
44672             });
44673             
44674             this.split.on("moved", this.onSplitMove, this);
44675             this.split.useShim = this.config.useShim === true;
44676             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44677             if(this.useSplitTips){
44678                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44679             }
44680             //if(config.collapsible){
44681             //    this.split.el.on("dblclick", this.collapse,  this);
44682             //}
44683         }
44684         if(typeof this.config.minSize != "undefined"){
44685             this.split.minSize = this.config.minSize;
44686         }
44687         if(typeof this.config.maxSize != "undefined"){
44688             this.split.maxSize = this.config.maxSize;
44689         }
44690         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44691             this.hideSplitter();
44692         }
44693         
44694     },
44695
44696     getHMaxSize : function(){
44697          var cmax = this.config.maxSize || 10000;
44698          var center = this.mgr.getRegion("center");
44699          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44700     },
44701
44702     getVMaxSize : function(){
44703          var cmax = this.config.maxSize || 10000;
44704          var center = this.mgr.getRegion("center");
44705          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44706     },
44707
44708     onSplitMove : function(split, newSize){
44709         this.fireEvent("resized", this, newSize);
44710     },
44711     
44712     /** 
44713      * Returns the {@link Roo.SplitBar} for this region.
44714      * @return {Roo.SplitBar}
44715      */
44716     getSplitBar : function(){
44717         return this.split;
44718     },
44719     
44720     hide : function(){
44721         this.hideSplitter();
44722         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44723     },
44724
44725     hideSplitter : function(){
44726         if(this.split){
44727             this.split.el.setLocation(-2000,-2000);
44728             this.split.el.hide();
44729         }
44730     },
44731
44732     show : function(){
44733         if(this.split){
44734             this.split.el.show();
44735         }
44736         Roo.bootstrap.layout.Split.superclass.show.call(this);
44737     },
44738     
44739     beforeSlide: function(){
44740         if(Roo.isGecko){// firefox overflow auto bug workaround
44741             this.bodyEl.clip();
44742             if(this.tabs) {
44743                 this.tabs.bodyEl.clip();
44744             }
44745             if(this.activePanel){
44746                 this.activePanel.getEl().clip();
44747                 
44748                 if(this.activePanel.beforeSlide){
44749                     this.activePanel.beforeSlide();
44750                 }
44751             }
44752         }
44753     },
44754     
44755     afterSlide : function(){
44756         if(Roo.isGecko){// firefox overflow auto bug workaround
44757             this.bodyEl.unclip();
44758             if(this.tabs) {
44759                 this.tabs.bodyEl.unclip();
44760             }
44761             if(this.activePanel){
44762                 this.activePanel.getEl().unclip();
44763                 if(this.activePanel.afterSlide){
44764                     this.activePanel.afterSlide();
44765                 }
44766             }
44767         }
44768     },
44769
44770     initAutoHide : function(){
44771         if(this.autoHide !== false){
44772             if(!this.autoHideHd){
44773                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44774                 this.autoHideHd = {
44775                     "mouseout": function(e){
44776                         if(!e.within(this.el, true)){
44777                             st.delay(500);
44778                         }
44779                     },
44780                     "mouseover" : function(e){
44781                         st.cancel();
44782                     },
44783                     scope : this
44784                 };
44785             }
44786             this.el.on(this.autoHideHd);
44787         }
44788     },
44789
44790     clearAutoHide : function(){
44791         if(this.autoHide !== false){
44792             this.el.un("mouseout", this.autoHideHd.mouseout);
44793             this.el.un("mouseover", this.autoHideHd.mouseover);
44794         }
44795     },
44796
44797     clearMonitor : function(){
44798         Roo.get(document).un("click", this.slideInIf, this);
44799     },
44800
44801     // these names are backwards but not changed for compat
44802     slideOut : function(){
44803         if(this.isSlid || this.el.hasActiveFx()){
44804             return;
44805         }
44806         this.isSlid = true;
44807         if(this.collapseBtn){
44808             this.collapseBtn.hide();
44809         }
44810         this.closeBtnState = this.closeBtn.getStyle('display');
44811         this.closeBtn.hide();
44812         if(this.stickBtn){
44813             this.stickBtn.show();
44814         }
44815         this.el.show();
44816         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44817         this.beforeSlide();
44818         this.el.setStyle("z-index", 10001);
44819         this.el.slideIn(this.getSlideAnchor(), {
44820             callback: function(){
44821                 this.afterSlide();
44822                 this.initAutoHide();
44823                 Roo.get(document).on("click", this.slideInIf, this);
44824                 this.fireEvent("slideshow", this);
44825             },
44826             scope: this,
44827             block: true
44828         });
44829     },
44830
44831     afterSlideIn : function(){
44832         this.clearAutoHide();
44833         this.isSlid = false;
44834         this.clearMonitor();
44835         this.el.setStyle("z-index", "");
44836         if(this.collapseBtn){
44837             this.collapseBtn.show();
44838         }
44839         this.closeBtn.setStyle('display', this.closeBtnState);
44840         if(this.stickBtn){
44841             this.stickBtn.hide();
44842         }
44843         this.fireEvent("slidehide", this);
44844     },
44845
44846     slideIn : function(cb){
44847         if(!this.isSlid || this.el.hasActiveFx()){
44848             Roo.callback(cb);
44849             return;
44850         }
44851         this.isSlid = false;
44852         this.beforeSlide();
44853         this.el.slideOut(this.getSlideAnchor(), {
44854             callback: function(){
44855                 this.el.setLeftTop(-10000, -10000);
44856                 this.afterSlide();
44857                 this.afterSlideIn();
44858                 Roo.callback(cb);
44859             },
44860             scope: this,
44861             block: true
44862         });
44863     },
44864     
44865     slideInIf : function(e){
44866         if(!e.within(this.el)){
44867             this.slideIn();
44868         }
44869     },
44870
44871     animateCollapse : function(){
44872         this.beforeSlide();
44873         this.el.setStyle("z-index", 20000);
44874         var anchor = this.getSlideAnchor();
44875         this.el.slideOut(anchor, {
44876             callback : function(){
44877                 this.el.setStyle("z-index", "");
44878                 this.collapsedEl.slideIn(anchor, {duration:.3});
44879                 this.afterSlide();
44880                 this.el.setLocation(-10000,-10000);
44881                 this.el.hide();
44882                 this.fireEvent("collapsed", this);
44883             },
44884             scope: this,
44885             block: true
44886         });
44887     },
44888
44889     animateExpand : function(){
44890         this.beforeSlide();
44891         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44892         this.el.setStyle("z-index", 20000);
44893         this.collapsedEl.hide({
44894             duration:.1
44895         });
44896         this.el.slideIn(this.getSlideAnchor(), {
44897             callback : function(){
44898                 this.el.setStyle("z-index", "");
44899                 this.afterSlide();
44900                 if(this.split){
44901                     this.split.el.show();
44902                 }
44903                 this.fireEvent("invalidated", this);
44904                 this.fireEvent("expanded", this);
44905             },
44906             scope: this,
44907             block: true
44908         });
44909     },
44910
44911     anchors : {
44912         "west" : "left",
44913         "east" : "right",
44914         "north" : "top",
44915         "south" : "bottom"
44916     },
44917
44918     sanchors : {
44919         "west" : "l",
44920         "east" : "r",
44921         "north" : "t",
44922         "south" : "b"
44923     },
44924
44925     canchors : {
44926         "west" : "tl-tr",
44927         "east" : "tr-tl",
44928         "north" : "tl-bl",
44929         "south" : "bl-tl"
44930     },
44931
44932     getAnchor : function(){
44933         return this.anchors[this.position];
44934     },
44935
44936     getCollapseAnchor : function(){
44937         return this.canchors[this.position];
44938     },
44939
44940     getSlideAnchor : function(){
44941         return this.sanchors[this.position];
44942     },
44943
44944     getAlignAdj : function(){
44945         var cm = this.cmargins;
44946         switch(this.position){
44947             case "west":
44948                 return [0, 0];
44949             break;
44950             case "east":
44951                 return [0, 0];
44952             break;
44953             case "north":
44954                 return [0, 0];
44955             break;
44956             case "south":
44957                 return [0, 0];
44958             break;
44959         }
44960     },
44961
44962     getExpandAdj : function(){
44963         var c = this.collapsedEl, cm = this.cmargins;
44964         switch(this.position){
44965             case "west":
44966                 return [-(cm.right+c.getWidth()+cm.left), 0];
44967             break;
44968             case "east":
44969                 return [cm.right+c.getWidth()+cm.left, 0];
44970             break;
44971             case "north":
44972                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44973             break;
44974             case "south":
44975                 return [0, cm.top+cm.bottom+c.getHeight()];
44976             break;
44977         }
44978     }
44979 });/*
44980  * Based on:
44981  * Ext JS Library 1.1.1
44982  * Copyright(c) 2006-2007, Ext JS, LLC.
44983  *
44984  * Originally Released Under LGPL - original licence link has changed is not relivant.
44985  *
44986  * Fork - LGPL
44987  * <script type="text/javascript">
44988  */
44989 /*
44990  * These classes are private internal classes
44991  */
44992 Roo.bootstrap.layout.Center = function(config){
44993     config.region = "center";
44994     Roo.bootstrap.layout.Region.call(this, config);
44995     this.visible = true;
44996     this.minWidth = config.minWidth || 20;
44997     this.minHeight = config.minHeight || 20;
44998 };
44999
45000 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45001     hide : function(){
45002         // center panel can't be hidden
45003     },
45004     
45005     show : function(){
45006         // center panel can't be hidden
45007     },
45008     
45009     getMinWidth: function(){
45010         return this.minWidth;
45011     },
45012     
45013     getMinHeight: function(){
45014         return this.minHeight;
45015     }
45016 });
45017
45018
45019
45020
45021  
45022
45023
45024
45025
45026
45027
45028 Roo.bootstrap.layout.North = function(config)
45029 {
45030     config.region = 'north';
45031     config.cursor = 'n-resize';
45032     
45033     Roo.bootstrap.layout.Split.call(this, config);
45034     
45035     
45036     if(this.split){
45037         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45038         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45039         this.split.el.addClass("roo-layout-split-v");
45040     }
45041     //var size = config.initialSize || config.height;
45042     //if(this.el && typeof size != "undefined"){
45043     //    this.el.setHeight(size);
45044     //}
45045 };
45046 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45047 {
45048     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45049      
45050      
45051     onRender : function(ctr, pos)
45052     {
45053         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45054         var size = this.config.initialSize || this.config.height;
45055         if(this.el && typeof size != "undefined"){
45056             this.el.setHeight(size);
45057         }
45058     
45059     },
45060     
45061     getBox : function(){
45062         if(this.collapsed){
45063             return this.collapsedEl.getBox();
45064         }
45065         var box = this.el.getBox();
45066         if(this.split){
45067             box.height += this.split.el.getHeight();
45068         }
45069         return box;
45070     },
45071     
45072     updateBox : function(box){
45073         if(this.split && !this.collapsed){
45074             box.height -= this.split.el.getHeight();
45075             this.split.el.setLeft(box.x);
45076             this.split.el.setTop(box.y+box.height);
45077             this.split.el.setWidth(box.width);
45078         }
45079         if(this.collapsed){
45080             this.updateBody(box.width, null);
45081         }
45082         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45083     }
45084 });
45085
45086
45087
45088
45089
45090 Roo.bootstrap.layout.South = function(config){
45091     config.region = 'south';
45092     config.cursor = 's-resize';
45093     Roo.bootstrap.layout.Split.call(this, config);
45094     if(this.split){
45095         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45096         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45097         this.split.el.addClass("roo-layout-split-v");
45098     }
45099     
45100 };
45101
45102 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45103     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45104     
45105     onRender : function(ctr, pos)
45106     {
45107         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45108         var size = this.config.initialSize || this.config.height;
45109         if(this.el && typeof size != "undefined"){
45110             this.el.setHeight(size);
45111         }
45112     
45113     },
45114     
45115     getBox : function(){
45116         if(this.collapsed){
45117             return this.collapsedEl.getBox();
45118         }
45119         var box = this.el.getBox();
45120         if(this.split){
45121             var sh = this.split.el.getHeight();
45122             box.height += sh;
45123             box.y -= sh;
45124         }
45125         return box;
45126     },
45127     
45128     updateBox : function(box){
45129         if(this.split && !this.collapsed){
45130             var sh = this.split.el.getHeight();
45131             box.height -= sh;
45132             box.y += sh;
45133             this.split.el.setLeft(box.x);
45134             this.split.el.setTop(box.y-sh);
45135             this.split.el.setWidth(box.width);
45136         }
45137         if(this.collapsed){
45138             this.updateBody(box.width, null);
45139         }
45140         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45141     }
45142 });
45143
45144 Roo.bootstrap.layout.East = function(config){
45145     config.region = "east";
45146     config.cursor = "e-resize";
45147     Roo.bootstrap.layout.Split.call(this, config);
45148     if(this.split){
45149         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45150         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45151         this.split.el.addClass("roo-layout-split-h");
45152     }
45153     
45154 };
45155 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45156     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45157     
45158     onRender : function(ctr, pos)
45159     {
45160         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45161         var size = this.config.initialSize || this.config.width;
45162         if(this.el && typeof size != "undefined"){
45163             this.el.setWidth(size);
45164         }
45165     
45166     },
45167     
45168     getBox : function(){
45169         if(this.collapsed){
45170             return this.collapsedEl.getBox();
45171         }
45172         var box = this.el.getBox();
45173         if(this.split){
45174             var sw = this.split.el.getWidth();
45175             box.width += sw;
45176             box.x -= sw;
45177         }
45178         return box;
45179     },
45180
45181     updateBox : function(box){
45182         if(this.split && !this.collapsed){
45183             var sw = this.split.el.getWidth();
45184             box.width -= sw;
45185             this.split.el.setLeft(box.x);
45186             this.split.el.setTop(box.y);
45187             this.split.el.setHeight(box.height);
45188             box.x += sw;
45189         }
45190         if(this.collapsed){
45191             this.updateBody(null, box.height);
45192         }
45193         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45194     }
45195 });
45196
45197 Roo.bootstrap.layout.West = function(config){
45198     config.region = "west";
45199     config.cursor = "w-resize";
45200     
45201     Roo.bootstrap.layout.Split.call(this, config);
45202     if(this.split){
45203         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45204         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45205         this.split.el.addClass("roo-layout-split-h");
45206     }
45207     
45208 };
45209 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45210     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45211     
45212     onRender: function(ctr, pos)
45213     {
45214         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45215         var size = this.config.initialSize || this.config.width;
45216         if(typeof size != "undefined"){
45217             this.el.setWidth(size);
45218         }
45219     },
45220     
45221     getBox : function(){
45222         if(this.collapsed){
45223             return this.collapsedEl.getBox();
45224         }
45225         var box = this.el.getBox();
45226         if (box.width == 0) {
45227             box.width = this.config.width; // kludge?
45228         }
45229         if(this.split){
45230             box.width += this.split.el.getWidth();
45231         }
45232         return box;
45233     },
45234     
45235     updateBox : function(box){
45236         if(this.split && !this.collapsed){
45237             var sw = this.split.el.getWidth();
45238             box.width -= sw;
45239             this.split.el.setLeft(box.x+box.width);
45240             this.split.el.setTop(box.y);
45241             this.split.el.setHeight(box.height);
45242         }
45243         if(this.collapsed){
45244             this.updateBody(null, box.height);
45245         }
45246         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45247     }
45248 });/*
45249  * Based on:
45250  * Ext JS Library 1.1.1
45251  * Copyright(c) 2006-2007, Ext JS, LLC.
45252  *
45253  * Originally Released Under LGPL - original licence link has changed is not relivant.
45254  *
45255  * Fork - LGPL
45256  * <script type="text/javascript">
45257  */
45258 /**
45259  * @class Roo.bootstrap.paenl.Content
45260  * @extends Roo.util.Observable
45261  * @children Roo.bootstrap.Component
45262  * @parent builder Roo.bootstrap.layout.Border
45263  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45264  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45265  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45266  * @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
45267  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45268  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45269  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45270  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45271  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45272  * @cfg {String} title          The title for this panel
45273  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45274  * @cfg {String} url            Calls {@link #setUrl} with this value
45275  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45276  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45277  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45278  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45279  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45280  * @cfg {Boolean} badges render the badges
45281  * @cfg {String} cls  extra classes to use  
45282  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45283  
45284  * @constructor
45285  * Create a new ContentPanel.
45286  * @param {String/Object} config A string to set only the title or a config object
45287  
45288  */
45289 Roo.bootstrap.panel.Content = function( config){
45290     
45291     this.tpl = config.tpl || false;
45292     
45293     var el = config.el;
45294     var content = config.content;
45295
45296     if(config.autoCreate){ // xtype is available if this is called from factory
45297         el = Roo.id();
45298     }
45299     this.el = Roo.get(el);
45300     if(!this.el && config && config.autoCreate){
45301         if(typeof config.autoCreate == "object"){
45302             if(!config.autoCreate.id){
45303                 config.autoCreate.id = config.id||el;
45304             }
45305             this.el = Roo.DomHelper.append(document.body,
45306                         config.autoCreate, true);
45307         }else{
45308             var elcfg =  {
45309                 tag: "div",
45310                 cls: (config.cls || '') +
45311                     (config.background ? ' bg-' + config.background : '') +
45312                     " roo-layout-inactive-content",
45313                 id: config.id||el
45314             };
45315             if (config.iframe) {
45316                 elcfg.cn = [
45317                     {
45318                         tag : 'iframe',
45319                         style : 'border: 0px',
45320                         src : 'about:blank'
45321                     }
45322                 ];
45323             }
45324               
45325             if (config.html) {
45326                 elcfg.html = config.html;
45327                 
45328             }
45329                         
45330             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45331             if (config.iframe) {
45332                 this.iframeEl = this.el.select('iframe',true).first();
45333             }
45334             
45335         }
45336     } 
45337     this.closable = false;
45338     this.loaded = false;
45339     this.active = false;
45340    
45341       
45342     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45343         
45344         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45345         
45346         this.wrapEl = this.el; //this.el.wrap();
45347         var ti = [];
45348         if (config.toolbar.items) {
45349             ti = config.toolbar.items ;
45350             delete config.toolbar.items ;
45351         }
45352         
45353         var nitems = [];
45354         this.toolbar.render(this.wrapEl, 'before');
45355         for(var i =0;i < ti.length;i++) {
45356           //  Roo.log(['add child', items[i]]);
45357             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45358         }
45359         this.toolbar.items = nitems;
45360         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45361         delete config.toolbar;
45362         
45363     }
45364     /*
45365     // xtype created footer. - not sure if will work as we normally have to render first..
45366     if (this.footer && !this.footer.el && this.footer.xtype) {
45367         if (!this.wrapEl) {
45368             this.wrapEl = this.el.wrap();
45369         }
45370     
45371         this.footer.container = this.wrapEl.createChild();
45372          
45373         this.footer = Roo.factory(this.footer, Roo);
45374         
45375     }
45376     */
45377     
45378      if(typeof config == "string"){
45379         this.title = config;
45380     }else{
45381         Roo.apply(this, config);
45382     }
45383     
45384     if(this.resizeEl){
45385         this.resizeEl = Roo.get(this.resizeEl, true);
45386     }else{
45387         this.resizeEl = this.el;
45388     }
45389     // handle view.xtype
45390     
45391  
45392     
45393     
45394     this.addEvents({
45395         /**
45396          * @event activate
45397          * Fires when this panel is activated. 
45398          * @param {Roo.ContentPanel} this
45399          */
45400         "activate" : true,
45401         /**
45402          * @event deactivate
45403          * Fires when this panel is activated. 
45404          * @param {Roo.ContentPanel} this
45405          */
45406         "deactivate" : true,
45407
45408         /**
45409          * @event resize
45410          * Fires when this panel is resized if fitToFrame is true.
45411          * @param {Roo.ContentPanel} this
45412          * @param {Number} width The width after any component adjustments
45413          * @param {Number} height The height after any component adjustments
45414          */
45415         "resize" : true,
45416         
45417          /**
45418          * @event render
45419          * Fires when this tab is created
45420          * @param {Roo.ContentPanel} this
45421          */
45422         "render" : true,
45423         
45424           /**
45425          * @event scroll
45426          * Fires when this content is scrolled
45427          * @param {Roo.ContentPanel} this
45428          * @param {Event} scrollEvent
45429          */
45430         "scroll" : true
45431         
45432         
45433         
45434     });
45435     
45436
45437     
45438     
45439     if(this.autoScroll && !this.iframe){
45440         this.resizeEl.setStyle("overflow", "auto");
45441         this.resizeEl.on('scroll', this.onScroll, this);
45442     } else {
45443         // fix randome scrolling
45444         //this.el.on('scroll', function() {
45445         //    Roo.log('fix random scolling');
45446         //    this.scrollTo('top',0); 
45447         //});
45448     }
45449     content = content || this.content;
45450     if(content){
45451         this.setContent(content);
45452     }
45453     if(config && config.url){
45454         this.setUrl(this.url, this.params, this.loadOnce);
45455     }
45456     
45457     
45458     
45459     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45460     
45461     if (this.view && typeof(this.view.xtype) != 'undefined') {
45462         this.view.el = this.el.appendChild(document.createElement("div"));
45463         this.view = Roo.factory(this.view); 
45464         this.view.render  &&  this.view.render(false, '');  
45465     }
45466     
45467     
45468     this.fireEvent('render', this);
45469 };
45470
45471 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45472     
45473     cls : '',
45474     background : '',
45475     
45476     tabTip : '',
45477     
45478     iframe : false,
45479     iframeEl : false,
45480     
45481     /* Resize Element - use this to work out scroll etc. */
45482     resizeEl : false,
45483     
45484     setRegion : function(region){
45485         this.region = region;
45486         this.setActiveClass(region && !this.background);
45487     },
45488     
45489     
45490     setActiveClass: function(state)
45491     {
45492         if(state){
45493            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45494            this.el.setStyle('position','relative');
45495         }else{
45496            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45497            this.el.setStyle('position', 'absolute');
45498         } 
45499     },
45500     
45501     /**
45502      * Returns the toolbar for this Panel if one was configured. 
45503      * @return {Roo.Toolbar} 
45504      */
45505     getToolbar : function(){
45506         return this.toolbar;
45507     },
45508     
45509     setActiveState : function(active)
45510     {
45511         this.active = active;
45512         this.setActiveClass(active);
45513         if(!active){
45514             if(this.fireEvent("deactivate", this) === false){
45515                 return false;
45516             }
45517             return true;
45518         }
45519         this.fireEvent("activate", this);
45520         return true;
45521     },
45522     /**
45523      * Updates this panel's element (not for iframe)
45524      * @param {String} content The new content
45525      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45526     */
45527     setContent : function(content, loadScripts){
45528         if (this.iframe) {
45529             return;
45530         }
45531         
45532         this.el.update(content, loadScripts);
45533     },
45534
45535     ignoreResize : function(w, h)
45536     {
45537         //return false; // always resize?
45538         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45539             return true;
45540         }else{
45541             this.lastSize = {width: w, height: h};
45542             return false;
45543         }
45544     },
45545     /**
45546      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45547      * @return {Roo.UpdateManager} The UpdateManager
45548      */
45549     getUpdateManager : function(){
45550         if (this.iframe) {
45551             return false;
45552         }
45553         return this.el.getUpdateManager();
45554     },
45555      /**
45556      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45557      * Does not work with IFRAME contents
45558      * @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:
45559 <pre><code>
45560 panel.load({
45561     url: "your-url.php",
45562     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45563     callback: yourFunction,
45564     scope: yourObject, //(optional scope)
45565     discardUrl: false,
45566     nocache: false,
45567     text: "Loading...",
45568     timeout: 30,
45569     scripts: false
45570 });
45571 </code></pre>
45572      
45573      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45574      * 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.
45575      * @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}
45576      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45577      * @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.
45578      * @return {Roo.ContentPanel} this
45579      */
45580     load : function(){
45581         
45582         if (this.iframe) {
45583             return this;
45584         }
45585         
45586         var um = this.el.getUpdateManager();
45587         um.update.apply(um, arguments);
45588         return this;
45589     },
45590
45591
45592     /**
45593      * 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.
45594      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45595      * @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)
45596      * @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)
45597      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45598      */
45599     setUrl : function(url, params, loadOnce){
45600         if (this.iframe) {
45601             this.iframeEl.dom.src = url;
45602             return false;
45603         }
45604         
45605         if(this.refreshDelegate){
45606             this.removeListener("activate", this.refreshDelegate);
45607         }
45608         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45609         this.on("activate", this.refreshDelegate);
45610         return this.el.getUpdateManager();
45611     },
45612     
45613     _handleRefresh : function(url, params, loadOnce){
45614         if(!loadOnce || !this.loaded){
45615             var updater = this.el.getUpdateManager();
45616             updater.update(url, params, this._setLoaded.createDelegate(this));
45617         }
45618     },
45619     
45620     _setLoaded : function(){
45621         this.loaded = true;
45622     }, 
45623     
45624     /**
45625      * Returns this panel's id
45626      * @return {String} 
45627      */
45628     getId : function(){
45629         return this.el.id;
45630     },
45631     
45632     /** 
45633      * Returns this panel's element - used by regiosn to add.
45634      * @return {Roo.Element} 
45635      */
45636     getEl : function(){
45637         return this.wrapEl || this.el;
45638     },
45639     
45640    
45641     
45642     adjustForComponents : function(width, height)
45643     {
45644         //Roo.log('adjustForComponents ');
45645         if(this.resizeEl != this.el){
45646             width -= this.el.getFrameWidth('lr');
45647             height -= this.el.getFrameWidth('tb');
45648         }
45649         if(this.toolbar){
45650             var te = this.toolbar.getEl();
45651             te.setWidth(width);
45652             height -= te.getHeight();
45653         }
45654         if(this.footer){
45655             var te = this.footer.getEl();
45656             te.setWidth(width);
45657             height -= te.getHeight();
45658         }
45659         
45660         
45661         if(this.adjustments){
45662             width += this.adjustments[0];
45663             height += this.adjustments[1];
45664         }
45665         return {"width": width, "height": height};
45666     },
45667     
45668     setSize : function(width, height){
45669         if(this.fitToFrame && !this.ignoreResize(width, height)){
45670             if(this.fitContainer && this.resizeEl != this.el){
45671                 this.el.setSize(width, height);
45672             }
45673             var size = this.adjustForComponents(width, height);
45674             if (this.iframe) {
45675                 this.iframeEl.setSize(width,height);
45676             }
45677             
45678             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45679             this.fireEvent('resize', this, size.width, size.height);
45680             
45681             
45682         }
45683     },
45684     
45685     /**
45686      * Returns this panel's title
45687      * @return {String} 
45688      */
45689     getTitle : function(){
45690         
45691         if (typeof(this.title) != 'object') {
45692             return this.title;
45693         }
45694         
45695         var t = '';
45696         for (var k in this.title) {
45697             if (!this.title.hasOwnProperty(k)) {
45698                 continue;
45699             }
45700             
45701             if (k.indexOf('-') >= 0) {
45702                 var s = k.split('-');
45703                 for (var i = 0; i<s.length; i++) {
45704                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45705                 }
45706             } else {
45707                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45708             }
45709         }
45710         return t;
45711     },
45712     
45713     /**
45714      * Set this panel's title
45715      * @param {String} title
45716      */
45717     setTitle : function(title){
45718         this.title = title;
45719         if(this.region){
45720             this.region.updatePanelTitle(this, title);
45721         }
45722     },
45723     
45724     /**
45725      * Returns true is this panel was configured to be closable
45726      * @return {Boolean} 
45727      */
45728     isClosable : function(){
45729         return this.closable;
45730     },
45731     
45732     beforeSlide : function(){
45733         this.el.clip();
45734         this.resizeEl.clip();
45735     },
45736     
45737     afterSlide : function(){
45738         this.el.unclip();
45739         this.resizeEl.unclip();
45740     },
45741     
45742     /**
45743      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45744      *   Will fail silently if the {@link #setUrl} method has not been called.
45745      *   This does not activate the panel, just updates its content.
45746      */
45747     refresh : function(){
45748         if(this.refreshDelegate){
45749            this.loaded = false;
45750            this.refreshDelegate();
45751         }
45752     },
45753     
45754     /**
45755      * Destroys this panel
45756      */
45757     destroy : function(){
45758         this.el.removeAllListeners();
45759         var tempEl = document.createElement("span");
45760         tempEl.appendChild(this.el.dom);
45761         tempEl.innerHTML = "";
45762         this.el.remove();
45763         this.el = null;
45764     },
45765     
45766     /**
45767      * form - if the content panel contains a form - this is a reference to it.
45768      * @type {Roo.form.Form}
45769      */
45770     form : false,
45771     /**
45772      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45773      *    This contains a reference to it.
45774      * @type {Roo.View}
45775      */
45776     view : false,
45777     
45778       /**
45779      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45780      * <pre><code>
45781
45782 layout.addxtype({
45783        xtype : 'Form',
45784        items: [ .... ]
45785    }
45786 );
45787
45788 </code></pre>
45789      * @param {Object} cfg Xtype definition of item to add.
45790      */
45791     
45792     
45793     getChildContainer: function () {
45794         return this.getEl();
45795     },
45796     
45797     
45798     onScroll : function(e)
45799     {
45800         this.fireEvent('scroll', this, e);
45801     }
45802     
45803     
45804     /*
45805         var  ret = new Roo.factory(cfg);
45806         return ret;
45807         
45808         
45809         // add form..
45810         if (cfg.xtype.match(/^Form$/)) {
45811             
45812             var el;
45813             //if (this.footer) {
45814             //    el = this.footer.container.insertSibling(false, 'before');
45815             //} else {
45816                 el = this.el.createChild();
45817             //}
45818
45819             this.form = new  Roo.form.Form(cfg);
45820             
45821             
45822             if ( this.form.allItems.length) {
45823                 this.form.render(el.dom);
45824             }
45825             return this.form;
45826         }
45827         // should only have one of theses..
45828         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45829             // views.. should not be just added - used named prop 'view''
45830             
45831             cfg.el = this.el.appendChild(document.createElement("div"));
45832             // factory?
45833             
45834             var ret = new Roo.factory(cfg);
45835              
45836              ret.render && ret.render(false, ''); // render blank..
45837             this.view = ret;
45838             return ret;
45839         }
45840         return false;
45841     }
45842     \*/
45843 });
45844  
45845 /**
45846  * @class Roo.bootstrap.panel.Grid
45847  * @extends Roo.bootstrap.panel.Content
45848  * @constructor
45849  * Create a new GridPanel.
45850  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45851  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45852  * @param {Object} config A the config object
45853   
45854  */
45855
45856
45857
45858 Roo.bootstrap.panel.Grid = function(config)
45859 {
45860     
45861       
45862     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45863         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45864
45865     config.el = this.wrapper;
45866     //this.el = this.wrapper;
45867     
45868       if (config.container) {
45869         // ctor'ed from a Border/panel.grid
45870         
45871         
45872         this.wrapper.setStyle("overflow", "hidden");
45873         this.wrapper.addClass('roo-grid-container');
45874
45875     }
45876     
45877     
45878     if(config.toolbar){
45879         var tool_el = this.wrapper.createChild();    
45880         this.toolbar = Roo.factory(config.toolbar);
45881         var ti = [];
45882         if (config.toolbar.items) {
45883             ti = config.toolbar.items ;
45884             delete config.toolbar.items ;
45885         }
45886         
45887         var nitems = [];
45888         this.toolbar.render(tool_el);
45889         for(var i =0;i < ti.length;i++) {
45890           //  Roo.log(['add child', items[i]]);
45891             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45892         }
45893         this.toolbar.items = nitems;
45894         
45895         delete config.toolbar;
45896     }
45897     
45898     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45899     config.grid.scrollBody = true;;
45900     config.grid.monitorWindowResize = false; // turn off autosizing
45901     config.grid.autoHeight = false;
45902     config.grid.autoWidth = false;
45903     
45904     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45905     
45906     if (config.background) {
45907         // render grid on panel activation (if panel background)
45908         this.on('activate', function(gp) {
45909             if (!gp.grid.rendered) {
45910                 gp.grid.render(this.wrapper);
45911                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45912             }
45913         });
45914             
45915     } else {
45916         this.grid.render(this.wrapper);
45917         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45918
45919     }
45920     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45921     // ??? needed ??? config.el = this.wrapper;
45922     
45923     
45924     
45925   
45926     // xtype created footer. - not sure if will work as we normally have to render first..
45927     if (this.footer && !this.footer.el && this.footer.xtype) {
45928         
45929         var ctr = this.grid.getView().getFooterPanel(true);
45930         this.footer.dataSource = this.grid.dataSource;
45931         this.footer = Roo.factory(this.footer, Roo);
45932         this.footer.render(ctr);
45933         
45934     }
45935     
45936     
45937     
45938     
45939      
45940 };
45941
45942 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45943 {
45944   
45945     getId : function(){
45946         return this.grid.id;
45947     },
45948     
45949     /**
45950      * Returns the grid for this panel
45951      * @return {Roo.bootstrap.Table} 
45952      */
45953     getGrid : function(){
45954         return this.grid;    
45955     },
45956     
45957     setSize : function(width, height)
45958     {
45959      
45960         //if(!this.ignoreResize(width, height)){
45961             var grid = this.grid;
45962             var size = this.adjustForComponents(width, height);
45963             // tfoot is not a footer?
45964           
45965             
45966             var gridel = grid.getGridEl();
45967             gridel.setSize(size.width, size.height);
45968             
45969             var tbd = grid.getGridEl().select('tbody', true).first();
45970             var thd = grid.getGridEl().select('thead',true).first();
45971             var tbf= grid.getGridEl().select('tfoot', true).first();
45972
45973             if (tbf) {
45974                 size.height -= tbf.getHeight();
45975             }
45976             if (thd) {
45977                 size.height -= thd.getHeight();
45978             }
45979             
45980             tbd.setSize(size.width, size.height );
45981             // this is for the account management tab -seems to work there.
45982             var thd = grid.getGridEl().select('thead',true).first();
45983             //if (tbd) {
45984             //    tbd.setSize(size.width, size.height - thd.getHeight());
45985             //}
45986              
45987             grid.autoSize();
45988         //}
45989    
45990     },
45991      
45992     
45993     
45994     beforeSlide : function(){
45995         this.grid.getView().scroller.clip();
45996     },
45997     
45998     afterSlide : function(){
45999         this.grid.getView().scroller.unclip();
46000     },
46001     
46002     destroy : function(){
46003         this.grid.destroy();
46004         delete this.grid;
46005         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46006     }
46007 });
46008
46009 /**
46010  * @class Roo.bootstrap.panel.Nest
46011  * @extends Roo.bootstrap.panel.Content
46012  * @constructor
46013  * Create a new Panel, that can contain a layout.Border.
46014  * 
46015  * 
46016  * @param {String/Object} config A string to set only the title or a config object
46017  */
46018 Roo.bootstrap.panel.Nest = function(config)
46019 {
46020     // construct with only one argument..
46021     /* FIXME - implement nicer consturctors
46022     if (layout.layout) {
46023         config = layout;
46024         layout = config.layout;
46025         delete config.layout;
46026     }
46027     if (layout.xtype && !layout.getEl) {
46028         // then layout needs constructing..
46029         layout = Roo.factory(layout, Roo);
46030     }
46031     */
46032     
46033     config.el =  config.layout.getEl();
46034     
46035     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46036     
46037     config.layout.monitorWindowResize = false; // turn off autosizing
46038     this.layout = config.layout;
46039     this.layout.getEl().addClass("roo-layout-nested-layout");
46040     this.layout.parent = this;
46041     
46042     
46043     
46044     
46045 };
46046
46047 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46048     /**
46049     * @cfg {Roo.BorderLayout} layout The layout for this panel
46050     */
46051     layout : false,
46052
46053     setSize : function(width, height){
46054         if(!this.ignoreResize(width, height)){
46055             var size = this.adjustForComponents(width, height);
46056             var el = this.layout.getEl();
46057             if (size.height < 1) {
46058                 el.setWidth(size.width);   
46059             } else {
46060                 el.setSize(size.width, size.height);
46061             }
46062             var touch = el.dom.offsetWidth;
46063             this.layout.layout();
46064             // ie requires a double layout on the first pass
46065             if(Roo.isIE && !this.initialized){
46066                 this.initialized = true;
46067                 this.layout.layout();
46068             }
46069         }
46070     },
46071     
46072     // activate all subpanels if not currently active..
46073     
46074     setActiveState : function(active){
46075         this.active = active;
46076         this.setActiveClass(active);
46077         
46078         if(!active){
46079             this.fireEvent("deactivate", this);
46080             return;
46081         }
46082         
46083         this.fireEvent("activate", this);
46084         // not sure if this should happen before or after..
46085         if (!this.layout) {
46086             return; // should not happen..
46087         }
46088         var reg = false;
46089         for (var r in this.layout.regions) {
46090             reg = this.layout.getRegion(r);
46091             if (reg.getActivePanel()) {
46092                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46093                 reg.setActivePanel(reg.getActivePanel());
46094                 continue;
46095             }
46096             if (!reg.panels.length) {
46097                 continue;
46098             }
46099             reg.showPanel(reg.getPanel(0));
46100         }
46101         
46102         
46103         
46104         
46105     },
46106     
46107     /**
46108      * Returns the nested BorderLayout for this panel
46109      * @return {Roo.BorderLayout} 
46110      */
46111     getLayout : function(){
46112         return this.layout;
46113     },
46114     
46115      /**
46116      * Adds a xtype elements to the layout of the nested panel
46117      * <pre><code>
46118
46119 panel.addxtype({
46120        xtype : 'ContentPanel',
46121        region: 'west',
46122        items: [ .... ]
46123    }
46124 );
46125
46126 panel.addxtype({
46127         xtype : 'NestedLayoutPanel',
46128         region: 'west',
46129         layout: {
46130            center: { },
46131            west: { }   
46132         },
46133         items : [ ... list of content panels or nested layout panels.. ]
46134    }
46135 );
46136 </code></pre>
46137      * @param {Object} cfg Xtype definition of item to add.
46138      */
46139     addxtype : function(cfg) {
46140         return this.layout.addxtype(cfg);
46141     
46142     }
46143 });/*
46144  * Based on:
46145  * Ext JS Library 1.1.1
46146  * Copyright(c) 2006-2007, Ext JS, LLC.
46147  *
46148  * Originally Released Under LGPL - original licence link has changed is not relivant.
46149  *
46150  * Fork - LGPL
46151  * <script type="text/javascript">
46152  */
46153 /**
46154  * @class Roo.TabPanel
46155  * @extends Roo.util.Observable
46156  * A lightweight tab container.
46157  * <br><br>
46158  * Usage:
46159  * <pre><code>
46160 // basic tabs 1, built from existing content
46161 var tabs = new Roo.TabPanel("tabs1");
46162 tabs.addTab("script", "View Script");
46163 tabs.addTab("markup", "View Markup");
46164 tabs.activate("script");
46165
46166 // more advanced tabs, built from javascript
46167 var jtabs = new Roo.TabPanel("jtabs");
46168 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46169
46170 // set up the UpdateManager
46171 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46172 var updater = tab2.getUpdateManager();
46173 updater.setDefaultUrl("ajax1.htm");
46174 tab2.on('activate', updater.refresh, updater, true);
46175
46176 // Use setUrl for Ajax loading
46177 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46178 tab3.setUrl("ajax2.htm", null, true);
46179
46180 // Disabled tab
46181 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46182 tab4.disable();
46183
46184 jtabs.activate("jtabs-1");
46185  * </code></pre>
46186  * @constructor
46187  * Create a new TabPanel.
46188  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46189  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46190  */
46191 Roo.bootstrap.panel.Tabs = function(config){
46192     /**
46193     * The container element for this TabPanel.
46194     * @type Roo.Element
46195     */
46196     this.el = Roo.get(config.el);
46197     delete config.el;
46198     if(config){
46199         if(typeof config == "boolean"){
46200             this.tabPosition = config ? "bottom" : "top";
46201         }else{
46202             Roo.apply(this, config);
46203         }
46204     }
46205     
46206     if(this.tabPosition == "bottom"){
46207         // if tabs are at the bottom = create the body first.
46208         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46209         this.el.addClass("roo-tabs-bottom");
46210     }
46211     // next create the tabs holders
46212     
46213     if (this.tabPosition == "west"){
46214         
46215         var reg = this.region; // fake it..
46216         while (reg) {
46217             if (!reg.mgr.parent) {
46218                 break;
46219             }
46220             reg = reg.mgr.parent.region;
46221         }
46222         Roo.log("got nest?");
46223         Roo.log(reg);
46224         if (reg.mgr.getRegion('west')) {
46225             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46226             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46227             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46228             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46229             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46230         
46231             
46232         }
46233         
46234         
46235     } else {
46236      
46237         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46238         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46239         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46240         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46241     }
46242     
46243     
46244     if(Roo.isIE){
46245         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46246     }
46247     
46248     // finally - if tabs are at the top, then create the body last..
46249     if(this.tabPosition != "bottom"){
46250         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46251          * @type Roo.Element
46252          */
46253         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46254         this.el.addClass("roo-tabs-top");
46255     }
46256     this.items = [];
46257
46258     this.bodyEl.setStyle("position", "relative");
46259
46260     this.active = null;
46261     this.activateDelegate = this.activate.createDelegate(this);
46262
46263     this.addEvents({
46264         /**
46265          * @event tabchange
46266          * Fires when the active tab changes
46267          * @param {Roo.TabPanel} this
46268          * @param {Roo.TabPanelItem} activePanel The new active tab
46269          */
46270         "tabchange": true,
46271         /**
46272          * @event beforetabchange
46273          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46274          * @param {Roo.TabPanel} this
46275          * @param {Object} e Set cancel to true on this object to cancel the tab change
46276          * @param {Roo.TabPanelItem} tab The tab being changed to
46277          */
46278         "beforetabchange" : true
46279     });
46280
46281     Roo.EventManager.onWindowResize(this.onResize, this);
46282     this.cpad = this.el.getPadding("lr");
46283     this.hiddenCount = 0;
46284
46285
46286     // toolbar on the tabbar support...
46287     if (this.toolbar) {
46288         alert("no toolbar support yet");
46289         this.toolbar  = false;
46290         /*
46291         var tcfg = this.toolbar;
46292         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46293         this.toolbar = new Roo.Toolbar(tcfg);
46294         if (Roo.isSafari) {
46295             var tbl = tcfg.container.child('table', true);
46296             tbl.setAttribute('width', '100%');
46297         }
46298         */
46299         
46300     }
46301    
46302
46303
46304     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46305 };
46306
46307 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46308     /*
46309      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46310      */
46311     tabPosition : "top",
46312     /*
46313      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46314      */
46315     currentTabWidth : 0,
46316     /*
46317      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46318      */
46319     minTabWidth : 40,
46320     /*
46321      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46322      */
46323     maxTabWidth : 250,
46324     /*
46325      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46326      */
46327     preferredTabWidth : 175,
46328     /*
46329      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46330      */
46331     resizeTabs : false,
46332     /*
46333      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46334      */
46335     monitorResize : true,
46336     /*
46337      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46338      */
46339     toolbar : false,  // set by caller..
46340     
46341     region : false, /// set by caller
46342     
46343     disableTooltips : true, // not used yet...
46344
46345     /**
46346      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46347      * @param {String} id The id of the div to use <b>or create</b>
46348      * @param {String} text The text for the tab
46349      * @param {String} content (optional) Content to put in the TabPanelItem body
46350      * @param {Boolean} closable (optional) True to create a close icon on the tab
46351      * @return {Roo.TabPanelItem} The created TabPanelItem
46352      */
46353     addTab : function(id, text, content, closable, tpl)
46354     {
46355         var item = new Roo.bootstrap.panel.TabItem({
46356             panel: this,
46357             id : id,
46358             text : text,
46359             closable : closable,
46360             tpl : tpl
46361         });
46362         this.addTabItem(item);
46363         if(content){
46364             item.setContent(content);
46365         }
46366         return item;
46367     },
46368
46369     /**
46370      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46371      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46372      * @return {Roo.TabPanelItem}
46373      */
46374     getTab : function(id){
46375         return this.items[id];
46376     },
46377
46378     /**
46379      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46380      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46381      */
46382     hideTab : function(id){
46383         var t = this.items[id];
46384         if(!t.isHidden()){
46385            t.setHidden(true);
46386            this.hiddenCount++;
46387            this.autoSizeTabs();
46388         }
46389     },
46390
46391     /**
46392      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46393      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46394      */
46395     unhideTab : function(id){
46396         var t = this.items[id];
46397         if(t.isHidden()){
46398            t.setHidden(false);
46399            this.hiddenCount--;
46400            this.autoSizeTabs();
46401         }
46402     },
46403
46404     /**
46405      * Adds an existing {@link Roo.TabPanelItem}.
46406      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46407      */
46408     addTabItem : function(item)
46409     {
46410         this.items[item.id] = item;
46411         this.items.push(item);
46412         this.autoSizeTabs();
46413       //  if(this.resizeTabs){
46414     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46415   //         this.autoSizeTabs();
46416 //        }else{
46417 //            item.autoSize();
46418        // }
46419     },
46420
46421     /**
46422      * Removes a {@link Roo.TabPanelItem}.
46423      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46424      */
46425     removeTab : function(id){
46426         var items = this.items;
46427         var tab = items[id];
46428         if(!tab) { return; }
46429         var index = items.indexOf(tab);
46430         if(this.active == tab && items.length > 1){
46431             var newTab = this.getNextAvailable(index);
46432             if(newTab) {
46433                 newTab.activate();
46434             }
46435         }
46436         this.stripEl.dom.removeChild(tab.pnode.dom);
46437         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46438             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46439         }
46440         items.splice(index, 1);
46441         delete this.items[tab.id];
46442         tab.fireEvent("close", tab);
46443         tab.purgeListeners();
46444         this.autoSizeTabs();
46445     },
46446
46447     getNextAvailable : function(start){
46448         var items = this.items;
46449         var index = start;
46450         // look for a next tab that will slide over to
46451         // replace the one being removed
46452         while(index < items.length){
46453             var item = items[++index];
46454             if(item && !item.isHidden()){
46455                 return item;
46456             }
46457         }
46458         // if one isn't found select the previous tab (on the left)
46459         index = start;
46460         while(index >= 0){
46461             var item = items[--index];
46462             if(item && !item.isHidden()){
46463                 return item;
46464             }
46465         }
46466         return null;
46467     },
46468
46469     /**
46470      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46471      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46472      */
46473     disableTab : function(id){
46474         var tab = this.items[id];
46475         if(tab && this.active != tab){
46476             tab.disable();
46477         }
46478     },
46479
46480     /**
46481      * Enables a {@link Roo.TabPanelItem} that is disabled.
46482      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46483      */
46484     enableTab : function(id){
46485         var tab = this.items[id];
46486         tab.enable();
46487     },
46488
46489     /**
46490      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46491      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46492      * @return {Roo.TabPanelItem} The TabPanelItem.
46493      */
46494     activate : function(id)
46495     {
46496         //Roo.log('activite:'  + id);
46497         
46498         var tab = this.items[id];
46499         if(!tab){
46500             return null;
46501         }
46502         if(tab == this.active || tab.disabled){
46503             return tab;
46504         }
46505         var e = {};
46506         this.fireEvent("beforetabchange", this, e, tab);
46507         if(e.cancel !== true && !tab.disabled){
46508             if(this.active){
46509                 this.active.hide();
46510             }
46511             this.active = this.items[id];
46512             this.active.show();
46513             this.fireEvent("tabchange", this, this.active);
46514         }
46515         return tab;
46516     },
46517
46518     /**
46519      * Gets the active {@link Roo.TabPanelItem}.
46520      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46521      */
46522     getActiveTab : function(){
46523         return this.active;
46524     },
46525
46526     /**
46527      * Updates the tab body element to fit the height of the container element
46528      * for overflow scrolling
46529      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46530      */
46531     syncHeight : function(targetHeight){
46532         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46533         var bm = this.bodyEl.getMargins();
46534         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46535         this.bodyEl.setHeight(newHeight);
46536         return newHeight;
46537     },
46538
46539     onResize : function(){
46540         if(this.monitorResize){
46541             this.autoSizeTabs();
46542         }
46543     },
46544
46545     /**
46546      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46547      */
46548     beginUpdate : function(){
46549         this.updating = true;
46550     },
46551
46552     /**
46553      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46554      */
46555     endUpdate : function(){
46556         this.updating = false;
46557         this.autoSizeTabs();
46558     },
46559
46560     /**
46561      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46562      */
46563     autoSizeTabs : function()
46564     {
46565         var count = this.items.length;
46566         var vcount = count - this.hiddenCount;
46567         
46568         if (vcount < 2) {
46569             this.stripEl.hide();
46570         } else {
46571             this.stripEl.show();
46572         }
46573         
46574         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46575             return;
46576         }
46577         
46578         
46579         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46580         var availWidth = Math.floor(w / vcount);
46581         var b = this.stripBody;
46582         if(b.getWidth() > w){
46583             var tabs = this.items;
46584             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46585             if(availWidth < this.minTabWidth){
46586                 /*if(!this.sleft){    // incomplete scrolling code
46587                     this.createScrollButtons();
46588                 }
46589                 this.showScroll();
46590                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46591             }
46592         }else{
46593             if(this.currentTabWidth < this.preferredTabWidth){
46594                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46595             }
46596         }
46597     },
46598
46599     /**
46600      * Returns the number of tabs in this TabPanel.
46601      * @return {Number}
46602      */
46603      getCount : function(){
46604          return this.items.length;
46605      },
46606
46607     /**
46608      * Resizes all the tabs to the passed width
46609      * @param {Number} The new width
46610      */
46611     setTabWidth : function(width){
46612         this.currentTabWidth = width;
46613         for(var i = 0, len = this.items.length; i < len; i++) {
46614                 if(!this.items[i].isHidden()) {
46615                 this.items[i].setWidth(width);
46616             }
46617         }
46618     },
46619
46620     /**
46621      * Destroys this TabPanel
46622      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46623      */
46624     destroy : function(removeEl){
46625         Roo.EventManager.removeResizeListener(this.onResize, this);
46626         for(var i = 0, len = this.items.length; i < len; i++){
46627             this.items[i].purgeListeners();
46628         }
46629         if(removeEl === true){
46630             this.el.update("");
46631             this.el.remove();
46632         }
46633     },
46634     
46635     createStrip : function(container)
46636     {
46637         var strip = document.createElement("nav");
46638         strip.className = Roo.bootstrap.version == 4 ?
46639             "navbar-light bg-light" : 
46640             "navbar navbar-default"; //"x-tabs-wrap";
46641         container.appendChild(strip);
46642         return strip;
46643     },
46644     
46645     createStripList : function(strip)
46646     {
46647         // div wrapper for retard IE
46648         // returns the "tr" element.
46649         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46650         //'<div class="x-tabs-strip-wrap">'+
46651           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46652           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46653         return strip.firstChild; //.firstChild.firstChild.firstChild;
46654     },
46655     createBody : function(container)
46656     {
46657         var body = document.createElement("div");
46658         Roo.id(body, "tab-body");
46659         //Roo.fly(body).addClass("x-tabs-body");
46660         Roo.fly(body).addClass("tab-content");
46661         container.appendChild(body);
46662         return body;
46663     },
46664     createItemBody :function(bodyEl, id){
46665         var body = Roo.getDom(id);
46666         if(!body){
46667             body = document.createElement("div");
46668             body.id = id;
46669         }
46670         //Roo.fly(body).addClass("x-tabs-item-body");
46671         Roo.fly(body).addClass("tab-pane");
46672          bodyEl.insertBefore(body, bodyEl.firstChild);
46673         return body;
46674     },
46675     /** @private */
46676     createStripElements :  function(stripEl, text, closable, tpl)
46677     {
46678         var td = document.createElement("li"); // was td..
46679         td.className = 'nav-item';
46680         
46681         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46682         
46683         
46684         stripEl.appendChild(td);
46685         /*if(closable){
46686             td.className = "x-tabs-closable";
46687             if(!this.closeTpl){
46688                 this.closeTpl = new Roo.Template(
46689                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46690                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46691                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46692                 );
46693             }
46694             var el = this.closeTpl.overwrite(td, {"text": text});
46695             var close = el.getElementsByTagName("div")[0];
46696             var inner = el.getElementsByTagName("em")[0];
46697             return {"el": el, "close": close, "inner": inner};
46698         } else {
46699         */
46700         // not sure what this is..
46701 //            if(!this.tabTpl){
46702                 //this.tabTpl = new Roo.Template(
46703                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46704                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46705                 //);
46706 //                this.tabTpl = new Roo.Template(
46707 //                   '<a href="#">' +
46708 //                   '<span unselectable="on"' +
46709 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46710 //                            ' >{text}</span></a>'
46711 //                );
46712 //                
46713 //            }
46714
46715
46716             var template = tpl || this.tabTpl || false;
46717             
46718             if(!template){
46719                 template =  new Roo.Template(
46720                         Roo.bootstrap.version == 4 ? 
46721                             (
46722                                 '<a class="nav-link" href="#" unselectable="on"' +
46723                                      (this.disableTooltips ? '' : ' title="{text}"') +
46724                                      ' >{text}</a>'
46725                             ) : (
46726                                 '<a class="nav-link" href="#">' +
46727                                 '<span unselectable="on"' +
46728                                          (this.disableTooltips ? '' : ' title="{text}"') +
46729                                     ' >{text}</span></a>'
46730                             )
46731                 );
46732             }
46733             
46734             switch (typeof(template)) {
46735                 case 'object' :
46736                     break;
46737                 case 'string' :
46738                     template = new Roo.Template(template);
46739                     break;
46740                 default :
46741                     break;
46742             }
46743             
46744             var el = template.overwrite(td, {"text": text});
46745             
46746             var inner = el.getElementsByTagName("span")[0];
46747             
46748             return {"el": el, "inner": inner};
46749             
46750     }
46751         
46752     
46753 });
46754
46755 /**
46756  * @class Roo.TabPanelItem
46757  * @extends Roo.util.Observable
46758  * Represents an individual item (tab plus body) in a TabPanel.
46759  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46760  * @param {String} id The id of this TabPanelItem
46761  * @param {String} text The text for the tab of this TabPanelItem
46762  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46763  */
46764 Roo.bootstrap.panel.TabItem = function(config){
46765     /**
46766      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46767      * @type Roo.TabPanel
46768      */
46769     this.tabPanel = config.panel;
46770     /**
46771      * The id for this TabPanelItem
46772      * @type String
46773      */
46774     this.id = config.id;
46775     /** @private */
46776     this.disabled = false;
46777     /** @private */
46778     this.text = config.text;
46779     /** @private */
46780     this.loaded = false;
46781     this.closable = config.closable;
46782
46783     /**
46784      * The body element for this TabPanelItem.
46785      * @type Roo.Element
46786      */
46787     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46788     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46789     this.bodyEl.setStyle("display", "block");
46790     this.bodyEl.setStyle("zoom", "1");
46791     //this.hideAction();
46792
46793     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46794     /** @private */
46795     this.el = Roo.get(els.el);
46796     this.inner = Roo.get(els.inner, true);
46797      this.textEl = Roo.bootstrap.version == 4 ?
46798         this.el : Roo.get(this.el.dom.firstChild, true);
46799
46800     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46801     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46802
46803     
46804 //    this.el.on("mousedown", this.onTabMouseDown, this);
46805     this.el.on("click", this.onTabClick, this);
46806     /** @private */
46807     if(config.closable){
46808         var c = Roo.get(els.close, true);
46809         c.dom.title = this.closeText;
46810         c.addClassOnOver("close-over");
46811         c.on("click", this.closeClick, this);
46812      }
46813
46814     this.addEvents({
46815          /**
46816          * @event activate
46817          * Fires when this tab becomes the active tab.
46818          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46819          * @param {Roo.TabPanelItem} this
46820          */
46821         "activate": true,
46822         /**
46823          * @event beforeclose
46824          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46825          * @param {Roo.TabPanelItem} this
46826          * @param {Object} e Set cancel to true on this object to cancel the close.
46827          */
46828         "beforeclose": true,
46829         /**
46830          * @event close
46831          * Fires when this tab is closed.
46832          * @param {Roo.TabPanelItem} this
46833          */
46834          "close": true,
46835         /**
46836          * @event deactivate
46837          * Fires when this tab is no longer the active tab.
46838          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46839          * @param {Roo.TabPanelItem} this
46840          */
46841          "deactivate" : true
46842     });
46843     this.hidden = false;
46844
46845     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46846 };
46847
46848 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46849            {
46850     purgeListeners : function(){
46851        Roo.util.Observable.prototype.purgeListeners.call(this);
46852        this.el.removeAllListeners();
46853     },
46854     /**
46855      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46856      */
46857     show : function(){
46858         this.status_node.addClass("active");
46859         this.showAction();
46860         if(Roo.isOpera){
46861             this.tabPanel.stripWrap.repaint();
46862         }
46863         this.fireEvent("activate", this.tabPanel, this);
46864     },
46865
46866     /**
46867      * Returns true if this tab is the active tab.
46868      * @return {Boolean}
46869      */
46870     isActive : function(){
46871         return this.tabPanel.getActiveTab() == this;
46872     },
46873
46874     /**
46875      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46876      */
46877     hide : function(){
46878         this.status_node.removeClass("active");
46879         this.hideAction();
46880         this.fireEvent("deactivate", this.tabPanel, this);
46881     },
46882
46883     hideAction : function(){
46884         this.bodyEl.hide();
46885         this.bodyEl.setStyle("position", "absolute");
46886         this.bodyEl.setLeft("-20000px");
46887         this.bodyEl.setTop("-20000px");
46888     },
46889
46890     showAction : function(){
46891         this.bodyEl.setStyle("position", "relative");
46892         this.bodyEl.setTop("");
46893         this.bodyEl.setLeft("");
46894         this.bodyEl.show();
46895     },
46896
46897     /**
46898      * Set the tooltip for the tab.
46899      * @param {String} tooltip The tab's tooltip
46900      */
46901     setTooltip : function(text){
46902         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46903             this.textEl.dom.qtip = text;
46904             this.textEl.dom.removeAttribute('title');
46905         }else{
46906             this.textEl.dom.title = text;
46907         }
46908     },
46909
46910     onTabClick : function(e){
46911         e.preventDefault();
46912         this.tabPanel.activate(this.id);
46913     },
46914
46915     onTabMouseDown : function(e){
46916         e.preventDefault();
46917         this.tabPanel.activate(this.id);
46918     },
46919 /*
46920     getWidth : function(){
46921         return this.inner.getWidth();
46922     },
46923
46924     setWidth : function(width){
46925         var iwidth = width - this.linode.getPadding("lr");
46926         this.inner.setWidth(iwidth);
46927         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46928         this.linode.setWidth(width);
46929     },
46930 */
46931     /**
46932      * Show or hide the tab
46933      * @param {Boolean} hidden True to hide or false to show.
46934      */
46935     setHidden : function(hidden){
46936         this.hidden = hidden;
46937         this.linode.setStyle("display", hidden ? "none" : "");
46938     },
46939
46940     /**
46941      * Returns true if this tab is "hidden"
46942      * @return {Boolean}
46943      */
46944     isHidden : function(){
46945         return this.hidden;
46946     },
46947
46948     /**
46949      * Returns the text for this tab
46950      * @return {String}
46951      */
46952     getText : function(){
46953         return this.text;
46954     },
46955     /*
46956     autoSize : function(){
46957         //this.el.beginMeasure();
46958         this.textEl.setWidth(1);
46959         /*
46960          *  #2804 [new] Tabs in Roojs
46961          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46962          */
46963         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46964         //this.el.endMeasure();
46965     //},
46966
46967     /**
46968      * Sets the text for the tab (Note: this also sets the tooltip text)
46969      * @param {String} text The tab's text and tooltip
46970      */
46971     setText : function(text){
46972         this.text = text;
46973         this.textEl.update(text);
46974         this.setTooltip(text);
46975         //if(!this.tabPanel.resizeTabs){
46976         //    this.autoSize();
46977         //}
46978     },
46979     /**
46980      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46981      */
46982     activate : function(){
46983         this.tabPanel.activate(this.id);
46984     },
46985
46986     /**
46987      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46988      */
46989     disable : function(){
46990         if(this.tabPanel.active != this){
46991             this.disabled = true;
46992             this.status_node.addClass("disabled");
46993         }
46994     },
46995
46996     /**
46997      * Enables this TabPanelItem if it was previously disabled.
46998      */
46999     enable : function(){
47000         this.disabled = false;
47001         this.status_node.removeClass("disabled");
47002     },
47003
47004     /**
47005      * Sets the content for this TabPanelItem.
47006      * @param {String} content The content
47007      * @param {Boolean} loadScripts true to look for and load scripts
47008      */
47009     setContent : function(content, loadScripts){
47010         this.bodyEl.update(content, loadScripts);
47011     },
47012
47013     /**
47014      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47015      * @return {Roo.UpdateManager} The UpdateManager
47016      */
47017     getUpdateManager : function(){
47018         return this.bodyEl.getUpdateManager();
47019     },
47020
47021     /**
47022      * Set a URL to be used to load the content for this TabPanelItem.
47023      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47024      * @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)
47025      * @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)
47026      * @return {Roo.UpdateManager} The UpdateManager
47027      */
47028     setUrl : function(url, params, loadOnce){
47029         if(this.refreshDelegate){
47030             this.un('activate', this.refreshDelegate);
47031         }
47032         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47033         this.on("activate", this.refreshDelegate);
47034         return this.bodyEl.getUpdateManager();
47035     },
47036
47037     /** @private */
47038     _handleRefresh : function(url, params, loadOnce){
47039         if(!loadOnce || !this.loaded){
47040             var updater = this.bodyEl.getUpdateManager();
47041             updater.update(url, params, this._setLoaded.createDelegate(this));
47042         }
47043     },
47044
47045     /**
47046      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47047      *   Will fail silently if the setUrl method has not been called.
47048      *   This does not activate the panel, just updates its content.
47049      */
47050     refresh : function(){
47051         if(this.refreshDelegate){
47052            this.loaded = false;
47053            this.refreshDelegate();
47054         }
47055     },
47056
47057     /** @private */
47058     _setLoaded : function(){
47059         this.loaded = true;
47060     },
47061
47062     /** @private */
47063     closeClick : function(e){
47064         var o = {};
47065         e.stopEvent();
47066         this.fireEvent("beforeclose", this, o);
47067         if(o.cancel !== true){
47068             this.tabPanel.removeTab(this.id);
47069         }
47070     },
47071     /**
47072      * The text displayed in the tooltip for the close icon.
47073      * @type String
47074      */
47075     closeText : "Close this tab"
47076 });
47077 /**
47078 *    This script refer to:
47079 *    Title: International Telephone Input
47080 *    Author: Jack O'Connor
47081 *    Code version:  v12.1.12
47082 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47083 **/
47084
47085 Roo.bootstrap.form.PhoneInputData = function() {
47086     var d = [
47087       [
47088         "Afghanistan (‫افغانستان‬‎)",
47089         "af",
47090         "93"
47091       ],
47092       [
47093         "Albania (Shqipëri)",
47094         "al",
47095         "355"
47096       ],
47097       [
47098         "Algeria (‫الجزائر‬‎)",
47099         "dz",
47100         "213"
47101       ],
47102       [
47103         "American Samoa",
47104         "as",
47105         "1684"
47106       ],
47107       [
47108         "Andorra",
47109         "ad",
47110         "376"
47111       ],
47112       [
47113         "Angola",
47114         "ao",
47115         "244"
47116       ],
47117       [
47118         "Anguilla",
47119         "ai",
47120         "1264"
47121       ],
47122       [
47123         "Antigua and Barbuda",
47124         "ag",
47125         "1268"
47126       ],
47127       [
47128         "Argentina",
47129         "ar",
47130         "54"
47131       ],
47132       [
47133         "Armenia (Հայաստան)",
47134         "am",
47135         "374"
47136       ],
47137       [
47138         "Aruba",
47139         "aw",
47140         "297"
47141       ],
47142       [
47143         "Australia",
47144         "au",
47145         "61",
47146         0
47147       ],
47148       [
47149         "Austria (Österreich)",
47150         "at",
47151         "43"
47152       ],
47153       [
47154         "Azerbaijan (Azərbaycan)",
47155         "az",
47156         "994"
47157       ],
47158       [
47159         "Bahamas",
47160         "bs",
47161         "1242"
47162       ],
47163       [
47164         "Bahrain (‫البحرين‬‎)",
47165         "bh",
47166         "973"
47167       ],
47168       [
47169         "Bangladesh (বাংলাদেশ)",
47170         "bd",
47171         "880"
47172       ],
47173       [
47174         "Barbados",
47175         "bb",
47176         "1246"
47177       ],
47178       [
47179         "Belarus (Беларусь)",
47180         "by",
47181         "375"
47182       ],
47183       [
47184         "Belgium (België)",
47185         "be",
47186         "32"
47187       ],
47188       [
47189         "Belize",
47190         "bz",
47191         "501"
47192       ],
47193       [
47194         "Benin (Bénin)",
47195         "bj",
47196         "229"
47197       ],
47198       [
47199         "Bermuda",
47200         "bm",
47201         "1441"
47202       ],
47203       [
47204         "Bhutan (འབྲུག)",
47205         "bt",
47206         "975"
47207       ],
47208       [
47209         "Bolivia",
47210         "bo",
47211         "591"
47212       ],
47213       [
47214         "Bosnia and Herzegovina (Босна и Херцеговина)",
47215         "ba",
47216         "387"
47217       ],
47218       [
47219         "Botswana",
47220         "bw",
47221         "267"
47222       ],
47223       [
47224         "Brazil (Brasil)",
47225         "br",
47226         "55"
47227       ],
47228       [
47229         "British Indian Ocean Territory",
47230         "io",
47231         "246"
47232       ],
47233       [
47234         "British Virgin Islands",
47235         "vg",
47236         "1284"
47237       ],
47238       [
47239         "Brunei",
47240         "bn",
47241         "673"
47242       ],
47243       [
47244         "Bulgaria (България)",
47245         "bg",
47246         "359"
47247       ],
47248       [
47249         "Burkina Faso",
47250         "bf",
47251         "226"
47252       ],
47253       [
47254         "Burundi (Uburundi)",
47255         "bi",
47256         "257"
47257       ],
47258       [
47259         "Cambodia (កម្ពុជា)",
47260         "kh",
47261         "855"
47262       ],
47263       [
47264         "Cameroon (Cameroun)",
47265         "cm",
47266         "237"
47267       ],
47268       [
47269         "Canada",
47270         "ca",
47271         "1",
47272         1,
47273         ["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"]
47274       ],
47275       [
47276         "Cape Verde (Kabu Verdi)",
47277         "cv",
47278         "238"
47279       ],
47280       [
47281         "Caribbean Netherlands",
47282         "bq",
47283         "599",
47284         1
47285       ],
47286       [
47287         "Cayman Islands",
47288         "ky",
47289         "1345"
47290       ],
47291       [
47292         "Central African Republic (République centrafricaine)",
47293         "cf",
47294         "236"
47295       ],
47296       [
47297         "Chad (Tchad)",
47298         "td",
47299         "235"
47300       ],
47301       [
47302         "Chile",
47303         "cl",
47304         "56"
47305       ],
47306       [
47307         "China (中国)",
47308         "cn",
47309         "86"
47310       ],
47311       [
47312         "Christmas Island",
47313         "cx",
47314         "61",
47315         2
47316       ],
47317       [
47318         "Cocos (Keeling) Islands",
47319         "cc",
47320         "61",
47321         1
47322       ],
47323       [
47324         "Colombia",
47325         "co",
47326         "57"
47327       ],
47328       [
47329         "Comoros (‫جزر القمر‬‎)",
47330         "km",
47331         "269"
47332       ],
47333       [
47334         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47335         "cd",
47336         "243"
47337       ],
47338       [
47339         "Congo (Republic) (Congo-Brazzaville)",
47340         "cg",
47341         "242"
47342       ],
47343       [
47344         "Cook Islands",
47345         "ck",
47346         "682"
47347       ],
47348       [
47349         "Costa Rica",
47350         "cr",
47351         "506"
47352       ],
47353       [
47354         "Côte d’Ivoire",
47355         "ci",
47356         "225"
47357       ],
47358       [
47359         "Croatia (Hrvatska)",
47360         "hr",
47361         "385"
47362       ],
47363       [
47364         "Cuba",
47365         "cu",
47366         "53"
47367       ],
47368       [
47369         "Curaçao",
47370         "cw",
47371         "599",
47372         0
47373       ],
47374       [
47375         "Cyprus (Κύπρος)",
47376         "cy",
47377         "357"
47378       ],
47379       [
47380         "Czech Republic (Česká republika)",
47381         "cz",
47382         "420"
47383       ],
47384       [
47385         "Denmark (Danmark)",
47386         "dk",
47387         "45"
47388       ],
47389       [
47390         "Djibouti",
47391         "dj",
47392         "253"
47393       ],
47394       [
47395         "Dominica",
47396         "dm",
47397         "1767"
47398       ],
47399       [
47400         "Dominican Republic (República Dominicana)",
47401         "do",
47402         "1",
47403         2,
47404         ["809", "829", "849"]
47405       ],
47406       [
47407         "Ecuador",
47408         "ec",
47409         "593"
47410       ],
47411       [
47412         "Egypt (‫مصر‬‎)",
47413         "eg",
47414         "20"
47415       ],
47416       [
47417         "El Salvador",
47418         "sv",
47419         "503"
47420       ],
47421       [
47422         "Equatorial Guinea (Guinea Ecuatorial)",
47423         "gq",
47424         "240"
47425       ],
47426       [
47427         "Eritrea",
47428         "er",
47429         "291"
47430       ],
47431       [
47432         "Estonia (Eesti)",
47433         "ee",
47434         "372"
47435       ],
47436       [
47437         "Ethiopia",
47438         "et",
47439         "251"
47440       ],
47441       [
47442         "Falkland Islands (Islas Malvinas)",
47443         "fk",
47444         "500"
47445       ],
47446       [
47447         "Faroe Islands (Føroyar)",
47448         "fo",
47449         "298"
47450       ],
47451       [
47452         "Fiji",
47453         "fj",
47454         "679"
47455       ],
47456       [
47457         "Finland (Suomi)",
47458         "fi",
47459         "358",
47460         0
47461       ],
47462       [
47463         "France",
47464         "fr",
47465         "33"
47466       ],
47467       [
47468         "French Guiana (Guyane française)",
47469         "gf",
47470         "594"
47471       ],
47472       [
47473         "French Polynesia (Polynésie française)",
47474         "pf",
47475         "689"
47476       ],
47477       [
47478         "Gabon",
47479         "ga",
47480         "241"
47481       ],
47482       [
47483         "Gambia",
47484         "gm",
47485         "220"
47486       ],
47487       [
47488         "Georgia (საქართველო)",
47489         "ge",
47490         "995"
47491       ],
47492       [
47493         "Germany (Deutschland)",
47494         "de",
47495         "49"
47496       ],
47497       [
47498         "Ghana (Gaana)",
47499         "gh",
47500         "233"
47501       ],
47502       [
47503         "Gibraltar",
47504         "gi",
47505         "350"
47506       ],
47507       [
47508         "Greece (Ελλάδα)",
47509         "gr",
47510         "30"
47511       ],
47512       [
47513         "Greenland (Kalaallit Nunaat)",
47514         "gl",
47515         "299"
47516       ],
47517       [
47518         "Grenada",
47519         "gd",
47520         "1473"
47521       ],
47522       [
47523         "Guadeloupe",
47524         "gp",
47525         "590",
47526         0
47527       ],
47528       [
47529         "Guam",
47530         "gu",
47531         "1671"
47532       ],
47533       [
47534         "Guatemala",
47535         "gt",
47536         "502"
47537       ],
47538       [
47539         "Guernsey",
47540         "gg",
47541         "44",
47542         1
47543       ],
47544       [
47545         "Guinea (Guinée)",
47546         "gn",
47547         "224"
47548       ],
47549       [
47550         "Guinea-Bissau (Guiné Bissau)",
47551         "gw",
47552         "245"
47553       ],
47554       [
47555         "Guyana",
47556         "gy",
47557         "592"
47558       ],
47559       [
47560         "Haiti",
47561         "ht",
47562         "509"
47563       ],
47564       [
47565         "Honduras",
47566         "hn",
47567         "504"
47568       ],
47569       [
47570         "Hong Kong (香港)",
47571         "hk",
47572         "852"
47573       ],
47574       [
47575         "Hungary (Magyarország)",
47576         "hu",
47577         "36"
47578       ],
47579       [
47580         "Iceland (Ísland)",
47581         "is",
47582         "354"
47583       ],
47584       [
47585         "India (भारत)",
47586         "in",
47587         "91"
47588       ],
47589       [
47590         "Indonesia",
47591         "id",
47592         "62"
47593       ],
47594       [
47595         "Iran (‫ایران‬‎)",
47596         "ir",
47597         "98"
47598       ],
47599       [
47600         "Iraq (‫العراق‬‎)",
47601         "iq",
47602         "964"
47603       ],
47604       [
47605         "Ireland",
47606         "ie",
47607         "353"
47608       ],
47609       [
47610         "Isle of Man",
47611         "im",
47612         "44",
47613         2
47614       ],
47615       [
47616         "Israel (‫ישראל‬‎)",
47617         "il",
47618         "972"
47619       ],
47620       [
47621         "Italy (Italia)",
47622         "it",
47623         "39",
47624         0
47625       ],
47626       [
47627         "Jamaica",
47628         "jm",
47629         "1876"
47630       ],
47631       [
47632         "Japan (日本)",
47633         "jp",
47634         "81"
47635       ],
47636       [
47637         "Jersey",
47638         "je",
47639         "44",
47640         3
47641       ],
47642       [
47643         "Jordan (‫الأردن‬‎)",
47644         "jo",
47645         "962"
47646       ],
47647       [
47648         "Kazakhstan (Казахстан)",
47649         "kz",
47650         "7",
47651         1
47652       ],
47653       [
47654         "Kenya",
47655         "ke",
47656         "254"
47657       ],
47658       [
47659         "Kiribati",
47660         "ki",
47661         "686"
47662       ],
47663       [
47664         "Kosovo",
47665         "xk",
47666         "383"
47667       ],
47668       [
47669         "Kuwait (‫الكويت‬‎)",
47670         "kw",
47671         "965"
47672       ],
47673       [
47674         "Kyrgyzstan (Кыргызстан)",
47675         "kg",
47676         "996"
47677       ],
47678       [
47679         "Laos (ລາວ)",
47680         "la",
47681         "856"
47682       ],
47683       [
47684         "Latvia (Latvija)",
47685         "lv",
47686         "371"
47687       ],
47688       [
47689         "Lebanon (‫لبنان‬‎)",
47690         "lb",
47691         "961"
47692       ],
47693       [
47694         "Lesotho",
47695         "ls",
47696         "266"
47697       ],
47698       [
47699         "Liberia",
47700         "lr",
47701         "231"
47702       ],
47703       [
47704         "Libya (‫ليبيا‬‎)",
47705         "ly",
47706         "218"
47707       ],
47708       [
47709         "Liechtenstein",
47710         "li",
47711         "423"
47712       ],
47713       [
47714         "Lithuania (Lietuva)",
47715         "lt",
47716         "370"
47717       ],
47718       [
47719         "Luxembourg",
47720         "lu",
47721         "352"
47722       ],
47723       [
47724         "Macau (澳門)",
47725         "mo",
47726         "853"
47727       ],
47728       [
47729         "Macedonia (FYROM) (Македонија)",
47730         "mk",
47731         "389"
47732       ],
47733       [
47734         "Madagascar (Madagasikara)",
47735         "mg",
47736         "261"
47737       ],
47738       [
47739         "Malawi",
47740         "mw",
47741         "265"
47742       ],
47743       [
47744         "Malaysia",
47745         "my",
47746         "60"
47747       ],
47748       [
47749         "Maldives",
47750         "mv",
47751         "960"
47752       ],
47753       [
47754         "Mali",
47755         "ml",
47756         "223"
47757       ],
47758       [
47759         "Malta",
47760         "mt",
47761         "356"
47762       ],
47763       [
47764         "Marshall Islands",
47765         "mh",
47766         "692"
47767       ],
47768       [
47769         "Martinique",
47770         "mq",
47771         "596"
47772       ],
47773       [
47774         "Mauritania (‫موريتانيا‬‎)",
47775         "mr",
47776         "222"
47777       ],
47778       [
47779         "Mauritius (Moris)",
47780         "mu",
47781         "230"
47782       ],
47783       [
47784         "Mayotte",
47785         "yt",
47786         "262",
47787         1
47788       ],
47789       [
47790         "Mexico (México)",
47791         "mx",
47792         "52"
47793       ],
47794       [
47795         "Micronesia",
47796         "fm",
47797         "691"
47798       ],
47799       [
47800         "Moldova (Republica Moldova)",
47801         "md",
47802         "373"
47803       ],
47804       [
47805         "Monaco",
47806         "mc",
47807         "377"
47808       ],
47809       [
47810         "Mongolia (Монгол)",
47811         "mn",
47812         "976"
47813       ],
47814       [
47815         "Montenegro (Crna Gora)",
47816         "me",
47817         "382"
47818       ],
47819       [
47820         "Montserrat",
47821         "ms",
47822         "1664"
47823       ],
47824       [
47825         "Morocco (‫المغرب‬‎)",
47826         "ma",
47827         "212",
47828         0
47829       ],
47830       [
47831         "Mozambique (Moçambique)",
47832         "mz",
47833         "258"
47834       ],
47835       [
47836         "Myanmar (Burma) (မြန်မာ)",
47837         "mm",
47838         "95"
47839       ],
47840       [
47841         "Namibia (Namibië)",
47842         "na",
47843         "264"
47844       ],
47845       [
47846         "Nauru",
47847         "nr",
47848         "674"
47849       ],
47850       [
47851         "Nepal (नेपाल)",
47852         "np",
47853         "977"
47854       ],
47855       [
47856         "Netherlands (Nederland)",
47857         "nl",
47858         "31"
47859       ],
47860       [
47861         "New Caledonia (Nouvelle-Calédonie)",
47862         "nc",
47863         "687"
47864       ],
47865       [
47866         "New Zealand",
47867         "nz",
47868         "64"
47869       ],
47870       [
47871         "Nicaragua",
47872         "ni",
47873         "505"
47874       ],
47875       [
47876         "Niger (Nijar)",
47877         "ne",
47878         "227"
47879       ],
47880       [
47881         "Nigeria",
47882         "ng",
47883         "234"
47884       ],
47885       [
47886         "Niue",
47887         "nu",
47888         "683"
47889       ],
47890       [
47891         "Norfolk Island",
47892         "nf",
47893         "672"
47894       ],
47895       [
47896         "North Korea (조선 민주주의 인민 공화국)",
47897         "kp",
47898         "850"
47899       ],
47900       [
47901         "Northern Mariana Islands",
47902         "mp",
47903         "1670"
47904       ],
47905       [
47906         "Norway (Norge)",
47907         "no",
47908         "47",
47909         0
47910       ],
47911       [
47912         "Oman (‫عُمان‬‎)",
47913         "om",
47914         "968"
47915       ],
47916       [
47917         "Pakistan (‫پاکستان‬‎)",
47918         "pk",
47919         "92"
47920       ],
47921       [
47922         "Palau",
47923         "pw",
47924         "680"
47925       ],
47926       [
47927         "Palestine (‫فلسطين‬‎)",
47928         "ps",
47929         "970"
47930       ],
47931       [
47932         "Panama (Panamá)",
47933         "pa",
47934         "507"
47935       ],
47936       [
47937         "Papua New Guinea",
47938         "pg",
47939         "675"
47940       ],
47941       [
47942         "Paraguay",
47943         "py",
47944         "595"
47945       ],
47946       [
47947         "Peru (Perú)",
47948         "pe",
47949         "51"
47950       ],
47951       [
47952         "Philippines",
47953         "ph",
47954         "63"
47955       ],
47956       [
47957         "Poland (Polska)",
47958         "pl",
47959         "48"
47960       ],
47961       [
47962         "Portugal",
47963         "pt",
47964         "351"
47965       ],
47966       [
47967         "Puerto Rico",
47968         "pr",
47969         "1",
47970         3,
47971         ["787", "939"]
47972       ],
47973       [
47974         "Qatar (‫قطر‬‎)",
47975         "qa",
47976         "974"
47977       ],
47978       [
47979         "Réunion (La Réunion)",
47980         "re",
47981         "262",
47982         0
47983       ],
47984       [
47985         "Romania (România)",
47986         "ro",
47987         "40"
47988       ],
47989       [
47990         "Russia (Россия)",
47991         "ru",
47992         "7",
47993         0
47994       ],
47995       [
47996         "Rwanda",
47997         "rw",
47998         "250"
47999       ],
48000       [
48001         "Saint Barthélemy",
48002         "bl",
48003         "590",
48004         1
48005       ],
48006       [
48007         "Saint Helena",
48008         "sh",
48009         "290"
48010       ],
48011       [
48012         "Saint Kitts and Nevis",
48013         "kn",
48014         "1869"
48015       ],
48016       [
48017         "Saint Lucia",
48018         "lc",
48019         "1758"
48020       ],
48021       [
48022         "Saint Martin (Saint-Martin (partie française))",
48023         "mf",
48024         "590",
48025         2
48026       ],
48027       [
48028         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48029         "pm",
48030         "508"
48031       ],
48032       [
48033         "Saint Vincent and the Grenadines",
48034         "vc",
48035         "1784"
48036       ],
48037       [
48038         "Samoa",
48039         "ws",
48040         "685"
48041       ],
48042       [
48043         "San Marino",
48044         "sm",
48045         "378"
48046       ],
48047       [
48048         "São Tomé and Príncipe (São Tomé e Príncipe)",
48049         "st",
48050         "239"
48051       ],
48052       [
48053         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48054         "sa",
48055         "966"
48056       ],
48057       [
48058         "Senegal (Sénégal)",
48059         "sn",
48060         "221"
48061       ],
48062       [
48063         "Serbia (Србија)",
48064         "rs",
48065         "381"
48066       ],
48067       [
48068         "Seychelles",
48069         "sc",
48070         "248"
48071       ],
48072       [
48073         "Sierra Leone",
48074         "sl",
48075         "232"
48076       ],
48077       [
48078         "Singapore",
48079         "sg",
48080         "65"
48081       ],
48082       [
48083         "Sint Maarten",
48084         "sx",
48085         "1721"
48086       ],
48087       [
48088         "Slovakia (Slovensko)",
48089         "sk",
48090         "421"
48091       ],
48092       [
48093         "Slovenia (Slovenija)",
48094         "si",
48095         "386"
48096       ],
48097       [
48098         "Solomon Islands",
48099         "sb",
48100         "677"
48101       ],
48102       [
48103         "Somalia (Soomaaliya)",
48104         "so",
48105         "252"
48106       ],
48107       [
48108         "South Africa",
48109         "za",
48110         "27"
48111       ],
48112       [
48113         "South Korea (대한민국)",
48114         "kr",
48115         "82"
48116       ],
48117       [
48118         "South Sudan (‫جنوب السودان‬‎)",
48119         "ss",
48120         "211"
48121       ],
48122       [
48123         "Spain (España)",
48124         "es",
48125         "34"
48126       ],
48127       [
48128         "Sri Lanka (ශ්‍රී ලංකාව)",
48129         "lk",
48130         "94"
48131       ],
48132       [
48133         "Sudan (‫السودان‬‎)",
48134         "sd",
48135         "249"
48136       ],
48137       [
48138         "Suriname",
48139         "sr",
48140         "597"
48141       ],
48142       [
48143         "Svalbard and Jan Mayen",
48144         "sj",
48145         "47",
48146         1
48147       ],
48148       [
48149         "Swaziland",
48150         "sz",
48151         "268"
48152       ],
48153       [
48154         "Sweden (Sverige)",
48155         "se",
48156         "46"
48157       ],
48158       [
48159         "Switzerland (Schweiz)",
48160         "ch",
48161         "41"
48162       ],
48163       [
48164         "Syria (‫سوريا‬‎)",
48165         "sy",
48166         "963"
48167       ],
48168       [
48169         "Taiwan (台灣)",
48170         "tw",
48171         "886"
48172       ],
48173       [
48174         "Tajikistan",
48175         "tj",
48176         "992"
48177       ],
48178       [
48179         "Tanzania",
48180         "tz",
48181         "255"
48182       ],
48183       [
48184         "Thailand (ไทย)",
48185         "th",
48186         "66"
48187       ],
48188       [
48189         "Timor-Leste",
48190         "tl",
48191         "670"
48192       ],
48193       [
48194         "Togo",
48195         "tg",
48196         "228"
48197       ],
48198       [
48199         "Tokelau",
48200         "tk",
48201         "690"
48202       ],
48203       [
48204         "Tonga",
48205         "to",
48206         "676"
48207       ],
48208       [
48209         "Trinidad and Tobago",
48210         "tt",
48211         "1868"
48212       ],
48213       [
48214         "Tunisia (‫تونس‬‎)",
48215         "tn",
48216         "216"
48217       ],
48218       [
48219         "Turkey (Türkiye)",
48220         "tr",
48221         "90"
48222       ],
48223       [
48224         "Turkmenistan",
48225         "tm",
48226         "993"
48227       ],
48228       [
48229         "Turks and Caicos Islands",
48230         "tc",
48231         "1649"
48232       ],
48233       [
48234         "Tuvalu",
48235         "tv",
48236         "688"
48237       ],
48238       [
48239         "U.S. Virgin Islands",
48240         "vi",
48241         "1340"
48242       ],
48243       [
48244         "Uganda",
48245         "ug",
48246         "256"
48247       ],
48248       [
48249         "Ukraine (Україна)",
48250         "ua",
48251         "380"
48252       ],
48253       [
48254         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48255         "ae",
48256         "971"
48257       ],
48258       [
48259         "United Kingdom",
48260         "gb",
48261         "44",
48262         0
48263       ],
48264       [
48265         "United States",
48266         "us",
48267         "1",
48268         0
48269       ],
48270       [
48271         "Uruguay",
48272         "uy",
48273         "598"
48274       ],
48275       [
48276         "Uzbekistan (Oʻzbekiston)",
48277         "uz",
48278         "998"
48279       ],
48280       [
48281         "Vanuatu",
48282         "vu",
48283         "678"
48284       ],
48285       [
48286         "Vatican City (Città del Vaticano)",
48287         "va",
48288         "39",
48289         1
48290       ],
48291       [
48292         "Venezuela",
48293         "ve",
48294         "58"
48295       ],
48296       [
48297         "Vietnam (Việt Nam)",
48298         "vn",
48299         "84"
48300       ],
48301       [
48302         "Wallis and Futuna (Wallis-et-Futuna)",
48303         "wf",
48304         "681"
48305       ],
48306       [
48307         "Western Sahara (‫الصحراء الغربية‬‎)",
48308         "eh",
48309         "212",
48310         1
48311       ],
48312       [
48313         "Yemen (‫اليمن‬‎)",
48314         "ye",
48315         "967"
48316       ],
48317       [
48318         "Zambia",
48319         "zm",
48320         "260"
48321       ],
48322       [
48323         "Zimbabwe",
48324         "zw",
48325         "263"
48326       ],
48327       [
48328         "Åland Islands",
48329         "ax",
48330         "358",
48331         1
48332       ]
48333   ];
48334   
48335   return d;
48336 }/**
48337 *    This script refer to:
48338 *    Title: International Telephone Input
48339 *    Author: Jack O'Connor
48340 *    Code version:  v12.1.12
48341 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48342 **/
48343
48344 /**
48345  * @class Roo.bootstrap.form.PhoneInput
48346  * @extends Roo.bootstrap.form.TriggerField
48347  * An input with International dial-code selection
48348  
48349  * @cfg {String} defaultDialCode default '+852'
48350  * @cfg {Array} preferedCountries default []
48351   
48352  * @constructor
48353  * Create a new PhoneInput.
48354  * @param {Object} config Configuration options
48355  */
48356
48357 Roo.bootstrap.form.PhoneInput = function(config) {
48358     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48359 };
48360
48361 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48362         /**
48363         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48364         */
48365         listWidth: undefined,
48366         
48367         selectedClass: 'active',
48368         
48369         invalidClass : "has-warning",
48370         
48371         validClass: 'has-success',
48372         
48373         allowed: '0123456789',
48374         
48375         max_length: 15,
48376         
48377         /**
48378          * @cfg {String} defaultDialCode The default dial code when initializing the input
48379          */
48380         defaultDialCode: '+852',
48381         
48382         /**
48383          * @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
48384          */
48385         preferedCountries: false,
48386         
48387         getAutoCreate : function()
48388         {
48389             var data = Roo.bootstrap.form.PhoneInputData();
48390             var align = this.labelAlign || this.parentLabelAlign();
48391             var id = Roo.id();
48392             
48393             this.allCountries = [];
48394             this.dialCodeMapping = [];
48395             
48396             for (var i = 0; i < data.length; i++) {
48397               var c = data[i];
48398               this.allCountries[i] = {
48399                 name: c[0],
48400                 iso2: c[1],
48401                 dialCode: c[2],
48402                 priority: c[3] || 0,
48403                 areaCodes: c[4] || null
48404               };
48405               this.dialCodeMapping[c[2]] = {
48406                   name: c[0],
48407                   iso2: c[1],
48408                   priority: c[3] || 0,
48409                   areaCodes: c[4] || null
48410               };
48411             }
48412             
48413             var cfg = {
48414                 cls: 'form-group',
48415                 cn: []
48416             };
48417             
48418             var input =  {
48419                 tag: 'input',
48420                 id : id,
48421                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48422                 maxlength: this.max_length,
48423                 cls : 'form-control tel-input',
48424                 autocomplete: 'new-password'
48425             };
48426             
48427             var hiddenInput = {
48428                 tag: 'input',
48429                 type: 'hidden',
48430                 cls: 'hidden-tel-input'
48431             };
48432             
48433             if (this.name) {
48434                 hiddenInput.name = this.name;
48435             }
48436             
48437             if (this.disabled) {
48438                 input.disabled = true;
48439             }
48440             
48441             var flag_container = {
48442                 tag: 'div',
48443                 cls: 'flag-box',
48444                 cn: [
48445                     {
48446                         tag: 'div',
48447                         cls: 'flag'
48448                     },
48449                     {
48450                         tag: 'div',
48451                         cls: 'caret'
48452                     }
48453                 ]
48454             };
48455             
48456             var box = {
48457                 tag: 'div',
48458                 cls: this.hasFeedback ? 'has-feedback' : '',
48459                 cn: [
48460                     hiddenInput,
48461                     input,
48462                     {
48463                         tag: 'input',
48464                         cls: 'dial-code-holder',
48465                         disabled: true
48466                     }
48467                 ]
48468             };
48469             
48470             var container = {
48471                 cls: 'roo-select2-container input-group',
48472                 cn: [
48473                     flag_container,
48474                     box
48475                 ]
48476             };
48477             
48478             if (this.fieldLabel.length) {
48479                 var indicator = {
48480                     tag: 'i',
48481                     tooltip: 'This field is required'
48482                 };
48483                 
48484                 var label = {
48485                     tag: 'label',
48486                     'for':  id,
48487                     cls: 'control-label',
48488                     cn: []
48489                 };
48490                 
48491                 var label_text = {
48492                     tag: 'span',
48493                     html: this.fieldLabel
48494                 };
48495                 
48496                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48497                 label.cn = [
48498                     indicator,
48499                     label_text
48500                 ];
48501                 
48502                 if(this.indicatorpos == 'right') {
48503                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48504                     label.cn = [
48505                         label_text,
48506                         indicator
48507                     ];
48508                 }
48509                 
48510                 if(align == 'left') {
48511                     container = {
48512                         tag: 'div',
48513                         cn: [
48514                             container
48515                         ]
48516                     };
48517                     
48518                     if(this.labelWidth > 12){
48519                         label.style = "width: " + this.labelWidth + 'px';
48520                     }
48521                     if(this.labelWidth < 13 && this.labelmd == 0){
48522                         this.labelmd = this.labelWidth;
48523                     }
48524                     if(this.labellg > 0){
48525                         label.cls += ' col-lg-' + this.labellg;
48526                         input.cls += ' col-lg-' + (12 - this.labellg);
48527                     }
48528                     if(this.labelmd > 0){
48529                         label.cls += ' col-md-' + this.labelmd;
48530                         container.cls += ' col-md-' + (12 - this.labelmd);
48531                     }
48532                     if(this.labelsm > 0){
48533                         label.cls += ' col-sm-' + this.labelsm;
48534                         container.cls += ' col-sm-' + (12 - this.labelsm);
48535                     }
48536                     if(this.labelxs > 0){
48537                         label.cls += ' col-xs-' + this.labelxs;
48538                         container.cls += ' col-xs-' + (12 - this.labelxs);
48539                     }
48540                 }
48541             }
48542             
48543             cfg.cn = [
48544                 label,
48545                 container
48546             ];
48547             
48548             var settings = this;
48549             
48550             ['xs','sm','md','lg'].map(function(size){
48551                 if (settings[size]) {
48552                     cfg.cls += ' col-' + size + '-' + settings[size];
48553                 }
48554             });
48555             
48556             this.store = new Roo.data.Store({
48557                 proxy : new Roo.data.MemoryProxy({}),
48558                 reader : new Roo.data.JsonReader({
48559                     fields : [
48560                         {
48561                             'name' : 'name',
48562                             'type' : 'string'
48563                         },
48564                         {
48565                             'name' : 'iso2',
48566                             'type' : 'string'
48567                         },
48568                         {
48569                             'name' : 'dialCode',
48570                             'type' : 'string'
48571                         },
48572                         {
48573                             'name' : 'priority',
48574                             'type' : 'string'
48575                         },
48576                         {
48577                             'name' : 'areaCodes',
48578                             'type' : 'string'
48579                         }
48580                     ]
48581                 })
48582             });
48583             
48584             if(!this.preferedCountries) {
48585                 this.preferedCountries = [
48586                     'hk',
48587                     'gb',
48588                     'us'
48589                 ];
48590             }
48591             
48592             var p = this.preferedCountries.reverse();
48593             
48594             if(p) {
48595                 for (var i = 0; i < p.length; i++) {
48596                     for (var j = 0; j < this.allCountries.length; j++) {
48597                         if(this.allCountries[j].iso2 == p[i]) {
48598                             var t = this.allCountries[j];
48599                             this.allCountries.splice(j,1);
48600                             this.allCountries.unshift(t);
48601                         }
48602                     } 
48603                 }
48604             }
48605             
48606             this.store.proxy.data = {
48607                 success: true,
48608                 data: this.allCountries
48609             };
48610             
48611             return cfg;
48612         },
48613         
48614         initEvents : function()
48615         {
48616             this.createList();
48617             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48618             
48619             this.indicator = this.indicatorEl();
48620             this.flag = this.flagEl();
48621             this.dialCodeHolder = this.dialCodeHolderEl();
48622             
48623             this.trigger = this.el.select('div.flag-box',true).first();
48624             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48625             
48626             var _this = this;
48627             
48628             (function(){
48629                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48630                 _this.list.setWidth(lw);
48631             }).defer(100);
48632             
48633             this.list.on('mouseover', this.onViewOver, this);
48634             this.list.on('mousemove', this.onViewMove, this);
48635             this.inputEl().on("keyup", this.onKeyUp, this);
48636             this.inputEl().on("keypress", this.onKeyPress, this);
48637             
48638             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48639
48640             this.view = new Roo.View(this.list, this.tpl, {
48641                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48642             });
48643             
48644             this.view.on('click', this.onViewClick, this);
48645             this.setValue(this.defaultDialCode);
48646         },
48647         
48648         onTriggerClick : function(e)
48649         {
48650             Roo.log('trigger click');
48651             if(this.disabled){
48652                 return;
48653             }
48654             
48655             if(this.isExpanded()){
48656                 this.collapse();
48657                 this.hasFocus = false;
48658             }else {
48659                 this.store.load({});
48660                 this.hasFocus = true;
48661                 this.expand();
48662             }
48663         },
48664         
48665         isExpanded : function()
48666         {
48667             return this.list.isVisible();
48668         },
48669         
48670         collapse : function()
48671         {
48672             if(!this.isExpanded()){
48673                 return;
48674             }
48675             this.list.hide();
48676             Roo.get(document).un('mousedown', this.collapseIf, this);
48677             Roo.get(document).un('mousewheel', this.collapseIf, this);
48678             this.fireEvent('collapse', this);
48679             this.validate();
48680         },
48681         
48682         expand : function()
48683         {
48684             Roo.log('expand');
48685
48686             if(this.isExpanded() || !this.hasFocus){
48687                 return;
48688             }
48689             
48690             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48691             this.list.setWidth(lw);
48692             
48693             this.list.show();
48694             this.restrictHeight();
48695             
48696             Roo.get(document).on('mousedown', this.collapseIf, this);
48697             Roo.get(document).on('mousewheel', this.collapseIf, this);
48698             
48699             this.fireEvent('expand', this);
48700         },
48701         
48702         restrictHeight : function()
48703         {
48704             this.list.alignTo(this.inputEl(), this.listAlign);
48705             this.list.alignTo(this.inputEl(), this.listAlign);
48706         },
48707         
48708         onViewOver : function(e, t)
48709         {
48710             if(this.inKeyMode){
48711                 return;
48712             }
48713             var item = this.view.findItemFromChild(t);
48714             
48715             if(item){
48716                 var index = this.view.indexOf(item);
48717                 this.select(index, false);
48718             }
48719         },
48720
48721         // private
48722         onViewClick : function(view, doFocus, el, e)
48723         {
48724             var index = this.view.getSelectedIndexes()[0];
48725             
48726             var r = this.store.getAt(index);
48727             
48728             if(r){
48729                 this.onSelect(r, index);
48730             }
48731             if(doFocus !== false && !this.blockFocus){
48732                 this.inputEl().focus();
48733             }
48734         },
48735         
48736         onViewMove : function(e, t)
48737         {
48738             this.inKeyMode = false;
48739         },
48740         
48741         select : function(index, scrollIntoView)
48742         {
48743             this.selectedIndex = index;
48744             this.view.select(index);
48745             if(scrollIntoView !== false){
48746                 var el = this.view.getNode(index);
48747                 if(el){
48748                     this.list.scrollChildIntoView(el, false);
48749                 }
48750             }
48751         },
48752         
48753         createList : function()
48754         {
48755             this.list = Roo.get(document.body).createChild({
48756                 tag: 'ul',
48757                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48758                 style: 'display:none'
48759             });
48760             
48761             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48762         },
48763         
48764         collapseIf : function(e)
48765         {
48766             var in_combo  = e.within(this.el);
48767             var in_list =  e.within(this.list);
48768             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48769             
48770             if (in_combo || in_list || is_list) {
48771                 return;
48772             }
48773             this.collapse();
48774         },
48775         
48776         onSelect : function(record, index)
48777         {
48778             if(this.fireEvent('beforeselect', this, record, index) !== false){
48779                 
48780                 this.setFlagClass(record.data.iso2);
48781                 this.setDialCode(record.data.dialCode);
48782                 this.hasFocus = false;
48783                 this.collapse();
48784                 this.fireEvent('select', this, record, index);
48785             }
48786         },
48787         
48788         flagEl : function()
48789         {
48790             var flag = this.el.select('div.flag',true).first();
48791             if(!flag){
48792                 return false;
48793             }
48794             return flag;
48795         },
48796         
48797         dialCodeHolderEl : function()
48798         {
48799             var d = this.el.select('input.dial-code-holder',true).first();
48800             if(!d){
48801                 return false;
48802             }
48803             return d;
48804         },
48805         
48806         setDialCode : function(v)
48807         {
48808             this.dialCodeHolder.dom.value = '+'+v;
48809         },
48810         
48811         setFlagClass : function(n)
48812         {
48813             this.flag.dom.className = 'flag '+n;
48814         },
48815         
48816         getValue : function()
48817         {
48818             var v = this.inputEl().getValue();
48819             if(this.dialCodeHolder) {
48820                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48821             }
48822             return v;
48823         },
48824         
48825         setValue : function(v)
48826         {
48827             var d = this.getDialCode(v);
48828             
48829             //invalid dial code
48830             if(v.length == 0 || !d || d.length == 0) {
48831                 if(this.rendered){
48832                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48833                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48834                 }
48835                 return;
48836             }
48837             
48838             //valid dial code
48839             this.setFlagClass(this.dialCodeMapping[d].iso2);
48840             this.setDialCode(d);
48841             this.inputEl().dom.value = v.replace('+'+d,'');
48842             this.hiddenEl().dom.value = this.getValue();
48843             
48844             this.validate();
48845         },
48846         
48847         getDialCode : function(v)
48848         {
48849             v = v ||  '';
48850             
48851             if (v.length == 0) {
48852                 return this.dialCodeHolder.dom.value;
48853             }
48854             
48855             var dialCode = "";
48856             if (v.charAt(0) != "+") {
48857                 return false;
48858             }
48859             var numericChars = "";
48860             for (var i = 1; i < v.length; i++) {
48861               var c = v.charAt(i);
48862               if (!isNaN(c)) {
48863                 numericChars += c;
48864                 if (this.dialCodeMapping[numericChars]) {
48865                   dialCode = v.substr(1, i);
48866                 }
48867                 if (numericChars.length == 4) {
48868                   break;
48869                 }
48870               }
48871             }
48872             return dialCode;
48873         },
48874         
48875         reset : function()
48876         {
48877             this.setValue(this.defaultDialCode);
48878             this.validate();
48879         },
48880         
48881         hiddenEl : function()
48882         {
48883             return this.el.select('input.hidden-tel-input',true).first();
48884         },
48885         
48886         // after setting val
48887         onKeyUp : function(e){
48888             this.setValue(this.getValue());
48889         },
48890         
48891         onKeyPress : function(e){
48892             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48893                 e.stopEvent();
48894             }
48895         }
48896         
48897 });
48898 /**
48899  * @class Roo.bootstrap.form.MoneyField
48900  * @extends Roo.bootstrap.form.ComboBox
48901  * Bootstrap MoneyField class
48902  * 
48903  * @constructor
48904  * Create a new MoneyField.
48905  * @param {Object} config Configuration options
48906  */
48907
48908 Roo.bootstrap.form.MoneyField = function(config) {
48909     
48910     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48911     
48912 };
48913
48914 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48915     
48916     /**
48917      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48918      */
48919     allowDecimals : true,
48920     /**
48921      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48922      */
48923     decimalSeparator : ".",
48924     /**
48925      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48926      */
48927     decimalPrecision : 0,
48928     /**
48929      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48930      */
48931     allowNegative : true,
48932     /**
48933      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48934      */
48935     allowZero: true,
48936     /**
48937      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48938      */
48939     minValue : Number.NEGATIVE_INFINITY,
48940     /**
48941      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48942      */
48943     maxValue : Number.MAX_VALUE,
48944     /**
48945      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48946      */
48947     minText : "The minimum value for this field is {0}",
48948     /**
48949      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48950      */
48951     maxText : "The maximum value for this field is {0}",
48952     /**
48953      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48954      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48955      */
48956     nanText : "{0} is not a valid number",
48957     /**
48958      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48959      */
48960     castInt : true,
48961     /**
48962      * @cfg {String} defaults currency of the MoneyField
48963      * value should be in lkey
48964      */
48965     defaultCurrency : false,
48966     /**
48967      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48968      */
48969     thousandsDelimiter : false,
48970     /**
48971      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48972      */
48973     max_length: false,
48974     
48975     inputlg : 9,
48976     inputmd : 9,
48977     inputsm : 9,
48978     inputxs : 6,
48979      /**
48980      * @cfg {Roo.data.Store} store  Store to lookup currency??
48981      */
48982     store : false,
48983     
48984     getAutoCreate : function()
48985     {
48986         var align = this.labelAlign || this.parentLabelAlign();
48987         
48988         var id = Roo.id();
48989
48990         var cfg = {
48991             cls: 'form-group',
48992             cn: []
48993         };
48994
48995         var input =  {
48996             tag: 'input',
48997             id : id,
48998             cls : 'form-control roo-money-amount-input',
48999             autocomplete: 'new-password'
49000         };
49001         
49002         var hiddenInput = {
49003             tag: 'input',
49004             type: 'hidden',
49005             id: Roo.id(),
49006             cls: 'hidden-number-input'
49007         };
49008         
49009         if(this.max_length) {
49010             input.maxlength = this.max_length; 
49011         }
49012         
49013         if (this.name) {
49014             hiddenInput.name = this.name;
49015         }
49016
49017         if (this.disabled) {
49018             input.disabled = true;
49019         }
49020
49021         var clg = 12 - this.inputlg;
49022         var cmd = 12 - this.inputmd;
49023         var csm = 12 - this.inputsm;
49024         var cxs = 12 - this.inputxs;
49025         
49026         var container = {
49027             tag : 'div',
49028             cls : 'row roo-money-field',
49029             cn : [
49030                 {
49031                     tag : 'div',
49032                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49033                     cn : [
49034                         {
49035                             tag : 'div',
49036                             cls: 'roo-select2-container input-group',
49037                             cn: [
49038                                 {
49039                                     tag : 'input',
49040                                     cls : 'form-control roo-money-currency-input',
49041                                     autocomplete: 'new-password',
49042                                     readOnly : 1,
49043                                     name : this.currencyName
49044                                 },
49045                                 {
49046                                     tag :'span',
49047                                     cls : 'input-group-addon',
49048                                     cn : [
49049                                         {
49050                                             tag: 'span',
49051                                             cls: 'caret'
49052                                         }
49053                                     ]
49054                                 }
49055                             ]
49056                         }
49057                     ]
49058                 },
49059                 {
49060                     tag : 'div',
49061                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49062                     cn : [
49063                         {
49064                             tag: 'div',
49065                             cls: this.hasFeedback ? 'has-feedback' : '',
49066                             cn: [
49067                                 input
49068                             ]
49069                         }
49070                     ]
49071                 }
49072             ]
49073             
49074         };
49075         
49076         if (this.fieldLabel.length) {
49077             var indicator = {
49078                 tag: 'i',
49079                 tooltip: 'This field is required'
49080             };
49081
49082             var label = {
49083                 tag: 'label',
49084                 'for':  id,
49085                 cls: 'control-label',
49086                 cn: []
49087             };
49088
49089             var label_text = {
49090                 tag: 'span',
49091                 html: this.fieldLabel
49092             };
49093
49094             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49095             label.cn = [
49096                 indicator,
49097                 label_text
49098             ];
49099
49100             if(this.indicatorpos == 'right') {
49101                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49102                 label.cn = [
49103                     label_text,
49104                     indicator
49105                 ];
49106             }
49107
49108             if(align == 'left') {
49109                 container = {
49110                     tag: 'div',
49111                     cn: [
49112                         container
49113                     ]
49114                 };
49115
49116                 if(this.labelWidth > 12){
49117                     label.style = "width: " + this.labelWidth + 'px';
49118                 }
49119                 if(this.labelWidth < 13 && this.labelmd == 0){
49120                     this.labelmd = this.labelWidth;
49121                 }
49122                 if(this.labellg > 0){
49123                     label.cls += ' col-lg-' + this.labellg;
49124                     input.cls += ' col-lg-' + (12 - this.labellg);
49125                 }
49126                 if(this.labelmd > 0){
49127                     label.cls += ' col-md-' + this.labelmd;
49128                     container.cls += ' col-md-' + (12 - this.labelmd);
49129                 }
49130                 if(this.labelsm > 0){
49131                     label.cls += ' col-sm-' + this.labelsm;
49132                     container.cls += ' col-sm-' + (12 - this.labelsm);
49133                 }
49134                 if(this.labelxs > 0){
49135                     label.cls += ' col-xs-' + this.labelxs;
49136                     container.cls += ' col-xs-' + (12 - this.labelxs);
49137                 }
49138             }
49139         }
49140
49141         cfg.cn = [
49142             label,
49143             container,
49144             hiddenInput
49145         ];
49146         
49147         var settings = this;
49148
49149         ['xs','sm','md','lg'].map(function(size){
49150             if (settings[size]) {
49151                 cfg.cls += ' col-' + size + '-' + settings[size];
49152             }
49153         });
49154         
49155         return cfg;
49156     },
49157     
49158     initEvents : function()
49159     {
49160         this.indicator = this.indicatorEl();
49161         
49162         this.initCurrencyEvent();
49163         
49164         this.initNumberEvent();
49165     },
49166     
49167     initCurrencyEvent : function()
49168     {
49169         if (!this.store) {
49170             throw "can not find store for combo";
49171         }
49172         
49173         this.store = Roo.factory(this.store, Roo.data);
49174         this.store.parent = this;
49175         
49176         this.createList();
49177         
49178         this.triggerEl = this.el.select('.input-group-addon', true).first();
49179         
49180         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49181         
49182         var _this = this;
49183         
49184         (function(){
49185             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49186             _this.list.setWidth(lw);
49187         }).defer(100);
49188         
49189         this.list.on('mouseover', this.onViewOver, this);
49190         this.list.on('mousemove', this.onViewMove, this);
49191         this.list.on('scroll', this.onViewScroll, this);
49192         
49193         if(!this.tpl){
49194             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49195         }
49196         
49197         this.view = new Roo.View(this.list, this.tpl, {
49198             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49199         });
49200         
49201         this.view.on('click', this.onViewClick, this);
49202         
49203         this.store.on('beforeload', this.onBeforeLoad, this);
49204         this.store.on('load', this.onLoad, this);
49205         this.store.on('loadexception', this.onLoadException, this);
49206         
49207         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49208             "up" : function(e){
49209                 this.inKeyMode = true;
49210                 this.selectPrev();
49211             },
49212
49213             "down" : function(e){
49214                 if(!this.isExpanded()){
49215                     this.onTriggerClick();
49216                 }else{
49217                     this.inKeyMode = true;
49218                     this.selectNext();
49219                 }
49220             },
49221
49222             "enter" : function(e){
49223                 this.collapse();
49224                 
49225                 if(this.fireEvent("specialkey", this, e)){
49226                     this.onViewClick(false);
49227                 }
49228                 
49229                 return true;
49230             },
49231
49232             "esc" : function(e){
49233                 this.collapse();
49234             },
49235
49236             "tab" : function(e){
49237                 this.collapse();
49238                 
49239                 if(this.fireEvent("specialkey", this, e)){
49240                     this.onViewClick(false);
49241                 }
49242                 
49243                 return true;
49244             },
49245
49246             scope : this,
49247
49248             doRelay : function(foo, bar, hname){
49249                 if(hname == 'down' || this.scope.isExpanded()){
49250                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49251                 }
49252                 return true;
49253             },
49254
49255             forceKeyDown: true
49256         });
49257         
49258         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49259         
49260     },
49261     
49262     initNumberEvent : function(e)
49263     {
49264         this.inputEl().on("keydown" , this.fireKey,  this);
49265         this.inputEl().on("focus", this.onFocus,  this);
49266         this.inputEl().on("blur", this.onBlur,  this);
49267         
49268         this.inputEl().relayEvent('keyup', this);
49269         
49270         if(this.indicator){
49271             this.indicator.addClass('invisible');
49272         }
49273  
49274         this.originalValue = this.getValue();
49275         
49276         if(this.validationEvent == 'keyup'){
49277             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49278             this.inputEl().on('keyup', this.filterValidation, this);
49279         }
49280         else if(this.validationEvent !== false){
49281             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49282         }
49283         
49284         if(this.selectOnFocus){
49285             this.on("focus", this.preFocus, this);
49286             
49287         }
49288         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49289             this.inputEl().on("keypress", this.filterKeys, this);
49290         } else {
49291             this.inputEl().relayEvent('keypress', this);
49292         }
49293         
49294         var allowed = "0123456789";
49295         
49296         if(this.allowDecimals){
49297             allowed += this.decimalSeparator;
49298         }
49299         
49300         if(this.allowNegative){
49301             allowed += "-";
49302         }
49303         
49304         if(this.thousandsDelimiter) {
49305             allowed += ",";
49306         }
49307         
49308         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49309         
49310         var keyPress = function(e){
49311             
49312             var k = e.getKey();
49313             
49314             var c = e.getCharCode();
49315             
49316             if(
49317                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49318                     allowed.indexOf(String.fromCharCode(c)) === -1
49319             ){
49320                 e.stopEvent();
49321                 return;
49322             }
49323             
49324             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49325                 return;
49326             }
49327             
49328             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49329                 e.stopEvent();
49330             }
49331         };
49332         
49333         this.inputEl().on("keypress", keyPress, this);
49334         
49335     },
49336     
49337     onTriggerClick : function(e)
49338     {   
49339         if(this.disabled){
49340             return;
49341         }
49342         
49343         this.page = 0;
49344         this.loadNext = false;
49345         
49346         if(this.isExpanded()){
49347             this.collapse();
49348             return;
49349         }
49350         
49351         this.hasFocus = true;
49352         
49353         if(this.triggerAction == 'all') {
49354             this.doQuery(this.allQuery, true);
49355             return;
49356         }
49357         
49358         this.doQuery(this.getRawValue());
49359     },
49360     
49361     getCurrency : function()
49362     {   
49363         var v = this.currencyEl().getValue();
49364         
49365         return v;
49366     },
49367     
49368     restrictHeight : function()
49369     {
49370         this.list.alignTo(this.currencyEl(), this.listAlign);
49371         this.list.alignTo(this.currencyEl(), this.listAlign);
49372     },
49373     
49374     onViewClick : function(view, doFocus, el, e)
49375     {
49376         var index = this.view.getSelectedIndexes()[0];
49377         
49378         var r = this.store.getAt(index);
49379         
49380         if(r){
49381             this.onSelect(r, index);
49382         }
49383     },
49384     
49385     onSelect : function(record, index){
49386         
49387         if(this.fireEvent('beforeselect', this, record, index) !== false){
49388         
49389             this.setFromCurrencyData(index > -1 ? record.data : false);
49390             
49391             this.collapse();
49392             
49393             this.fireEvent('select', this, record, index);
49394         }
49395     },
49396     
49397     setFromCurrencyData : function(o)
49398     {
49399         var currency = '';
49400         
49401         this.lastCurrency = o;
49402         
49403         if (this.currencyField) {
49404             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49405         } else {
49406             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49407         }
49408         
49409         this.lastSelectionText = currency;
49410         
49411         //setting default currency
49412         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49413             this.setCurrency(this.defaultCurrency);
49414             return;
49415         }
49416         
49417         this.setCurrency(currency);
49418     },
49419     
49420     setFromData : function(o)
49421     {
49422         var c = {};
49423         
49424         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49425         
49426         this.setFromCurrencyData(c);
49427         
49428         var value = '';
49429         
49430         if (this.name) {
49431             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49432         } else {
49433             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49434         }
49435         
49436         this.setValue(value);
49437         
49438     },
49439     
49440     setCurrency : function(v)
49441     {   
49442         this.currencyValue = v;
49443         
49444         if(this.rendered){
49445             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49446             this.validate();
49447         }
49448     },
49449     
49450     setValue : function(v)
49451     {
49452         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49453         
49454         this.value = v;
49455         
49456         if(this.rendered){
49457             
49458             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49459             
49460             this.inputEl().dom.value = (v == '') ? '' :
49461                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49462             
49463             if(!this.allowZero && v === '0') {
49464                 this.hiddenEl().dom.value = '';
49465                 this.inputEl().dom.value = '';
49466             }
49467             
49468             this.validate();
49469         }
49470     },
49471     
49472     getRawValue : function()
49473     {
49474         var v = this.inputEl().getValue();
49475         
49476         return v;
49477     },
49478     
49479     getValue : function()
49480     {
49481         return this.fixPrecision(this.parseValue(this.getRawValue()));
49482     },
49483     
49484     parseValue : function(value)
49485     {
49486         if(this.thousandsDelimiter) {
49487             value += "";
49488             r = new RegExp(",", "g");
49489             value = value.replace(r, "");
49490         }
49491         
49492         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49493         return isNaN(value) ? '' : value;
49494         
49495     },
49496     
49497     fixPrecision : function(value)
49498     {
49499         if(this.thousandsDelimiter) {
49500             value += "";
49501             r = new RegExp(",", "g");
49502             value = value.replace(r, "");
49503         }
49504         
49505         var nan = isNaN(value);
49506         
49507         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49508             return nan ? '' : value;
49509         }
49510         return parseFloat(value).toFixed(this.decimalPrecision);
49511     },
49512     
49513     decimalPrecisionFcn : function(v)
49514     {
49515         return Math.floor(v);
49516     },
49517     
49518     validateValue : function(value)
49519     {
49520         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49521             return false;
49522         }
49523         
49524         var num = this.parseValue(value);
49525         
49526         if(isNaN(num)){
49527             this.markInvalid(String.format(this.nanText, value));
49528             return false;
49529         }
49530         
49531         if(num < this.minValue){
49532             this.markInvalid(String.format(this.minText, this.minValue));
49533             return false;
49534         }
49535         
49536         if(num > this.maxValue){
49537             this.markInvalid(String.format(this.maxText, this.maxValue));
49538             return false;
49539         }
49540         
49541         return true;
49542     },
49543     
49544     validate : function()
49545     {
49546         if(this.disabled || this.allowBlank){
49547             this.markValid();
49548             return true;
49549         }
49550         
49551         var currency = this.getCurrency();
49552         
49553         if(this.validateValue(this.getRawValue()) && currency.length){
49554             this.markValid();
49555             return true;
49556         }
49557         
49558         this.markInvalid();
49559         return false;
49560     },
49561     
49562     getName: function()
49563     {
49564         return this.name;
49565     },
49566     
49567     beforeBlur : function()
49568     {
49569         if(!this.castInt){
49570             return;
49571         }
49572         
49573         var v = this.parseValue(this.getRawValue());
49574         
49575         if(v || v == 0){
49576             this.setValue(v);
49577         }
49578     },
49579     
49580     onBlur : function()
49581     {
49582         this.beforeBlur();
49583         
49584         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49585             //this.el.removeClass(this.focusClass);
49586         }
49587         
49588         this.hasFocus = false;
49589         
49590         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49591             this.validate();
49592         }
49593         
49594         var v = this.getValue();
49595         
49596         if(String(v) !== String(this.startValue)){
49597             this.fireEvent('change', this, v, this.startValue);
49598         }
49599         
49600         this.fireEvent("blur", this);
49601     },
49602     
49603     inputEl : function()
49604     {
49605         return this.el.select('.roo-money-amount-input', true).first();
49606     },
49607     
49608     currencyEl : function()
49609     {
49610         return this.el.select('.roo-money-currency-input', true).first();
49611     },
49612     
49613     hiddenEl : function()
49614     {
49615         return this.el.select('input.hidden-number-input',true).first();
49616     }
49617     
49618 });/**
49619  * @class Roo.bootstrap.BezierSignature
49620  * @extends Roo.bootstrap.Component
49621  * Bootstrap BezierSignature class
49622  * This script refer to:
49623  *    Title: Signature Pad
49624  *    Author: szimek
49625  *    Availability: https://github.com/szimek/signature_pad
49626  *
49627  * @constructor
49628  * Create a new BezierSignature
49629  * @param {Object} config The config object
49630  */
49631
49632 Roo.bootstrap.BezierSignature = function(config){
49633     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49634     this.addEvents({
49635         "resize" : true
49636     });
49637 };
49638
49639 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49640 {
49641      
49642     curve_data: [],
49643     
49644     is_empty: true,
49645     
49646     mouse_btn_down: true,
49647     
49648     /**
49649      * @cfg {int} canvas height
49650      */
49651     canvas_height: '200px',
49652     
49653     /**
49654      * @cfg {float|function} Radius of a single dot.
49655      */ 
49656     dot_size: false,
49657     
49658     /**
49659      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49660      */
49661     min_width: 0.5,
49662     
49663     /**
49664      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49665      */
49666     max_width: 2.5,
49667     
49668     /**
49669      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49670      */
49671     throttle: 16,
49672     
49673     /**
49674      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49675      */
49676     min_distance: 5,
49677     
49678     /**
49679      * @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.
49680      */
49681     bg_color: 'rgba(0, 0, 0, 0)',
49682     
49683     /**
49684      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49685      */
49686     dot_color: 'black',
49687     
49688     /**
49689      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49690      */ 
49691     velocity_filter_weight: 0.7,
49692     
49693     /**
49694      * @cfg {function} Callback when stroke begin. 
49695      */
49696     onBegin: false,
49697     
49698     /**
49699      * @cfg {function} Callback when stroke end.
49700      */
49701     onEnd: false,
49702     
49703     getAutoCreate : function()
49704     {
49705         var cls = 'roo-signature column';
49706         
49707         if(this.cls){
49708             cls += ' ' + this.cls;
49709         }
49710         
49711         var col_sizes = [
49712             'lg',
49713             'md',
49714             'sm',
49715             'xs'
49716         ];
49717         
49718         for(var i = 0; i < col_sizes.length; i++) {
49719             if(this[col_sizes[i]]) {
49720                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49721             }
49722         }
49723         
49724         var cfg = {
49725             tag: 'div',
49726             cls: cls,
49727             cn: [
49728                 {
49729                     tag: 'div',
49730                     cls: 'roo-signature-body',
49731                     cn: [
49732                         {
49733                             tag: 'canvas',
49734                             cls: 'roo-signature-body-canvas',
49735                             height: this.canvas_height,
49736                             width: this.canvas_width
49737                         }
49738                     ]
49739                 },
49740                 {
49741                     tag: 'input',
49742                     type: 'file',
49743                     style: 'display: none'
49744                 }
49745             ]
49746         };
49747         
49748         return cfg;
49749     },
49750     
49751     initEvents: function() 
49752     {
49753         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49754         
49755         var canvas = this.canvasEl();
49756         
49757         // mouse && touch event swapping...
49758         canvas.dom.style.touchAction = 'none';
49759         canvas.dom.style.msTouchAction = 'none';
49760         
49761         this.mouse_btn_down = false;
49762         canvas.on('mousedown', this._handleMouseDown, this);
49763         canvas.on('mousemove', this._handleMouseMove, this);
49764         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49765         
49766         if (window.PointerEvent) {
49767             canvas.on('pointerdown', this._handleMouseDown, this);
49768             canvas.on('pointermove', this._handleMouseMove, this);
49769             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49770         }
49771         
49772         if ('ontouchstart' in window) {
49773             canvas.on('touchstart', this._handleTouchStart, this);
49774             canvas.on('touchmove', this._handleTouchMove, this);
49775             canvas.on('touchend', this._handleTouchEnd, this);
49776         }
49777         
49778         Roo.EventManager.onWindowResize(this.resize, this, true);
49779         
49780         // file input event
49781         this.fileEl().on('change', this.uploadImage, this);
49782         
49783         this.clear();
49784         
49785         this.resize();
49786     },
49787     
49788     resize: function(){
49789         
49790         var canvas = this.canvasEl().dom;
49791         var ctx = this.canvasElCtx();
49792         var img_data = false;
49793         
49794         if(canvas.width > 0) {
49795             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49796         }
49797         // setting canvas width will clean img data
49798         canvas.width = 0;
49799         
49800         var style = window.getComputedStyle ? 
49801             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49802             
49803         var padding_left = parseInt(style.paddingLeft) || 0;
49804         var padding_right = parseInt(style.paddingRight) || 0;
49805         
49806         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49807         
49808         if(img_data) {
49809             ctx.putImageData(img_data, 0, 0);
49810         }
49811     },
49812     
49813     _handleMouseDown: function(e)
49814     {
49815         if (e.browserEvent.which === 1) {
49816             this.mouse_btn_down = true;
49817             this.strokeBegin(e);
49818         }
49819     },
49820     
49821     _handleMouseMove: function (e)
49822     {
49823         if (this.mouse_btn_down) {
49824             this.strokeMoveUpdate(e);
49825         }
49826     },
49827     
49828     _handleMouseUp: function (e)
49829     {
49830         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49831             this.mouse_btn_down = false;
49832             this.strokeEnd(e);
49833         }
49834     },
49835     
49836     _handleTouchStart: function (e) {
49837         
49838         e.preventDefault();
49839         if (e.browserEvent.targetTouches.length === 1) {
49840             // var touch = e.browserEvent.changedTouches[0];
49841             // this.strokeBegin(touch);
49842             
49843              this.strokeBegin(e); // assume e catching the correct xy...
49844         }
49845     },
49846     
49847     _handleTouchMove: function (e) {
49848         e.preventDefault();
49849         // var touch = event.targetTouches[0];
49850         // _this._strokeMoveUpdate(touch);
49851         this.strokeMoveUpdate(e);
49852     },
49853     
49854     _handleTouchEnd: function (e) {
49855         var wasCanvasTouched = e.target === this.canvasEl().dom;
49856         if (wasCanvasTouched) {
49857             e.preventDefault();
49858             // var touch = event.changedTouches[0];
49859             // _this._strokeEnd(touch);
49860             this.strokeEnd(e);
49861         }
49862     },
49863     
49864     reset: function () {
49865         this._lastPoints = [];
49866         this._lastVelocity = 0;
49867         this._lastWidth = (this.min_width + this.max_width) / 2;
49868         this.canvasElCtx().fillStyle = this.dot_color;
49869     },
49870     
49871     strokeMoveUpdate: function(e)
49872     {
49873         this.strokeUpdate(e);
49874         
49875         if (this.throttle) {
49876             this.throttleStroke(this.strokeUpdate, this.throttle);
49877         }
49878         else {
49879             this.strokeUpdate(e);
49880         }
49881     },
49882     
49883     strokeBegin: function(e)
49884     {
49885         var newPointGroup = {
49886             color: this.dot_color,
49887             points: []
49888         };
49889         
49890         if (typeof this.onBegin === 'function') {
49891             this.onBegin(e);
49892         }
49893         
49894         this.curve_data.push(newPointGroup);
49895         this.reset();
49896         this.strokeUpdate(e);
49897     },
49898     
49899     strokeUpdate: function(e)
49900     {
49901         var rect = this.canvasEl().dom.getBoundingClientRect();
49902         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49903         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49904         var lastPoints = lastPointGroup.points;
49905         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49906         var isLastPointTooClose = lastPoint
49907             ? point.distanceTo(lastPoint) <= this.min_distance
49908             : false;
49909         var color = lastPointGroup.color;
49910         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49911             var curve = this.addPoint(point);
49912             if (!lastPoint) {
49913                 this.drawDot({color: color, point: point});
49914             }
49915             else if (curve) {
49916                 this.drawCurve({color: color, curve: curve});
49917             }
49918             lastPoints.push({
49919                 time: point.time,
49920                 x: point.x,
49921                 y: point.y
49922             });
49923         }
49924     },
49925     
49926     strokeEnd: function(e)
49927     {
49928         this.strokeUpdate(e);
49929         if (typeof this.onEnd === 'function') {
49930             this.onEnd(e);
49931         }
49932     },
49933     
49934     addPoint:  function (point) {
49935         var _lastPoints = this._lastPoints;
49936         _lastPoints.push(point);
49937         if (_lastPoints.length > 2) {
49938             if (_lastPoints.length === 3) {
49939                 _lastPoints.unshift(_lastPoints[0]);
49940             }
49941             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49942             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49943             _lastPoints.shift();
49944             return curve;
49945         }
49946         return null;
49947     },
49948     
49949     calculateCurveWidths: function (startPoint, endPoint) {
49950         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49951             (1 - this.velocity_filter_weight) * this._lastVelocity;
49952
49953         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49954         var widths = {
49955             end: newWidth,
49956             start: this._lastWidth
49957         };
49958         
49959         this._lastVelocity = velocity;
49960         this._lastWidth = newWidth;
49961         return widths;
49962     },
49963     
49964     drawDot: function (_a) {
49965         var color = _a.color, point = _a.point;
49966         var ctx = this.canvasElCtx();
49967         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49968         ctx.beginPath();
49969         this.drawCurveSegment(point.x, point.y, width);
49970         ctx.closePath();
49971         ctx.fillStyle = color;
49972         ctx.fill();
49973     },
49974     
49975     drawCurve: function (_a) {
49976         var color = _a.color, curve = _a.curve;
49977         var ctx = this.canvasElCtx();
49978         var widthDelta = curve.endWidth - curve.startWidth;
49979         var drawSteps = Math.floor(curve.length()) * 2;
49980         ctx.beginPath();
49981         ctx.fillStyle = color;
49982         for (var i = 0; i < drawSteps; i += 1) {
49983         var t = i / drawSteps;
49984         var tt = t * t;
49985         var ttt = tt * t;
49986         var u = 1 - t;
49987         var uu = u * u;
49988         var uuu = uu * u;
49989         var x = uuu * curve.startPoint.x;
49990         x += 3 * uu * t * curve.control1.x;
49991         x += 3 * u * tt * curve.control2.x;
49992         x += ttt * curve.endPoint.x;
49993         var y = uuu * curve.startPoint.y;
49994         y += 3 * uu * t * curve.control1.y;
49995         y += 3 * u * tt * curve.control2.y;
49996         y += ttt * curve.endPoint.y;
49997         var width = curve.startWidth + ttt * widthDelta;
49998         this.drawCurveSegment(x, y, width);
49999         }
50000         ctx.closePath();
50001         ctx.fill();
50002     },
50003     
50004     drawCurveSegment: function (x, y, width) {
50005         var ctx = this.canvasElCtx();
50006         ctx.moveTo(x, y);
50007         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50008         this.is_empty = false;
50009     },
50010     
50011     clear: function()
50012     {
50013         var ctx = this.canvasElCtx();
50014         var canvas = this.canvasEl().dom;
50015         ctx.fillStyle = this.bg_color;
50016         ctx.clearRect(0, 0, canvas.width, canvas.height);
50017         ctx.fillRect(0, 0, canvas.width, canvas.height);
50018         this.curve_data = [];
50019         this.reset();
50020         this.is_empty = true;
50021     },
50022     
50023     fileEl: function()
50024     {
50025         return  this.el.select('input',true).first();
50026     },
50027     
50028     canvasEl: function()
50029     {
50030         return this.el.select('canvas',true).first();
50031     },
50032     
50033     canvasElCtx: function()
50034     {
50035         return this.el.select('canvas',true).first().dom.getContext('2d');
50036     },
50037     
50038     getImage: function(type)
50039     {
50040         if(this.is_empty) {
50041             return false;
50042         }
50043         
50044         // encryption ?
50045         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50046     },
50047     
50048     drawFromImage: function(img_src)
50049     {
50050         var img = new Image();
50051         
50052         img.onload = function(){
50053             this.canvasElCtx().drawImage(img, 0, 0);
50054         }.bind(this);
50055         
50056         img.src = img_src;
50057         
50058         this.is_empty = false;
50059     },
50060     
50061     selectImage: function()
50062     {
50063         this.fileEl().dom.click();
50064     },
50065     
50066     uploadImage: function(e)
50067     {
50068         var reader = new FileReader();
50069         
50070         reader.onload = function(e){
50071             var img = new Image();
50072             img.onload = function(){
50073                 this.reset();
50074                 this.canvasElCtx().drawImage(img, 0, 0);
50075             }.bind(this);
50076             img.src = e.target.result;
50077         }.bind(this);
50078         
50079         reader.readAsDataURL(e.target.files[0]);
50080     },
50081     
50082     // Bezier Point Constructor
50083     Point: (function () {
50084         function Point(x, y, time) {
50085             this.x = x;
50086             this.y = y;
50087             this.time = time || Date.now();
50088         }
50089         Point.prototype.distanceTo = function (start) {
50090             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50091         };
50092         Point.prototype.equals = function (other) {
50093             return this.x === other.x && this.y === other.y && this.time === other.time;
50094         };
50095         Point.prototype.velocityFrom = function (start) {
50096             return this.time !== start.time
50097             ? this.distanceTo(start) / (this.time - start.time)
50098             : 0;
50099         };
50100         return Point;
50101     }()),
50102     
50103     
50104     // Bezier Constructor
50105     Bezier: (function () {
50106         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50107             this.startPoint = startPoint;
50108             this.control2 = control2;
50109             this.control1 = control1;
50110             this.endPoint = endPoint;
50111             this.startWidth = startWidth;
50112             this.endWidth = endWidth;
50113         }
50114         Bezier.fromPoints = function (points, widths, scope) {
50115             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50116             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50117             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50118         };
50119         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50120             var dx1 = s1.x - s2.x;
50121             var dy1 = s1.y - s2.y;
50122             var dx2 = s2.x - s3.x;
50123             var dy2 = s2.y - s3.y;
50124             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50125             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50126             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50127             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50128             var dxm = m1.x - m2.x;
50129             var dym = m1.y - m2.y;
50130             var k = l2 / (l1 + l2);
50131             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50132             var tx = s2.x - cm.x;
50133             var ty = s2.y - cm.y;
50134             return {
50135                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50136                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50137             };
50138         };
50139         Bezier.prototype.length = function () {
50140             var steps = 10;
50141             var length = 0;
50142             var px;
50143             var py;
50144             for (var i = 0; i <= steps; i += 1) {
50145                 var t = i / steps;
50146                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50147                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50148                 if (i > 0) {
50149                     var xdiff = cx - px;
50150                     var ydiff = cy - py;
50151                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50152                 }
50153                 px = cx;
50154                 py = cy;
50155             }
50156             return length;
50157         };
50158         Bezier.prototype.point = function (t, start, c1, c2, end) {
50159             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50160             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50161             + (3.0 * c2 * (1.0 - t) * t * t)
50162             + (end * t * t * t);
50163         };
50164         return Bezier;
50165     }()),
50166     
50167     throttleStroke: function(fn, wait) {
50168       if (wait === void 0) { wait = 250; }
50169       var previous = 0;
50170       var timeout = null;
50171       var result;
50172       var storedContext;
50173       var storedArgs;
50174       var later = function () {
50175           previous = Date.now();
50176           timeout = null;
50177           result = fn.apply(storedContext, storedArgs);
50178           if (!timeout) {
50179               storedContext = null;
50180               storedArgs = [];
50181           }
50182       };
50183       return function wrapper() {
50184           var args = [];
50185           for (var _i = 0; _i < arguments.length; _i++) {
50186               args[_i] = arguments[_i];
50187           }
50188           var now = Date.now();
50189           var remaining = wait - (now - previous);
50190           storedContext = this;
50191           storedArgs = args;
50192           if (remaining <= 0 || remaining > wait) {
50193               if (timeout) {
50194                   clearTimeout(timeout);
50195                   timeout = null;
50196               }
50197               previous = now;
50198               result = fn.apply(storedContext, storedArgs);
50199               if (!timeout) {
50200                   storedContext = null;
50201                   storedArgs = [];
50202               }
50203           }
50204           else if (!timeout) {
50205               timeout = window.setTimeout(later, remaining);
50206           }
50207           return result;
50208       };
50209   }
50210   
50211 });
50212
50213  
50214
50215  // old names for form elements
50216 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50217 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50218 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50219 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50220 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50221 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50222 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50223 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50224 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50225 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50226 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50227 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50228 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50229 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50230 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50231 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50232 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50233 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50234 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50235 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50236 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50237 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50238 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50239 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50240 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50241 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50242
50243 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50244 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50245
50246 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50247 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50248
50249 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50250 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50251 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50252 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50253