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                 Roo.log(echild);
516                 Roo.log(cn);
517                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
518                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
519                   
520                   
521                   
522                     cn.el = echild;
523                     Roo.log(cn.el);
524                   //  Roo.log("GOT");
525                     //echild.dom.removeAttribute('xtype');
526                 } else {
527                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
528                     Roo.debug && Roo.log(self_cntr_el);
529                     Roo.debug && Roo.log(echild);
530                     Roo.debug && Roo.log(cn);
531                 }
532             }
533            
534             
535            
536             // if object has flexy:if - then it may or may not be rendered.
537             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
538                 // skip a flexy if element.
539                 Roo.debug && Roo.log('skipping render');
540                 Roo.debug && Roo.log(tree);
541                 if (!cn.el) {
542                     Roo.debug && Roo.log('skipping all children');
543                     skip_children = true;
544                 }
545                 
546              } else {
547                  
548                 // actually if flexy:foreach is found, we really want to create 
549                 // multiple copies here...
550                 //Roo.log('render');
551                 //Roo.log(this[cntr]());
552                 // some elements do not have render methods.. like the layouts...
553                 /*
554                 if(this[cntr](true) === false){
555                     cn.items = [];
556                     return cn;
557                 }
558                 */
559                 cn.render && cn.render(this[cntr](true));
560                 
561              }
562             // then add the element..
563         }
564          
565         // handle the kids..
566         
567         var nitems = [];
568         /*
569         if (typeof (tree.menu) != 'undefined') {
570             tree.menu.parentType = cn.xtype;
571             tree.menu.triggerEl = cn.el;
572             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
573             
574         }
575         */
576         if (!tree.items || !tree.items.length) {
577             cn.items = nitems;
578             //Roo.log(["no children", this]);
579             
580             return cn;
581         }
582          
583         var items = tree.items;
584         delete tree.items;
585         
586         //Roo.log(items.length);
587             // add the items..
588         if (!skip_children) {    
589             for(var i =0;i < items.length;i++) {
590               //  Roo.log(['add child', items[i]]);
591                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
592             }
593         }
594         
595         cn.items = nitems;
596         
597         //Roo.log("fire childrenrendered");
598         
599         cn.fireEvent('childrenrendered', this);
600         
601         return cn;
602     },
603     
604     /**
605      * Set the element that will be used to show or hide
606      */
607     setVisibilityEl : function(el)
608     {
609         this.visibilityEl = el;
610     },
611     
612      /**
613      * Get the element that will be used to show or hide
614      */
615     getVisibilityEl : function()
616     {
617         if (typeof(this.visibilityEl) == 'object') {
618             return this.visibilityEl;
619         }
620         
621         if (typeof(this.visibilityEl) == 'string') {
622             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
623         }
624         
625         return this.getEl();
626     },
627     
628     /**
629      * Show a component - removes 'hidden' class
630      */
631     show : function()
632     {
633         if(!this.getVisibilityEl()){
634             return;
635         }
636          
637         this.getVisibilityEl().removeClass(['hidden','d-none']);
638         
639         this.fireEvent('show', this);
640         
641         
642     },
643     /**
644      * Hide a component - adds 'hidden' class
645      */
646     hide: function()
647     {
648         if(!this.getVisibilityEl()){
649             return;
650         }
651         
652         this.getVisibilityEl().addClass(['hidden','d-none']);
653         
654         this.fireEvent('hide', this);
655         
656     }
657 });
658
659  /*
660  * - LGPL
661  *
662  * element
663  * 
664  */
665
666 /**
667  * @class Roo.bootstrap.Element
668  * @extends Roo.bootstrap.Component
669  * @children Roo.bootstrap.Component
670  * Bootstrap Element class (basically a DIV used to make random stuff )
671  * 
672  * @cfg {String} html contents of the element
673  * @cfg {String} tag tag of the element
674  * @cfg {String} cls class of the element
675  * @cfg {Boolean} preventDefault (true|false) default false
676  * @cfg {Boolean} clickable (true|false) default false
677  * @cfg {String} role default blank - set to button to force cursor pointer
678  
679  * 
680  * @constructor
681  * Create a new Element
682  * @param {Object} config The config object
683  */
684
685 Roo.bootstrap.Element = function(config){
686     Roo.bootstrap.Element.superclass.constructor.call(this, config);
687     
688     this.addEvents({
689         // raw events
690         /**
691          * @event click
692          * When a element is chick
693          * @param {Roo.bootstrap.Element} this
694          * @param {Roo.EventObject} e
695          */
696         "click" : true 
697         
698       
699     });
700 };
701
702 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
703     
704     tag: 'div',
705     cls: '',
706     html: '',
707     preventDefault: false, 
708     clickable: false,
709     tapedTwice : false,
710     role : false,
711     
712     getAutoCreate : function(){
713         
714         var cfg = {
715             tag: this.tag,
716             // cls: this.cls, double assign in parent class Component.js :: onRender
717             html: this.html
718         };
719         if (this.role !== false) {
720             cfg.role = this.role;
721         }
722         
723         return cfg;
724     },
725     
726     initEvents: function() 
727     {
728         Roo.bootstrap.Element.superclass.initEvents.call(this);
729         
730         if(this.clickable){
731             this.el.on('click', this.onClick, this);
732         }
733         
734         
735     },
736     
737     onClick : function(e)
738     {
739         if(this.preventDefault){
740             e.preventDefault();
741         }
742         
743         this.fireEvent('click', this, e); // why was this double click before?
744     },
745     
746     
747     
748
749     
750     
751     getValue : function()
752     {
753         return this.el.dom.innerHTML;
754     },
755     
756     setValue : function(value)
757     {
758         this.el.dom.innerHTML = value;
759     }
760    
761 });
762
763  
764
765  /*
766  * - LGPL
767  *
768  * dropable area
769  * 
770  */
771
772 /**
773  * @class Roo.bootstrap.DropTarget
774  * @extends Roo.bootstrap.Element
775  * Bootstrap DropTarget class
776  
777  * @cfg {string} name dropable name
778  * 
779  * @constructor
780  * Create a new Dropable Area
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.DropTarget = function(config){
785     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
786     
787     this.addEvents({
788         // raw events
789         /**
790          * @event click
791          * When a element is chick
792          * @param {Roo.bootstrap.Element} this
793          * @param {Roo.EventObject} e
794          */
795         "drop" : true
796     });
797 };
798
799 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
800     
801     
802     getAutoCreate : function(){
803         
804          
805     },
806     
807     initEvents: function() 
808     {
809         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
810         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
811             ddGroup: this.name,
812             listeners : {
813                 drop : this.dragDrop.createDelegate(this),
814                 enter : this.dragEnter.createDelegate(this),
815                 out : this.dragOut.createDelegate(this),
816                 over : this.dragOver.createDelegate(this)
817             }
818             
819         });
820         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
821     },
822     
823     dragDrop : function(source,e,data)
824     {
825         // user has to decide how to impliment this.
826         Roo.log('drop');
827         Roo.log(this);
828         //this.fireEvent('drop', this, source, e ,data);
829         return false;
830     },
831     
832     dragEnter : function(n, dd, e, data)
833     {
834         // probably want to resize the element to match the dropped element..
835         Roo.log("enter");
836         this.originalSize = this.el.getSize();
837         this.el.setSize( n.el.getSize());
838         this.dropZone.DDM.refreshCache(this.name);
839         Roo.log([n, dd, e, data]);
840     },
841     
842     dragOut : function(value)
843     {
844         // resize back to normal
845         Roo.log("out");
846         this.el.setSize(this.originalSize);
847         this.dropZone.resetConstraints();
848     },
849     
850     dragOver : function()
851     {
852         // ??? do nothing?
853     }
854    
855 });
856
857  
858
859  /*
860  * - LGPL
861  *
862  * Body
863  *
864  */
865
866 /**
867  * @class Roo.bootstrap.Body
868  * @extends Roo.bootstrap.Component
869  * @children Roo.bootstrap.Component 
870  * @parent none builder
871  * Bootstrap Body class
872  *
873  * @constructor
874  * Create a new body
875  * @param {Object} config The config object
876  */
877
878 Roo.bootstrap.Body = function(config){
879
880     config = config || {};
881
882     Roo.bootstrap.Body.superclass.constructor.call(this, config);
883     this.el = Roo.get(config.el ? config.el : document.body );
884     if (this.cls && this.cls.length) {
885         Roo.get(document.body).addClass(this.cls);
886     }
887 };
888
889 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
890
891     is_body : true,// just to make sure it's constructed?
892
893         autoCreate : {
894         cls: 'container'
895     },
896     onRender : function(ct, position)
897     {
898        /* Roo.log("Roo.bootstrap.Body - onRender");
899         if (this.cls && this.cls.length) {
900             Roo.get(document.body).addClass(this.cls);
901         }
902         // style??? xttr???
903         */
904     }
905
906
907
908
909 });
910 /*
911  * - LGPL
912  *
913  * button group
914  * 
915  */
916
917
918 /**
919  * @class Roo.bootstrap.ButtonGroup
920  * @extends Roo.bootstrap.Component
921  * Bootstrap ButtonGroup class
922  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
923  * 
924  * @cfg {String} size lg | sm | xs (default empty normal)
925  * @cfg {String} align vertical | justified  (default none)
926  * @cfg {String} direction up | down (default down)
927  * @cfg {Boolean} toolbar false | true
928  * @cfg {Boolean} btn true | false
929  * 
930  * 
931  * @constructor
932  * Create a new Input
933  * @param {Object} config The config object
934  */
935
936 Roo.bootstrap.ButtonGroup = function(config){
937     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
938 };
939
940 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
941     
942     size: '',
943     align: '',
944     direction: '',
945     toolbar: false,
946     btn: true,
947
948     getAutoCreate : function(){
949         var cfg = {
950             cls: 'btn-group',
951             html : null
952         };
953         
954         cfg.html = this.html || cfg.html;
955         
956         if (this.toolbar) {
957             cfg = {
958                 cls: 'btn-toolbar',
959                 html: null
960             };
961             
962             return cfg;
963         }
964         
965         if (['vertical','justified'].indexOf(this.align)!==-1) {
966             cfg.cls = 'btn-group-' + this.align;
967             
968             if (this.align == 'justified') {
969                 console.log(this.items);
970             }
971         }
972         
973         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
974             cfg.cls += ' btn-group-' + this.size;
975         }
976         
977         if (this.direction == 'up') {
978             cfg.cls += ' dropup' ;
979         }
980         
981         return cfg;
982     },
983     /**
984      * Add a button to the group (similar to NavItem API.)
985      */
986     addItem : function(cfg)
987     {
988         var cn = new Roo.bootstrap.Button(cfg);
989         //this.register(cn);
990         cn.parentId = this.id;
991         cn.onRender(this.el, null);
992         return cn;
993     }
994    
995 });
996
997  /*
998  * - LGPL
999  *
1000  * button
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Button
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Button class
1008  * @cfg {String} html The button content
1009  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1010  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1011  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1012  * @cfg {String} size (lg|sm|xs)
1013  * @cfg {String} tag (a|input|submit)
1014  * @cfg {String} href empty or href
1015  * @cfg {Boolean} disabled default false;
1016  * @cfg {Boolean} isClose default false;
1017  * @cfg {String} glyphicon depricated - use fa
1018  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1019  * @cfg {String} badge text for badge
1020  * @cfg {String} theme (default|glow)  
1021  * @cfg {Boolean} inverse dark themed version
1022  * @cfg {Boolean} toggle is it a slidy toggle button
1023  * @cfg {Boolean} pressed   default null - if the button ahs active state
1024  * @cfg {String} ontext text for on slidy toggle state
1025  * @cfg {String} offtext text for off slidy toggle state
1026  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1027  * @cfg {Boolean} removeClass remove the standard class..
1028  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1029  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1030  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1031
1032  * @constructor
1033  * Create a new button
1034  * @param {Object} config The config object
1035  */
1036
1037
1038 Roo.bootstrap.Button = function(config){
1039     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1040     
1041     this.addEvents({
1042         // raw events
1043         /**
1044          * @event click
1045          * When a button is pressed
1046          * @param {Roo.bootstrap.Button} btn
1047          * @param {Roo.EventObject} e
1048          */
1049         "click" : true,
1050         /**
1051          * @event dblclick
1052          * When a button is double clicked
1053          * @param {Roo.bootstrap.Button} btn
1054          * @param {Roo.EventObject} e
1055          */
1056         "dblclick" : true,
1057          /**
1058          * @event toggle
1059          * After the button has been toggles
1060          * @param {Roo.bootstrap.Button} btn
1061          * @param {Roo.EventObject} e
1062          * @param {boolean} pressed (also available as button.pressed)
1063          */
1064         "toggle" : true
1065     });
1066 };
1067
1068 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1069     html: false,
1070     active: false,
1071     weight: '',
1072     badge_weight: '',
1073     outline : false,
1074     size: '',
1075     tag: 'button',
1076     href: '',
1077     disabled: false,
1078     isClose: false,
1079     glyphicon: '',
1080     fa: '',
1081     badge: '',
1082     theme: 'default',
1083     inverse: false,
1084     
1085     toggle: false,
1086     ontext: 'ON',
1087     offtext: 'OFF',
1088     defaulton: true,
1089     preventDefault: true,
1090     removeClass: false,
1091     name: false,
1092     target: false,
1093     group : false,
1094      
1095     pressed : null,
1096      
1097     
1098     getAutoCreate : function(){
1099         
1100         var cfg = {
1101             tag : 'button',
1102             cls : 'roo-button',
1103             html: ''
1104         };
1105         
1106         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1107             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1108             this.tag = 'button';
1109         } else {
1110             cfg.tag = this.tag;
1111         }
1112         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1113         
1114         if (this.toggle == true) {
1115             cfg={
1116                 tag: 'div',
1117                 cls: 'slider-frame roo-button',
1118                 cn: [
1119                     {
1120                         tag: 'span',
1121                         'data-on-text':'ON',
1122                         'data-off-text':'OFF',
1123                         cls: 'slider-button',
1124                         html: this.offtext
1125                     }
1126                 ]
1127             };
1128             // why are we validating the weights?
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 cfg.cls +=  ' ' + this.weight;
1131             }
1132             
1133             return cfg;
1134         }
1135         
1136         if (this.isClose) {
1137             cfg.cls += ' close';
1138             
1139             cfg["aria-hidden"] = true;
1140             
1141             cfg.html = "&times;";
1142             
1143             return cfg;
1144         }
1145              
1146         
1147         if (this.theme==='default') {
1148             cfg.cls = 'btn roo-button';
1149             
1150             //if (this.parentType != 'Navbar') {
1151             this.weight = this.weight.length ?  this.weight : 'default';
1152             //}
1153             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1154                 
1155                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1156                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1157                 cfg.cls += ' btn-' + outline + weight;
1158                 if (this.weight == 'default') {
1159                     // BC
1160                     cfg.cls += ' btn-' + this.weight;
1161                 }
1162             }
1163         } else if (this.theme==='glow') {
1164             
1165             cfg.tag = 'a';
1166             cfg.cls = 'btn-glow roo-button';
1167             
1168             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1169                 
1170                 cfg.cls += ' ' + this.weight;
1171             }
1172         }
1173    
1174         
1175         if (this.inverse) {
1176             this.cls += ' inverse';
1177         }
1178         
1179         
1180         if (this.active || this.pressed === true) {
1181             cfg.cls += ' active';
1182         }
1183         
1184         if (this.disabled) {
1185             cfg.disabled = 'disabled';
1186         }
1187         
1188         if (this.items) {
1189             Roo.log('changing to ul' );
1190             cfg.tag = 'ul';
1191             this.glyphicon = 'caret';
1192             if (Roo.bootstrap.version == 4) {
1193                 this.fa = 'caret-down';
1194             }
1195             
1196         }
1197         
1198         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1199          
1200         //gsRoo.log(this.parentType);
1201         if (this.parentType === 'Navbar' && !this.parent().bar) {
1202             Roo.log('changing to li?');
1203             
1204             cfg.tag = 'li';
1205             
1206             cfg.cls = '';
1207             cfg.cn =  [{
1208                 tag : 'a',
1209                 cls : 'roo-button',
1210                 html : this.html,
1211                 href : this.href || '#'
1212             }];
1213             if (this.menu) {
1214                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1215                 cfg.cls += ' dropdown';
1216             }   
1217             
1218             delete cfg.html;
1219             
1220         }
1221         
1222        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1223         
1224         if (this.glyphicon) {
1225             cfg.html = ' ' + cfg.html;
1226             
1227             cfg.cn = [
1228                 {
1229                     tag: 'span',
1230                     cls: 'glyphicon glyphicon-' + this.glyphicon
1231                 }
1232             ];
1233         }
1234         if (this.fa) {
1235             cfg.html = ' ' + cfg.html;
1236             
1237             cfg.cn = [
1238                 {
1239                     tag: 'i',
1240                     cls: 'fa fas fa-' + this.fa
1241                 }
1242             ];
1243         }
1244         
1245         if (this.badge) {
1246             cfg.html += ' ';
1247             
1248             cfg.tag = 'a';
1249             
1250 //            cfg.cls='btn roo-button';
1251             
1252             cfg.href=this.href;
1253             
1254             var value = cfg.html;
1255             
1256             if(this.glyphicon){
1257                 value = {
1258                     tag: 'span',
1259                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1260                     html: this.html
1261                 };
1262             }
1263             if(this.fa){
1264                 value = {
1265                     tag: 'i',
1266                     cls: 'fa fas fa-' + this.fa,
1267                     html: this.html
1268                 };
1269             }
1270             
1271             var bw = this.badge_weight.length ? this.badge_weight :
1272                 (this.weight.length ? this.weight : 'secondary');
1273             bw = bw == 'default' ? 'secondary' : bw;
1274             
1275             cfg.cn = [
1276                 value,
1277                 {
1278                     tag: 'span',
1279                     cls: 'badge badge-' + bw,
1280                     html: this.badge
1281                 }
1282             ];
1283             
1284             cfg.html='';
1285         }
1286         
1287         if (this.menu) {
1288             cfg.cls += ' dropdown';
1289             cfg.html = typeof(cfg.html) != 'undefined' ?
1290                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1291         }
1292         
1293         if (cfg.tag !== 'a' && this.href !== '') {
1294             throw "Tag must be a to set href.";
1295         } else if (this.href.length > 0) {
1296             cfg.href = this.href;
1297         }
1298         
1299         if(this.removeClass){
1300             cfg.cls = '';
1301         }
1302         
1303         if(this.target){
1304             cfg.target = this.target;
1305         }
1306         
1307         return cfg;
1308     },
1309     initEvents: function() {
1310        // Roo.log('init events?');
1311 //        Roo.log(this.el.dom);
1312         // add the menu...
1313         
1314         if (typeof (this.menu) != 'undefined') {
1315             this.menu.parentType = this.xtype;
1316             this.menu.triggerEl = this.el;
1317             this.addxtype(Roo.apply({}, this.menu));
1318         }
1319
1320
1321         if (this.el.hasClass('roo-button')) {
1322              this.el.on('click', this.onClick, this);
1323              this.el.on('dblclick', this.onDblClick, this);
1324         } else {
1325              this.el.select('.roo-button').on('click', this.onClick, this);
1326              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1327              
1328         }
1329         // why?
1330         if(this.removeClass){
1331             this.el.on('click', this.onClick, this);
1332         }
1333         
1334         if (this.group === true) {
1335              if (this.pressed === false || this.pressed === true) {
1336                 // nothing
1337             } else {
1338                 this.pressed = false;
1339                 this.setActive(this.pressed);
1340             }
1341             
1342         }
1343         
1344         this.el.enableDisplayMode();
1345         
1346     },
1347     onClick : function(e)
1348     {
1349         if (this.disabled) {
1350             return;
1351         }
1352         
1353         Roo.log('button on click ');
1354         if(this.href === '' || this.preventDefault){
1355             e.preventDefault();
1356         }
1357         
1358         if (this.group) {
1359             if (this.pressed) {
1360                 // do nothing -
1361                 return;
1362             }
1363             this.setActive(true);
1364             var pi = this.parent().items;
1365             for (var i = 0;i < pi.length;i++) {
1366                 if (this == pi[i]) {
1367                     continue;
1368                 }
1369                 if (pi[i].el.hasClass('roo-button')) {
1370                     pi[i].setActive(false);
1371                 }
1372             }
1373             this.fireEvent('click', this, e);            
1374             return;
1375         }
1376         
1377         if (this.pressed === true || this.pressed === false) {
1378             this.toggleActive(e);
1379         }
1380         
1381         
1382         this.fireEvent('click', this, e);
1383     },
1384     onDblClick: function(e)
1385     {
1386         if (this.disabled) {
1387             return;
1388         }
1389         if(this.preventDefault){
1390             e.preventDefault();
1391         }
1392         this.fireEvent('dblclick', this, e);
1393     },
1394     /**
1395      * Enables this button
1396      */
1397     enable : function()
1398     {
1399         this.disabled = false;
1400         this.el.removeClass('disabled');
1401         this.el.dom.removeAttribute("disabled");
1402     },
1403     
1404     /**
1405      * Disable this button
1406      */
1407     disable : function()
1408     {
1409         this.disabled = true;
1410         this.el.addClass('disabled');
1411         this.el.attr("disabled", "disabled")
1412     },
1413      /**
1414      * sets the active state on/off, 
1415      * @param {Boolean} state (optional) Force a particular state
1416      */
1417     setActive : function(v) {
1418         
1419         this.el[v ? 'addClass' : 'removeClass']('active');
1420         this.pressed = v;
1421     },
1422      /**
1423      * toggles the current active state 
1424      */
1425     toggleActive : function(e)
1426     {
1427         this.setActive(!this.pressed); // this modifies pressed...
1428         this.fireEvent('toggle', this, e, this.pressed);
1429     },
1430      /**
1431      * get the current active state
1432      * @return {boolean} true if it's active
1433      */
1434     isActive : function()
1435     {
1436         return this.el.hasClass('active');
1437     },
1438     /**
1439      * set the text of the first selected button
1440      */
1441     setText : function(str)
1442     {
1443         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1444     },
1445     /**
1446      * get the text of the first selected button
1447      */
1448     getText : function()
1449     {
1450         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1451     },
1452     
1453     setWeight : function(str)
1454     {
1455         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1456         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1457         this.weight = str;
1458         var outline = this.outline ? 'outline-' : '';
1459         if (str == 'default') {
1460             this.el.addClass('btn-default btn-outline-secondary');        
1461             return;
1462         }
1463         this.el.addClass('btn-' + outline + str);        
1464     }
1465     
1466     
1467 });
1468 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1469
1470 Roo.bootstrap.Button.weights = [
1471     'default',
1472     'secondary' ,
1473     'primary',
1474     'success',
1475     'info',
1476     'warning',
1477     'danger',
1478     'link',
1479     'light',
1480     'dark'              
1481    
1482 ];/*
1483  * - LGPL
1484  *
1485  * column
1486  * 
1487  */
1488
1489 /**
1490  * @class Roo.bootstrap.Column
1491  * @extends Roo.bootstrap.Component
1492  * @children Roo.bootstrap.Component
1493  * Bootstrap Column class
1494  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1495  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1496  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1497  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1498  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1499  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1500  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1501  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1502  *
1503  * 
1504  * @cfg {Boolean} hidden (true|false) hide the element
1505  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1506  * @cfg {String} fa (ban|check|...) font awesome icon
1507  * @cfg {Number} fasize (1|2|....) font awsome size
1508
1509  * @cfg {String} icon (info-sign|check|...) glyphicon name
1510
1511  * @cfg {String} html content of column.
1512  * 
1513  * @constructor
1514  * Create a new Column
1515  * @param {Object} config The config object
1516  */
1517
1518 Roo.bootstrap.Column = function(config){
1519     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1520 };
1521
1522 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1523     
1524     xs: false,
1525     sm: false,
1526     md: false,
1527     lg: false,
1528     xsoff: false,
1529     smoff: false,
1530     mdoff: false,
1531     lgoff: false,
1532     html: '',
1533     offset: 0,
1534     alert: false,
1535     fa: false,
1536     icon : false,
1537     hidden : false,
1538     fasize : 1,
1539     
1540     getAutoCreate : function(){
1541         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1542         
1543         cfg = {
1544             tag: 'div',
1545             cls: 'column'
1546         };
1547         
1548         var settings=this;
1549         var sizes =   ['xs','sm','md','lg'];
1550         sizes.map(function(size ,ix){
1551             //Roo.log( size + ':' + settings[size]);
1552             
1553             if (settings[size+'off'] !== false) {
1554                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1555             }
1556             
1557             if (settings[size] === false) {
1558                 return;
1559             }
1560             
1561             if (!settings[size]) { // 0 = hidden
1562                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1563                 // bootsrap4
1564                 for (var i = ix; i > -1; i--) {
1565                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1566                 }
1567                 
1568                 
1569                 return;
1570             }
1571             cfg.cls += ' col-' + size + '-' + settings[size] + (
1572                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1573             );
1574             
1575         });
1576         
1577         if (this.hidden) {
1578             cfg.cls += ' hidden';
1579         }
1580         
1581         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1582             cfg.cls +=' alert alert-' + this.alert;
1583         }
1584         
1585         
1586         if (this.html.length) {
1587             cfg.html = this.html;
1588         }
1589         if (this.fa) {
1590             var fasize = '';
1591             if (this.fasize > 1) {
1592                 fasize = ' fa-' + this.fasize + 'x';
1593             }
1594             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1595             
1596             
1597         }
1598         if (this.icon) {
1599             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1600         }
1601         
1602         return cfg;
1603     }
1604    
1605 });
1606
1607  
1608
1609  /*
1610  * - LGPL
1611  *
1612  * page container.
1613  * 
1614  */
1615
1616
1617 /**
1618  * @class Roo.bootstrap.Container
1619  * @extends Roo.bootstrap.Component
1620  * @children Roo.bootstrap.Component
1621  * @parent builder
1622  * Bootstrap Container class
1623  * @cfg {Boolean} jumbotron is it a jumbotron element
1624  * @cfg {String} html content of element
1625  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1626  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1627  * @cfg {String} header content of header (for panel)
1628  * @cfg {String} footer content of footer (for panel)
1629  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1630  * @cfg {String} tag (header|aside|section) type of HTML tag.
1631  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1632  * @cfg {String} fa font awesome icon
1633  * @cfg {String} icon (info-sign|check|...) glyphicon name
1634  * @cfg {Boolean} hidden (true|false) hide the element
1635  * @cfg {Boolean} expandable (true|false) default false
1636  * @cfg {Boolean} expanded (true|false) default true
1637  * @cfg {String} rheader contet on the right of header
1638  * @cfg {Boolean} clickable (true|false) default false
1639
1640  *     
1641  * @constructor
1642  * Create a new Container
1643  * @param {Object} config The config object
1644  */
1645
1646 Roo.bootstrap.Container = function(config){
1647     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1648     
1649     this.addEvents({
1650         // raw events
1651          /**
1652          * @event expand
1653          * After the panel has been expand
1654          * 
1655          * @param {Roo.bootstrap.Container} this
1656          */
1657         "expand" : true,
1658         /**
1659          * @event collapse
1660          * After the panel has been collapsed
1661          * 
1662          * @param {Roo.bootstrap.Container} this
1663          */
1664         "collapse" : true,
1665         /**
1666          * @event click
1667          * When a element is chick
1668          * @param {Roo.bootstrap.Container} this
1669          * @param {Roo.EventObject} e
1670          */
1671         "click" : true
1672     });
1673 };
1674
1675 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1676     
1677     jumbotron : false,
1678     well: '',
1679     panel : '',
1680     header: '',
1681     footer : '',
1682     sticky: '',
1683     tag : false,
1684     alert : false,
1685     fa: false,
1686     icon : false,
1687     expandable : false,
1688     rheader : '',
1689     expanded : true,
1690     clickable: false,
1691   
1692      
1693     getChildContainer : function() {
1694         
1695         if(!this.el){
1696             return false;
1697         }
1698         
1699         if (this.panel.length) {
1700             return this.el.select('.panel-body',true).first();
1701         }
1702         
1703         return this.el;
1704     },
1705     
1706     
1707     getAutoCreate : function(){
1708         
1709         var cfg = {
1710             tag : this.tag || 'div',
1711             html : '',
1712             cls : ''
1713         };
1714         if (this.jumbotron) {
1715             cfg.cls = 'jumbotron';
1716         }
1717         
1718         
1719         
1720         // - this is applied by the parent..
1721         //if (this.cls) {
1722         //    cfg.cls = this.cls + '';
1723         //}
1724         
1725         if (this.sticky.length) {
1726             
1727             var bd = Roo.get(document.body);
1728             if (!bd.hasClass('bootstrap-sticky')) {
1729                 bd.addClass('bootstrap-sticky');
1730                 Roo.select('html',true).setStyle('height', '100%');
1731             }
1732              
1733             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1734         }
1735         
1736         
1737         if (this.well.length) {
1738             switch (this.well) {
1739                 case 'lg':
1740                 case 'sm':
1741                     cfg.cls +=' well well-' +this.well;
1742                     break;
1743                 default:
1744                     cfg.cls +=' well';
1745                     break;
1746             }
1747         }
1748         
1749         if (this.hidden) {
1750             cfg.cls += ' hidden';
1751         }
1752         
1753         
1754         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1755             cfg.cls +=' alert alert-' + this.alert;
1756         }
1757         
1758         var body = cfg;
1759         
1760         if (this.panel.length) {
1761             cfg.cls += ' panel panel-' + this.panel;
1762             cfg.cn = [];
1763             if (this.header.length) {
1764                 
1765                 var h = [];
1766                 
1767                 if(this.expandable){
1768                     
1769                     cfg.cls = cfg.cls + ' expandable';
1770                     
1771                     h.push({
1772                         tag: 'i',
1773                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1774                     });
1775                     
1776                 }
1777                 
1778                 h.push(
1779                     {
1780                         tag: 'span',
1781                         cls : 'panel-title',
1782                         html : (this.expandable ? '&nbsp;' : '') + this.header
1783                     },
1784                     {
1785                         tag: 'span',
1786                         cls: 'panel-header-right',
1787                         html: this.rheader
1788                     }
1789                 );
1790                 
1791                 cfg.cn.push({
1792                     cls : 'panel-heading',
1793                     style : this.expandable ? 'cursor: pointer' : '',
1794                     cn : h
1795                 });
1796                 
1797             }
1798             
1799             body = false;
1800             cfg.cn.push({
1801                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1802                 html : this.html
1803             });
1804             
1805             
1806             if (this.footer.length) {
1807                 cfg.cn.push({
1808                     cls : 'panel-footer',
1809                     html : this.footer
1810                     
1811                 });
1812             }
1813             
1814         }
1815         
1816         if (body) {
1817             body.html = this.html || cfg.html;
1818             // prefix with the icons..
1819             if (this.fa) {
1820                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1821             }
1822             if (this.icon) {
1823                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1824             }
1825             
1826             
1827         }
1828         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1829             cfg.cls =  'container';
1830         }
1831         
1832         return cfg;
1833     },
1834     
1835     initEvents: function() 
1836     {
1837         if(this.expandable){
1838             var headerEl = this.headerEl();
1839         
1840             if(headerEl){
1841                 headerEl.on('click', this.onToggleClick, this);
1842             }
1843         }
1844         
1845         if(this.clickable){
1846             this.el.on('click', this.onClick, this);
1847         }
1848         
1849     },
1850     
1851     onToggleClick : function()
1852     {
1853         var headerEl = this.headerEl();
1854         
1855         if(!headerEl){
1856             return;
1857         }
1858         
1859         if(this.expanded){
1860             this.collapse();
1861             return;
1862         }
1863         
1864         this.expand();
1865     },
1866     
1867     expand : function()
1868     {
1869         if(this.fireEvent('expand', this)) {
1870             
1871             this.expanded = true;
1872             
1873             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1874             
1875             this.el.select('.panel-body',true).first().removeClass('hide');
1876             
1877             var toggleEl = this.toggleEl();
1878
1879             if(!toggleEl){
1880                 return;
1881             }
1882
1883             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1884         }
1885         
1886     },
1887     
1888     collapse : function()
1889     {
1890         if(this.fireEvent('collapse', this)) {
1891             
1892             this.expanded = false;
1893             
1894             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1895             this.el.select('.panel-body',true).first().addClass('hide');
1896         
1897             var toggleEl = this.toggleEl();
1898
1899             if(!toggleEl){
1900                 return;
1901             }
1902
1903             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1904         }
1905     },
1906     
1907     toggleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-heading .fa',true).first();
1914     },
1915     
1916     headerEl : function()
1917     {
1918         if(!this.el || !this.panel.length || !this.header.length){
1919             return;
1920         }
1921         
1922         return this.el.select('.panel-heading',true).first()
1923     },
1924     
1925     bodyEl : function()
1926     {
1927         if(!this.el || !this.panel.length){
1928             return;
1929         }
1930         
1931         return this.el.select('.panel-body',true).first()
1932     },
1933     
1934     titleEl : function()
1935     {
1936         if(!this.el || !this.panel.length || !this.header.length){
1937             return;
1938         }
1939         
1940         return this.el.select('.panel-title',true).first();
1941     },
1942     
1943     setTitle : function(v)
1944     {
1945         var titleEl = this.titleEl();
1946         
1947         if(!titleEl){
1948             return;
1949         }
1950         
1951         titleEl.dom.innerHTML = v;
1952     },
1953     
1954     getTitle : function()
1955     {
1956         
1957         var titleEl = this.titleEl();
1958         
1959         if(!titleEl){
1960             return '';
1961         }
1962         
1963         return titleEl.dom.innerHTML;
1964     },
1965     
1966     setRightTitle : function(v)
1967     {
1968         var t = this.el.select('.panel-header-right',true).first();
1969         
1970         if(!t){
1971             return;
1972         }
1973         
1974         t.dom.innerHTML = v;
1975     },
1976     
1977     onClick : function(e)
1978     {
1979         e.preventDefault();
1980         
1981         this.fireEvent('click', this, e);
1982     }
1983 });
1984
1985  /**
1986  * @class Roo.bootstrap.Card
1987  * @extends Roo.bootstrap.Component
1988  * @children Roo.bootstrap.Component
1989  * @licence LGPL
1990  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1991  *
1992  *
1993  * possible... may not be implemented..
1994  * @cfg {String} header_image  src url of image.
1995  * @cfg {String|Object} header
1996  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1997  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1998  * 
1999  * @cfg {String} title
2000  * @cfg {String} subtitle
2001  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
2002  * @cfg {String} footer
2003  
2004  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2005  * 
2006  * @cfg {String} margin (0|1|2|3|4|5|auto)
2007  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2008  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2009  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2010  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2011  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2012  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2013  *
2014  * @cfg {String} padding (0|1|2|3|4|5)
2015  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2016  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2017  * @cfg {String} padding_left (0|1|2|3|4|5)
2018  * @cfg {String} padding_right (0|1|2|3|4|5)
2019  * @cfg {String} padding_x (0|1|2|3|4|5)
2020  * @cfg {String} padding_y (0|1|2|3|4|5)
2021  *
2022  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2024  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2025  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2026  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2027  
2028  * @config {Boolean} dragable  if this card can be dragged.
2029  * @config {String} drag_group  group for drag
2030  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2031  * @config {String} drop_group  group for drag
2032  * 
2033  * @config {Boolean} collapsable can the body be collapsed.
2034  * @config {Boolean} collapsed is the body collapsed when rendered...
2035  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2036  * @config {Boolean} rotated is the body rotated when rendered...
2037  * 
2038  * @constructor
2039  * Create a new Container
2040  * @param {Object} config The config object
2041  */
2042
2043 Roo.bootstrap.Card = function(config){
2044     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2045     
2046     this.addEvents({
2047          // raw events
2048         /**
2049          * @event drop
2050          * When a element a card is dropped
2051          * @param {Roo.bootstrap.Card} this
2052          *
2053          * 
2054          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2055          * @param {String} position 'above' or 'below'
2056          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2057         
2058          */
2059         'drop' : true,
2060          /**
2061          * @event rotate
2062          * When a element a card is rotate
2063          * @param {Roo.bootstrap.Card} this
2064          * @param {Roo.Element} n the node being dropped?
2065          * @param {Boolean} rotate status
2066          */
2067         'rotate' : true,
2068         /**
2069          * @event cardover
2070          * When a card element is dragged over ready to drop (return false to block dropable)
2071          * @param {Roo.bootstrap.Card} this
2072          * @param {Object} data from dragdrop 
2073          */
2074          'cardover' : true
2075          
2076     });
2077 };
2078
2079
2080 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2081     
2082     
2083     weight : '',
2084     
2085     margin: '', /// may be better in component?
2086     margin_top: '', 
2087     margin_bottom: '', 
2088     margin_left: '',
2089     margin_right: '',
2090     margin_x: '',
2091     margin_y: '',
2092     
2093     padding : '',
2094     padding_top: '', 
2095     padding_bottom: '', 
2096     padding_left: '',
2097     padding_right: '',
2098     padding_x: '',
2099     padding_y: '',
2100     
2101     display: '', 
2102     display_xs: '', 
2103     display_sm: '', 
2104     display_lg: '',
2105     display_xl: '',
2106  
2107     header_image  : '',
2108     header : '',
2109     header_size : 0,
2110     title : '',
2111     subtitle : '',
2112     html : '',
2113     footer: '',
2114
2115     collapsable : false,
2116     collapsed : false,
2117     rotateable : false,
2118     rotated : false,
2119     
2120     dragable : false,
2121     drag_group : false,
2122     dropable : false,
2123     drop_group : false,
2124     childContainer : false,
2125     dropEl : false, /// the dom placeholde element that indicates drop location.
2126     containerEl: false, // body container
2127     bodyEl: false, // card-body
2128     headerContainerEl : false, //
2129     headerEl : false,
2130     header_imageEl : false,
2131     
2132     
2133     layoutCls : function()
2134     {
2135         var cls = '';
2136         var t = this;
2137         Roo.log(this.margin_bottom.length);
2138         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2139             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2140             
2141             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2142                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2143             }
2144             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2145                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2146             }
2147         });
2148         
2149         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2150             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2151                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2152             }
2153         });
2154         
2155         // more generic support?
2156         if (this.hidden) {
2157             cls += ' d-none';
2158         }
2159         
2160         return cls;
2161     },
2162  
2163        // Roo.log("Call onRender: " + this.xtype);
2164         /*  We are looking at something like this.
2165 <div class="card">
2166     <img src="..." class="card-img-top" alt="...">
2167     <div class="card-body">
2168         <h5 class="card-title">Card title</h5>
2169          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2170
2171         >> this bit is really the body...
2172         <div> << we will ad dthis in hopefully it will not break shit.
2173         
2174         ** card text does not actually have any styling...
2175         
2176             <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>
2177         
2178         </div> <<
2179           <a href="#" class="card-link">Card link</a>
2180           
2181     </div>
2182     <div class="card-footer">
2183         <small class="text-muted">Last updated 3 mins ago</small>
2184     </div>
2185 </div>
2186          */
2187     getAutoCreate : function(){
2188         
2189         var cfg = {
2190             tag : 'div',
2191             cls : 'card',
2192             cn : [ ]
2193         };
2194         
2195         if (this.weight.length && this.weight != 'light') {
2196             cfg.cls += ' text-white';
2197         } else {
2198             cfg.cls += ' text-dark'; // need as it's nested..
2199         }
2200         if (this.weight.length) {
2201             cfg.cls += ' bg-' + this.weight;
2202         }
2203         
2204         cfg.cls += ' ' + this.layoutCls(); 
2205         
2206         var hdr = false;
2207         var hdr_ctr = false;
2208         if (this.header.length) {
2209             hdr = {
2210                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2211                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2212                 cn : []
2213             };
2214             cfg.cn.push(hdr);
2215             hdr_ctr = hdr;
2216         } else {
2217             hdr = {
2218                 tag : 'div',
2219                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2220                 cn : []
2221             };
2222             cfg.cn.push(hdr);
2223             hdr_ctr = hdr;
2224         }
2225         if (this.collapsable) {
2226             hdr_ctr = {
2227             tag : 'a',
2228             cls : 'd-block user-select-none',
2229             cn: [
2230                     {
2231                         tag: 'i',
2232                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2233                     }
2234                    
2235                 ]
2236             };
2237             hdr.cn.push(hdr_ctr);
2238         }
2239         
2240         hdr_ctr.cn.push(        {
2241             tag: 'span',
2242             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2243             html : this.header
2244         });
2245         
2246         
2247         if (this.header_image.length) {
2248             cfg.cn.push({
2249                 tag : 'img',
2250                 cls : 'card-img-top',
2251                 src: this.header_image // escape?
2252             });
2253         } else {
2254             cfg.cn.push({
2255                     tag : 'div',
2256                     cls : 'card-img-top d-none' 
2257                 });
2258         }
2259             
2260         var body = {
2261             tag : 'div',
2262             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2263             cn : []
2264         };
2265         var obody = body;
2266         if (this.collapsable || this.rotateable) {
2267             obody = {
2268                 tag: 'div',
2269                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2270                 cn : [  body ]
2271             };
2272         }
2273         
2274         cfg.cn.push(obody);
2275         
2276         if (this.title.length) {
2277             body.cn.push({
2278                 tag : 'div',
2279                 cls : 'card-title',
2280                 src: this.title // escape?
2281             });
2282         }  
2283         
2284         if (this.subtitle.length) {
2285             body.cn.push({
2286                 tag : 'div',
2287                 cls : 'card-title',
2288                 src: this.subtitle // escape?
2289             });
2290         }
2291         
2292         body.cn.push({
2293             tag : 'div',
2294             cls : 'roo-card-body-ctr'
2295         });
2296         
2297         if (this.html.length) {
2298             body.cn.push({
2299                 tag: 'div',
2300                 html : this.html
2301             });
2302         }
2303         // fixme ? handle objects?
2304         
2305         if (this.footer.length) {
2306            
2307             cfg.cn.push({
2308                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2309                 html : this.footer
2310             });
2311             
2312         } else {
2313             cfg.cn.push({cls : 'card-footer d-none'});
2314         }
2315         
2316         // footer...
2317         
2318         return cfg;
2319     },
2320     
2321     
2322     getCardHeader : function()
2323     {
2324         var  ret = this.el.select('.card-header',true).first();
2325         if (ret.hasClass('d-none')) {
2326             ret.removeClass('d-none');
2327         }
2328         
2329         return ret;
2330     },
2331     getCardFooter : function()
2332     {
2333         var  ret = this.el.select('.card-footer',true).first();
2334         if (ret.hasClass('d-none')) {
2335             ret.removeClass('d-none');
2336         }
2337         
2338         return ret;
2339     },
2340     getCardImageTop : function()
2341     {
2342         var  ret = this.header_imageEl;
2343         if (ret.hasClass('d-none')) {
2344             ret.removeClass('d-none');
2345         }
2346             
2347         return ret;
2348     },
2349     
2350     getChildContainer : function()
2351     {
2352         
2353         if(!this.el){
2354             return false;
2355         }
2356         return this.el.select('.roo-card-body-ctr',true).first();    
2357     },
2358     
2359     initEvents: function() 
2360     {
2361         this.bodyEl = this.el.select('.card-body',true).first(); 
2362         this.containerEl = this.getChildContainer();
2363         if(this.dragable){
2364             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2365                     containerScroll: true,
2366                     ddGroup: this.drag_group || 'default_card_drag_group'
2367             });
2368             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2369         }
2370         if (this.dropable) {
2371             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2372                 containerScroll: true,
2373                 ddGroup: this.drop_group || 'default_card_drag_group'
2374             });
2375             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2376             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2377             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2378             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2379             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2380         }
2381         
2382         if (this.collapsable) {
2383             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2384         }
2385         if (this.rotateable) {
2386             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2387         }
2388         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2389          
2390         this.footerEl = this.el.select('.card-footer',true).first();
2391         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2392         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2393         this.headerEl = this.el.select('.card-header',true).first();
2394         
2395         if (this.rotated) {
2396             this.el.addClass('roo-card-rotated');
2397             this.fireEvent('rotate', this, true);
2398         }
2399         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2400         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2401         
2402     },
2403     getDragData : function(e)
2404     {
2405         var target = this.getEl();
2406         if (target) {
2407             //this.handleSelection(e);
2408             
2409             var dragData = {
2410                 source: this,
2411                 copy: false,
2412                 nodes: this.getEl(),
2413                 records: []
2414             };
2415             
2416             
2417             dragData.ddel = target.dom ;    // the div element
2418             Roo.log(target.getWidth( ));
2419             dragData.ddel.style.width = target.getWidth() + 'px';
2420             
2421             return dragData;
2422         }
2423         return false;
2424     },
2425     /**
2426     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2427     *    whole Element becomes the target, and this causes the drop gesture to append.
2428     *
2429     *    Returns an object:
2430     *     {
2431            
2432            position : 'below' or 'above'
2433            card  : relateive to card OBJECT (or true for no cards listed)
2434            items_n : relative to nth item in list
2435            card_n : relative to  nth card in list
2436     }
2437     *
2438     *    
2439     */
2440     getTargetFromEvent : function(e, dragged_card_el)
2441     {
2442         var target = e.getTarget();
2443         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2444             target = target.parentNode;
2445         }
2446         
2447         var ret = {
2448             position: '',
2449             cards : [],
2450             card_n : -1,
2451             items_n : -1,
2452             card : false 
2453         };
2454         
2455         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2456         // see if target is one of the 'cards'...
2457         
2458         
2459         //Roo.log(this.items.length);
2460         var pos = false;
2461         
2462         var last_card_n = 0;
2463         var cards_len  = 0;
2464         for (var i = 0;i< this.items.length;i++) {
2465             
2466             if (!this.items[i].el.hasClass('card')) {
2467                  continue;
2468             }
2469             pos = this.getDropPoint(e, this.items[i].el.dom);
2470             
2471             cards_len = ret.cards.length;
2472             //Roo.log(this.items[i].el.dom.id);
2473             ret.cards.push(this.items[i]);
2474             last_card_n  = i;
2475             if (ret.card_n < 0 && pos == 'above') {
2476                 ret.position = cards_len > 0 ? 'below' : pos;
2477                 ret.items_n = i > 0 ? i - 1 : 0;
2478                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2479                 ret.card = ret.cards[ret.card_n];
2480             }
2481         }
2482         if (!ret.cards.length) {
2483             ret.card = true;
2484             ret.position = 'below';
2485             ret.items_n;
2486             return ret;
2487         }
2488         // could not find a card.. stick it at the end..
2489         if (ret.card_n < 0) {
2490             ret.card_n = last_card_n;
2491             ret.card = ret.cards[last_card_n];
2492             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2493             ret.position = 'below';
2494         }
2495         
2496         if (this.items[ret.items_n].el == dragged_card_el) {
2497             return false;
2498         }
2499         
2500         if (ret.position == 'below') {
2501             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2502             
2503             if (card_after  && card_after.el == dragged_card_el) {
2504                 return false;
2505             }
2506             return ret;
2507         }
2508         
2509         // its's after ..
2510         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2511         
2512         if (card_before  && card_before.el == dragged_card_el) {
2513             return false;
2514         }
2515         
2516         return ret;
2517     },
2518     
2519     onNodeEnter : function(n, dd, e, data){
2520         return false;
2521     },
2522     onNodeOver : function(n, dd, e, data)
2523     {
2524        
2525         var target_info = this.getTargetFromEvent(e,data.source.el);
2526         if (target_info === false) {
2527             this.dropPlaceHolder('hide');
2528             return false;
2529         }
2530         Roo.log(['getTargetFromEvent', target_info ]);
2531         
2532         
2533         if (this.fireEvent('cardover', this, [ data ]) === false) {
2534             return false;
2535         }
2536         
2537         this.dropPlaceHolder('show', target_info,data);
2538         
2539         return false; 
2540     },
2541     onNodeOut : function(n, dd, e, data){
2542         this.dropPlaceHolder('hide');
2543      
2544     },
2545     onNodeDrop : function(n, dd, e, data)
2546     {
2547         
2548         // call drop - return false if
2549         
2550         // this could actually fail - if the Network drops..
2551         // we will ignore this at present..- client should probably reload
2552         // the whole set of cards if stuff like that fails.
2553         
2554         
2555         var info = this.getTargetFromEvent(e,data.source.el);
2556         if (info === false) {
2557             return false;
2558         }
2559         this.dropPlaceHolder('hide');
2560   
2561           
2562     
2563         this.acceptCard(data.source, info.position, info.card, info.items_n);
2564         return true;
2565          
2566     },
2567     firstChildCard : function()
2568     {
2569         for (var i = 0;i< this.items.length;i++) {
2570             
2571             if (!this.items[i].el.hasClass('card')) {
2572                  continue;
2573             }
2574             return this.items[i];
2575         }
2576         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2577     },
2578     /**
2579      * accept card
2580      *
2581      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2582      */
2583     acceptCard : function(move_card,  position, next_to_card )
2584     {
2585         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2586             return false;
2587         }
2588         
2589         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2590         
2591         move_card.parent().removeCard(move_card);
2592         
2593         
2594         var dom = move_card.el.dom;
2595         dom.style.width = ''; // clear with - which is set by drag.
2596         
2597         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2598             var cardel = next_to_card.el.dom;
2599             
2600             if (position == 'above' ) {
2601                 cardel.parentNode.insertBefore(dom, cardel);
2602             } else if (cardel.nextSibling) {
2603                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2604             } else {
2605                 cardel.parentNode.append(dom);
2606             }
2607         } else {
2608             // card container???
2609             this.containerEl.dom.append(dom);
2610         }
2611         
2612         //FIXME HANDLE card = true 
2613         
2614         // add this to the correct place in items.
2615         
2616         // remove Card from items.
2617         
2618        
2619         if (this.items.length) {
2620             var nitems = [];
2621             //Roo.log([info.items_n, info.position, this.items.length]);
2622             for (var i =0; i < this.items.length; i++) {
2623                 if (i == to_items_n && position == 'above') {
2624                     nitems.push(move_card);
2625                 }
2626                 nitems.push(this.items[i]);
2627                 if (i == to_items_n && position == 'below') {
2628                     nitems.push(move_card);
2629                 }
2630             }
2631             this.items = nitems;
2632             Roo.log(this.items);
2633         } else {
2634             this.items.push(move_card);
2635         }
2636         
2637         move_card.parentId = this.id;
2638         
2639         return true;
2640         
2641         
2642     },
2643     removeCard : function(c)
2644     {
2645         this.items = this.items.filter(function(e) { return e != c });
2646  
2647         var dom = c.el.dom;
2648         dom.parentNode.removeChild(dom);
2649         dom.style.width = ''; // clear with - which is set by drag.
2650         c.parentId = false;
2651         
2652     },
2653     
2654     /**    Decide whether to drop above or below a View node. */
2655     getDropPoint : function(e, n, dd)
2656     {
2657         if (dd) {
2658              return false;
2659         }
2660         if (n == this.containerEl.dom) {
2661             return "above";
2662         }
2663         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2664         var c = t + (b - t) / 2;
2665         var y = Roo.lib.Event.getPageY(e);
2666         if(y <= c) {
2667             return "above";
2668         }else{
2669             return "below";
2670         }
2671     },
2672     onToggleCollapse : function(e)
2673         {
2674         if (this.collapsed) {
2675             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2676             this.collapsableEl.addClass('show');
2677             this.collapsed = false;
2678             return;
2679         }
2680         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2681         this.collapsableEl.removeClass('show');
2682         this.collapsed = true;
2683         
2684     
2685     },
2686     
2687     onToggleRotate : function(e)
2688     {
2689         this.collapsableEl.removeClass('show');
2690         this.footerEl.removeClass('d-none');
2691         this.el.removeClass('roo-card-rotated');
2692         this.el.removeClass('d-none');
2693         if (this.rotated) {
2694             
2695             this.collapsableEl.addClass('show');
2696             this.rotated = false;
2697             this.fireEvent('rotate', this, this.rotated);
2698             return;
2699         }
2700         this.el.addClass('roo-card-rotated');
2701         this.footerEl.addClass('d-none');
2702         this.el.select('.roo-collapsable').removeClass('show');
2703         
2704         this.rotated = true;
2705         this.fireEvent('rotate', this, this.rotated);
2706     
2707     },
2708     
2709     dropPlaceHolder: function (action, info, data)
2710     {
2711         if (this.dropEl === false) {
2712             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2713             cls : 'd-none'
2714             },true);
2715         }
2716         this.dropEl.removeClass(['d-none', 'd-block']);        
2717         if (action == 'hide') {
2718             
2719             this.dropEl.addClass('d-none');
2720             return;
2721         }
2722         // FIXME - info.card == true!!!
2723         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2724         
2725         if (info.card !== true) {
2726             var cardel = info.card.el.dom;
2727             
2728             if (info.position == 'above') {
2729                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2730             } else if (cardel.nextSibling) {
2731                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2732             } else {
2733                 cardel.parentNode.append(this.dropEl.dom);
2734             }
2735         } else {
2736             // card container???
2737             this.containerEl.dom.append(this.dropEl.dom);
2738         }
2739         
2740         this.dropEl.addClass('d-block roo-card-dropzone');
2741         
2742         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2743         
2744         
2745     
2746     
2747     
2748     },
2749     setHeaderText: function(html)
2750     {
2751         this.header = html;
2752         if (this.headerContainerEl) {
2753             this.headerContainerEl.dom.innerHTML = html;
2754         }
2755     },
2756     onHeaderImageLoad : function(ev, he)
2757     {
2758         if (!this.header_image_fit_square) {
2759             return;
2760         }
2761         
2762         var hw = he.naturalHeight / he.naturalWidth;
2763         // wide image = < 0
2764         // tall image = > 1
2765         //var w = he.dom.naturalWidth;
2766         var ww = he.width;
2767         he.style.left =  0;
2768         he.style.position =  'relative';
2769         if (hw > 1) {
2770             var nw = (ww * (1/hw));
2771             Roo.get(he).setSize( ww * (1/hw),  ww);
2772             he.style.left =  ((ww - nw)/ 2) + 'px';
2773             he.style.position =  'relative';
2774         }
2775
2776     }
2777
2778     
2779 });
2780
2781 /*
2782  * - LGPL
2783  *
2784  * Card header - holder for the card header elements.
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.CardHeader
2790  * @extends Roo.bootstrap.Element
2791  * @parent Roo.bootstrap.Card
2792  * @children Roo.bootstrap.Component
2793  * Bootstrap CardHeader class
2794  * @constructor
2795  * Create a new Card Header - that you can embed children into
2796  * @param {Object} config The config object
2797  */
2798
2799 Roo.bootstrap.CardHeader = function(config){
2800     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2801 };
2802
2803 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2804     
2805     
2806     container_method : 'getCardHeader' 
2807     
2808      
2809     
2810     
2811    
2812 });
2813
2814  
2815
2816  /*
2817  * - LGPL
2818  *
2819  * Card footer - holder for the card footer elements.
2820  * 
2821  */
2822
2823 /**
2824  * @class Roo.bootstrap.CardFooter
2825  * @extends Roo.bootstrap.Element
2826  * @parent Roo.bootstrap.Card
2827  * @children Roo.bootstrap.Component
2828  * Bootstrap CardFooter class
2829  * 
2830  * @constructor
2831  * Create a new Card Footer - that you can embed children into
2832  * @param {Object} config The config object
2833  */
2834
2835 Roo.bootstrap.CardFooter = function(config){
2836     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2837 };
2838
2839 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2840     
2841     
2842     container_method : 'getCardFooter' 
2843     
2844      
2845     
2846     
2847    
2848 });
2849
2850  
2851
2852  /*
2853  * - LGPL
2854  *
2855  * Card header - holder for the card header elements.
2856  * 
2857  */
2858
2859 /**
2860  * @class Roo.bootstrap.CardImageTop
2861  * @extends Roo.bootstrap.Element
2862  * @parent Roo.bootstrap.Card
2863  * @children Roo.bootstrap.Component
2864  * Bootstrap CardImageTop class
2865  * 
2866  * @constructor
2867  * Create a new Card Image Top container
2868  * @param {Object} config The config object
2869  */
2870
2871 Roo.bootstrap.CardImageTop = function(config){
2872     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2873 };
2874
2875 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2876     
2877    
2878     container_method : 'getCardImageTop' 
2879     
2880      
2881     
2882    
2883 });
2884
2885  
2886
2887  
2888 /*
2889 * Licence: LGPL
2890 */
2891
2892 /**
2893  * @class Roo.bootstrap.ButtonUploader
2894  * @extends Roo.bootstrap.Button
2895  * Bootstrap Button Uploader class - it's a button which when you add files to it
2896  *
2897  * 
2898  * @cfg {Number} errorTimeout default 3000
2899  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2900  * @cfg {Array}  html The button text.
2901  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2902  *
2903  * @constructor
2904  * Create a new CardUploader
2905  * @param {Object} config The config object
2906  */
2907
2908 Roo.bootstrap.ButtonUploader = function(config){
2909     
2910  
2911     
2912     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2913     
2914      
2915      this.addEvents({
2916          // raw events
2917         /**
2918          * @event beforeselect
2919          * When button is pressed, before show upload files dialog is shown
2920          * @param {Roo.bootstrap.UploaderButton} this
2921          *
2922          */
2923         'beforeselect' : true,
2924          /**
2925          * @event fired when files have been selected, 
2926          * When a the download link is clicked
2927          * @param {Roo.bootstrap.UploaderButton} this
2928          * @param {Array} Array of files that have been uploaded
2929          */
2930         'uploaded' : true
2931         
2932     });
2933 };
2934  
2935 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2936     
2937      
2938     errorTimeout : 3000,
2939      
2940     images : false,
2941    
2942     fileCollection : false,
2943     allowBlank : true,
2944     
2945     multiple : true,
2946     
2947     getAutoCreate : function()
2948     {
2949        
2950         
2951         return  {
2952             cls :'div' ,
2953             cn : [
2954                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2955             ]
2956         };
2957            
2958          
2959     },
2960      
2961    
2962     initEvents : function()
2963     {
2964         
2965         Roo.bootstrap.Button.prototype.initEvents.call(this);
2966         
2967         
2968         
2969         
2970         
2971         this.urlAPI = (window.createObjectURL && window) || 
2972                                 (window.URL && URL.revokeObjectURL && URL) || 
2973                                 (window.webkitURL && webkitURL);
2974                         
2975         var im = {
2976             tag: 'input',
2977             type : 'file',
2978             cls : 'd-none  roo-card-upload-selector' 
2979           
2980         };
2981         if (this.multiple) {
2982             im.multiple = 'multiple';
2983         }
2984         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2985        
2986         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2987         
2988         this.selectorEl.on('change', this.onFileSelected, this);
2989          
2990          
2991        
2992     },
2993     
2994    
2995     onClick : function(e)
2996     {
2997         e.preventDefault();
2998         
2999         if ( this.fireEvent('beforeselect', this) === false) {
3000             return;
3001         }
3002          
3003         this.selectorEl.dom.click();
3004          
3005     },
3006     
3007     onFileSelected : function(e)
3008     {
3009         e.preventDefault();
3010         
3011         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3012             return;
3013         }
3014         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3015         this.selectorEl.dom.value  = '';// hopefully reset..
3016         
3017         this.fireEvent('uploaded', this,  files );
3018         
3019     },
3020     
3021        
3022    
3023     
3024     /**
3025      * addCard - add an Attachment to the uploader
3026      * @param data - the data about the image to upload
3027      *
3028      * {
3029           id : 123
3030           title : "Title of file",
3031           is_uploaded : false,
3032           src : "http://.....",
3033           srcfile : { the File upload object },
3034           mimetype : file.type,
3035           preview : false,
3036           is_deleted : 0
3037           .. any other data...
3038         }
3039      *
3040      * 
3041     */
3042      
3043     reset: function()
3044     {
3045          
3046          this.selectorEl
3047     } 
3048     
3049     
3050     
3051     
3052 });
3053  /*
3054  * - LGPL
3055  *
3056  * image
3057  * 
3058  */
3059
3060
3061 /**
3062  * @class Roo.bootstrap.Img
3063  * @extends Roo.bootstrap.Component
3064  * Bootstrap Img class
3065  * @cfg {Boolean} imgResponsive false | true
3066  * @cfg {String} border rounded | circle | thumbnail
3067  * @cfg {String} src image source
3068  * @cfg {String} alt image alternative text
3069  * @cfg {String} href a tag href
3070  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3071  * @cfg {String} xsUrl xs image source
3072  * @cfg {String} smUrl sm image source
3073  * @cfg {String} mdUrl md image source
3074  * @cfg {String} lgUrl lg image source
3075  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3076  * 
3077  * @constructor
3078  * Create a new Input
3079  * @param {Object} config The config object
3080  */
3081
3082 Roo.bootstrap.Img = function(config){
3083     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3084     
3085     this.addEvents({
3086         // img events
3087         /**
3088          * @event click
3089          * The img click event for the img.
3090          * @param {Roo.EventObject} e
3091          */
3092         "click" : true,
3093         /**
3094          * @event load
3095          * The when any image loads
3096          * @param {Roo.EventObject} e
3097          */
3098         "load" : true
3099     });
3100 };
3101
3102 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3103     
3104     imgResponsive: true,
3105     border: '',
3106     src: 'about:blank',
3107     href: false,
3108     target: false,
3109     xsUrl: '',
3110     smUrl: '',
3111     mdUrl: '',
3112     lgUrl: '',
3113     backgroundContain : false,
3114
3115     getAutoCreate : function()
3116     {   
3117         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3118             return this.createSingleImg();
3119         }
3120         
3121         var cfg = {
3122             tag: 'div',
3123             cls: 'roo-image-responsive-group',
3124             cn: []
3125         };
3126         var _this = this;
3127         
3128         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3129             
3130             if(!_this[size + 'Url']){
3131                 return;
3132             }
3133             
3134             var img = {
3135                 tag: 'img',
3136                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3137                 html: _this.html || cfg.html,
3138                 src: _this[size + 'Url']
3139             };
3140             
3141             img.cls += ' roo-image-responsive-' + size;
3142             
3143             var s = ['xs', 'sm', 'md', 'lg'];
3144             
3145             s.splice(s.indexOf(size), 1);
3146             
3147             Roo.each(s, function(ss){
3148                 img.cls += ' hidden-' + ss;
3149             });
3150             
3151             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3152                 cfg.cls += ' img-' + _this.border;
3153             }
3154             
3155             if(_this.alt){
3156                 cfg.alt = _this.alt;
3157             }
3158             
3159             if(_this.href){
3160                 var a = {
3161                     tag: 'a',
3162                     href: _this.href,
3163                     cn: [
3164                         img
3165                     ]
3166                 };
3167
3168                 if(this.target){
3169                     a.target = _this.target;
3170                 }
3171             }
3172             
3173             cfg.cn.push((_this.href) ? a : img);
3174             
3175         });
3176         
3177         return cfg;
3178     },
3179     
3180     createSingleImg : function()
3181     {
3182         var cfg = {
3183             tag: 'img',
3184             cls: (this.imgResponsive) ? 'img-responsive' : '',
3185             html : null,
3186             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3187         };
3188         
3189         if (this.backgroundContain) {
3190             cfg.cls += ' background-contain';
3191         }
3192         
3193         cfg.html = this.html || cfg.html;
3194         
3195         if (this.backgroundContain) {
3196             cfg.style="background-image: url(" + this.src + ')';
3197         } else {
3198             cfg.src = this.src || cfg.src;
3199         }
3200         
3201         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3202             cfg.cls += ' img-' + this.border;
3203         }
3204         
3205         if(this.alt){
3206             cfg.alt = this.alt;
3207         }
3208         
3209         if(this.href){
3210             var a = {
3211                 tag: 'a',
3212                 href: this.href,
3213                 cn: [
3214                     cfg
3215                 ]
3216             };
3217             
3218             if(this.target){
3219                 a.target = this.target;
3220             }
3221             
3222         }
3223         
3224         return (this.href) ? a : cfg;
3225     },
3226     
3227     initEvents: function() 
3228     {
3229         if(!this.href){
3230             this.el.on('click', this.onClick, this);
3231         }
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             this.el.on('load', this.onImageLoad, this);
3234         } else {
3235             // not sure if this works.. not tested
3236             this.el.select('img', true).on('load', this.onImageLoad, this);
3237         }
3238         
3239     },
3240     
3241     onClick : function(e)
3242     {
3243         Roo.log('img onclick');
3244         this.fireEvent('click', this, e);
3245     },
3246     onImageLoad: function(e)
3247     {
3248         Roo.log('img load');
3249         this.fireEvent('load', this, e);
3250     },
3251     
3252     /**
3253      * Sets the url of the image - used to update it
3254      * @param {String} url the url of the image
3255      */
3256     
3257     setSrc : function(url)
3258     {
3259         this.src =  url;
3260         
3261         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3262             if (this.backgroundContain) {
3263                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3264             } else {
3265                 this.el.dom.src =  url;
3266             }
3267             return;
3268         }
3269         
3270         this.el.select('img', true).first().dom.src =  url;
3271     }
3272     
3273     
3274    
3275 });
3276
3277  /*
3278  * - LGPL
3279  *
3280  * image
3281  * 
3282  */
3283
3284
3285 /**
3286  * @class Roo.bootstrap.Link
3287  * @extends Roo.bootstrap.Component
3288  * @children Roo.bootstrap.Component
3289  * Bootstrap Link Class (eg. '<a href>')
3290  
3291  * @cfg {String} alt image alternative text
3292  * @cfg {String} href a tag href
3293  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3294  * @cfg {String} html the content of the link.
3295  * @cfg {String} anchor name for the anchor link
3296  * @cfg {String} fa - favicon
3297
3298  * @cfg {Boolean} preventDefault (true | false) default false
3299
3300  * 
3301  * @constructor
3302  * Create a new Input
3303  * @param {Object} config The config object
3304  */
3305
3306 Roo.bootstrap.Link = function(config){
3307     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3308     
3309     this.addEvents({
3310         // img events
3311         /**
3312          * @event click
3313          * The img click event for the img.
3314          * @param {Roo.EventObject} e
3315          */
3316         "click" : true
3317     });
3318 };
3319
3320 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3321     
3322     href: false,
3323     target: false,
3324     preventDefault: false,
3325     anchor : false,
3326     alt : false,
3327     fa: false,
3328
3329
3330     getAutoCreate : function()
3331     {
3332         var html = this.html || '';
3333         
3334         if (this.fa !== false) {
3335             html = '<i class="fa fa-' + this.fa + '"></i>';
3336         }
3337         var cfg = {
3338             tag: 'a'
3339         };
3340         // anchor's do not require html/href...
3341         if (this.anchor === false) {
3342             cfg.html = html;
3343             cfg.href = this.href || '#';
3344         } else {
3345             cfg.name = this.anchor;
3346             if (this.html !== false || this.fa !== false) {
3347                 cfg.html = html;
3348             }
3349             if (this.href !== false) {
3350                 cfg.href = this.href;
3351             }
3352         }
3353         
3354         if(this.alt !== false){
3355             cfg.alt = this.alt;
3356         }
3357         
3358         
3359         if(this.target !== false) {
3360             cfg.target = this.target;
3361         }
3362         
3363         return cfg;
3364     },
3365     
3366     initEvents: function() {
3367         
3368         if(!this.href || this.preventDefault){
3369             this.el.on('click', this.onClick, this);
3370         }
3371     },
3372     
3373     onClick : function(e)
3374     {
3375         if(this.preventDefault){
3376             e.preventDefault();
3377         }
3378         //Roo.log('img onclick');
3379         this.fireEvent('click', this, e);
3380     }
3381    
3382 });
3383
3384  /*
3385  * - LGPL
3386  *
3387  * header
3388  * 
3389  */
3390
3391 /**
3392  * @class Roo.bootstrap.Header
3393  * @extends Roo.bootstrap.Component
3394  * @children Roo.bootstrap.Component
3395  * Bootstrap Header class
3396  *
3397  * 
3398  * @cfg {String} html content of header
3399  * @cfg {Number} level (1|2|3|4|5|6) default 1
3400  * 
3401  * @constructor
3402  * Create a new Header
3403  * @param {Object} config The config object
3404  */
3405
3406
3407 Roo.bootstrap.Header  = function(config){
3408     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3409 };
3410
3411 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3412     
3413     //href : false,
3414     html : false,
3415     level : 1,
3416     
3417     
3418     
3419     getAutoCreate : function(){
3420         
3421         
3422         
3423         var cfg = {
3424             tag: 'h' + (1 *this.level),
3425             html: this.html || ''
3426         } ;
3427         
3428         return cfg;
3429     }
3430    
3431 });
3432
3433  
3434
3435  /**
3436  * @class Roo.bootstrap.MenuMgr
3437  * @licence LGPL
3438  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3439  * @static
3440  */
3441 Roo.bootstrap.menu.Manager = function(){
3442    var menus, active, groups = {}, attached = false, lastShow = new Date();
3443
3444    // private - called when first menu is created
3445    function init(){
3446        menus = {};
3447        active = new Roo.util.MixedCollection();
3448        Roo.get(document).addKeyListener(27, function(){
3449            if(active.length > 0){
3450                hideAll();
3451            }
3452        });
3453    }
3454
3455    // private
3456    function hideAll(){
3457        if(active && active.length > 0){
3458            var c = active.clone();
3459            c.each(function(m){
3460                m.hide();
3461            });
3462        }
3463    }
3464
3465    // private
3466    function onHide(m){
3467        active.remove(m);
3468        if(active.length < 1){
3469            Roo.get(document).un("mouseup", onMouseDown);
3470             
3471            attached = false;
3472        }
3473    }
3474
3475    // private
3476    function onShow(m){
3477        var last = active.last();
3478        lastShow = new Date();
3479        active.add(m);
3480        if(!attached){
3481           Roo.get(document).on("mouseup", onMouseDown);
3482            
3483            attached = true;
3484        }
3485        if(m.parentMenu){
3486           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3487           m.parentMenu.activeChild = m;
3488        }else if(last && last.isVisible()){
3489           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3490        }
3491    }
3492
3493    // private
3494    function onBeforeHide(m){
3495        if(m.activeChild){
3496            m.activeChild.hide();
3497        }
3498        if(m.autoHideTimer){
3499            clearTimeout(m.autoHideTimer);
3500            delete m.autoHideTimer;
3501        }
3502    }
3503
3504    // private
3505    function onBeforeShow(m){
3506        var pm = m.parentMenu;
3507        if(!pm && !m.allowOtherMenus){
3508            hideAll();
3509        }else if(pm && pm.activeChild && active != m){
3510            pm.activeChild.hide();
3511        }
3512    }
3513
3514    // private this should really trigger on mouseup..
3515    function onMouseDown(e){
3516         Roo.log("on Mouse Up");
3517         
3518         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3519             Roo.log("MenuManager hideAll");
3520             hideAll();
3521             e.stopEvent();
3522         }
3523         
3524         
3525    }
3526
3527    // private
3528    function onBeforeCheck(mi, state){
3529        if(state){
3530            var g = groups[mi.group];
3531            for(var i = 0, l = g.length; i < l; i++){
3532                if(g[i] != mi){
3533                    g[i].setChecked(false);
3534                }
3535            }
3536        }
3537    }
3538
3539    return {
3540
3541        /**
3542         * Hides all menus that are currently visible
3543         */
3544        hideAll : function(){
3545             hideAll();  
3546        },
3547
3548        // private
3549        register : function(menu){
3550            if(!menus){
3551                init();
3552            }
3553            menus[menu.id] = menu;
3554            menu.on("beforehide", onBeforeHide);
3555            menu.on("hide", onHide);
3556            menu.on("beforeshow", onBeforeShow);
3557            menu.on("show", onShow);
3558            var g = menu.group;
3559            if(g && menu.events["checkchange"]){
3560                if(!groups[g]){
3561                    groups[g] = [];
3562                }
3563                groups[g].push(menu);
3564                menu.on("checkchange", onCheck);
3565            }
3566        },
3567
3568         /**
3569          * Returns a {@link Roo.menu.Menu} object
3570          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3571          * be used to generate and return a new Menu instance.
3572          */
3573        get : function(menu){
3574            if(typeof menu == "string"){ // menu id
3575                return menus[menu];
3576            }else if(menu.events){  // menu instance
3577                return menu;
3578            }
3579            /*else if(typeof menu.length == 'number'){ // array of menu items?
3580                return new Roo.bootstrap.Menu({items:menu});
3581            }else{ // otherwise, must be a config
3582                return new Roo.bootstrap.Menu(menu);
3583            }
3584            */
3585            return false;
3586        },
3587
3588        // private
3589        unregister : function(menu){
3590            delete menus[menu.id];
3591            menu.un("beforehide", onBeforeHide);
3592            menu.un("hide", onHide);
3593            menu.un("beforeshow", onBeforeShow);
3594            menu.un("show", onShow);
3595            var g = menu.group;
3596            if(g && menu.events["checkchange"]){
3597                groups[g].remove(menu);
3598                menu.un("checkchange", onCheck);
3599            }
3600        },
3601
3602        // private
3603        registerCheckable : function(menuItem){
3604            var g = menuItem.group;
3605            if(g){
3606                if(!groups[g]){
3607                    groups[g] = [];
3608                }
3609                groups[g].push(menuItem);
3610                menuItem.on("beforecheckchange", onBeforeCheck);
3611            }
3612        },
3613
3614        // private
3615        unregisterCheckable : function(menuItem){
3616            var g = menuItem.group;
3617            if(g){
3618                groups[g].remove(menuItem);
3619                menuItem.un("beforecheckchange", onBeforeCheck);
3620            }
3621        }
3622    };
3623 }(); 
3624 /**
3625  * @class Roo.bootstrap.menu.Menu
3626  * @extends Roo.bootstrap.Component
3627  * @licence LGPL
3628  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3629  * @parent none
3630  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3631  * 
3632  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3633  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3634  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3635  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3636 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3637 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3638  
3639  * @constructor
3640  * Create a new Menu
3641  * @param {Object} config The config objectQ
3642  */
3643
3644
3645 Roo.bootstrap.menu.Menu = function(config){
3646     
3647     if (config.type == 'treeview') {
3648         // normally menu's are drawn attached to the document to handle layering etc..
3649         // however treeview (used by the docs menu is drawn into the parent element)
3650         this.container_method = 'getChildContainer'; 
3651     }
3652     
3653     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3654     if (this.registerMenu && this.type != 'treeview')  {
3655         Roo.bootstrap.menu.Manager.register(this);
3656     }
3657     
3658     
3659     this.addEvents({
3660         /**
3661          * @event beforeshow
3662          * Fires before this menu is displayed (return false to block)
3663          * @param {Roo.menu.Menu} this
3664          */
3665         beforeshow : true,
3666         /**
3667          * @event beforehide
3668          * Fires before this menu is hidden (return false to block)
3669          * @param {Roo.menu.Menu} this
3670          */
3671         beforehide : true,
3672         /**
3673          * @event show
3674          * Fires after this menu is displayed
3675          * @param {Roo.menu.Menu} this
3676          */
3677         show : true,
3678         /**
3679          * @event hide
3680          * Fires after this menu is hidden
3681          * @param {Roo.menu.Menu} this
3682          */
3683         hide : true,
3684         /**
3685          * @event click
3686          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          * @param {Roo.EventObject} e
3690          */
3691         click : true,
3692         /**
3693          * @event mouseover
3694          * Fires when the mouse is hovering over this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseover : true,
3700         /**
3701          * @event mouseout
3702          * Fires when the mouse exits this menu
3703          * @param {Roo.menu.Menu} this
3704          * @param {Roo.EventObject} e
3705          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3706          */
3707         mouseout : true,
3708         /**
3709          * @event itemclick
3710          * Fires when a menu item contained in this menu is clicked
3711          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3712          * @param {Roo.EventObject} e
3713          */
3714         itemclick: true
3715     });
3716     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3717 };
3718
3719 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3720     
3721    /// html : false,
3722    
3723     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3724     type: false,
3725     /**
3726      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3727      */
3728     registerMenu : true,
3729     
3730     menuItems :false, // stores the menu items..
3731     
3732     hidden:true,
3733         
3734     parentMenu : false,
3735     
3736     stopEvent : true,
3737     
3738     isLink : false,
3739     
3740     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3741     
3742     hideTrigger : false,
3743     
3744     align : 'tl-bl?',
3745     
3746     
3747     getChildContainer : function() {
3748         return this.el;  
3749     },
3750     
3751     getAutoCreate : function(){
3752          
3753         //if (['right'].indexOf(this.align)!==-1) {
3754         //    cfg.cn[1].cls += ' pull-right'
3755         //}
3756          
3757         var cfg = {
3758             tag : 'ul',
3759             cls : 'dropdown-menu shadow' ,
3760             style : 'z-index:1000'
3761             
3762         };
3763         
3764         if (this.type === 'submenu') {
3765             cfg.cls = 'submenu active';
3766         }
3767         if (this.type === 'treeview') {
3768             cfg.cls = 'treeview-menu';
3769         }
3770         
3771         return cfg;
3772     },
3773     initEvents : function() {
3774         
3775        // Roo.log("ADD event");
3776        // Roo.log(this.triggerEl.dom);
3777         if (this.triggerEl) {
3778             
3779             this.triggerEl.on('click', this.onTriggerClick, this);
3780             
3781             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3782             
3783             if (!this.hideTrigger) {
3784                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3785                     // dropdown toggle on the 'a' in BS4?
3786                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3787                 } else {
3788                     this.triggerEl.addClass('dropdown-toggle');
3789                 }
3790             }
3791         }
3792         
3793         if (Roo.isTouch) {
3794             this.el.on('touchstart'  , this.onTouch, this);
3795         }
3796         this.el.on('click' , this.onClick, this);
3797
3798         this.el.on("mouseover", this.onMouseOver, this);
3799         this.el.on("mouseout", this.onMouseOut, this);
3800         
3801     },
3802     
3803     findTargetItem : function(e)
3804     {
3805         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3806         if(!t){
3807             return false;
3808         }
3809         //Roo.log(t);         Roo.log(t.id);
3810         if(t && t.id){
3811             //Roo.log(this.menuitems);
3812             return this.menuitems.get(t.id);
3813             
3814             //return this.items.get(t.menuItemId);
3815         }
3816         
3817         return false;
3818     },
3819     
3820     onTouch : function(e) 
3821     {
3822         Roo.log("menu.onTouch");
3823         //e.stopEvent(); this make the user popdown broken
3824         this.onClick(e);
3825     },
3826     
3827     onClick : function(e)
3828     {
3829         Roo.log("menu.onClick");
3830         
3831         var t = this.findTargetItem(e);
3832         if(!t || t.isContainer){
3833             return;
3834         }
3835         Roo.log(e);
3836         /*
3837         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3838             if(t == this.activeItem && t.shouldDeactivate(e)){
3839                 this.activeItem.deactivate();
3840                 delete this.activeItem;
3841                 return;
3842             }
3843             if(t.canActivate){
3844                 this.setActiveItem(t, true);
3845             }
3846             return;
3847             
3848             
3849         }
3850         */
3851        
3852         Roo.log('pass click event');
3853         
3854         t.onClick(e);
3855         
3856         this.fireEvent("click", this, t, e);
3857         
3858         var _this = this;
3859         
3860         if(!t.href.length || t.href == '#'){
3861             (function() { _this.hide(); }).defer(100);
3862         }
3863         
3864     },
3865     
3866     onMouseOver : function(e){
3867         var t  = this.findTargetItem(e);
3868         //Roo.log(t);
3869         //if(t){
3870         //    if(t.canActivate && !t.disabled){
3871         //        this.setActiveItem(t, true);
3872         //    }
3873         //}
3874         
3875         this.fireEvent("mouseover", this, e, t);
3876     },
3877     isVisible : function(){
3878         return !this.hidden;
3879     },
3880     onMouseOut : function(e){
3881         var t  = this.findTargetItem(e);
3882         
3883         //if(t ){
3884         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3885         //        this.activeItem.deactivate();
3886         //        delete this.activeItem;
3887         //    }
3888         //}
3889         this.fireEvent("mouseout", this, e, t);
3890     },
3891     
3892     
3893     /**
3894      * Displays this menu relative to another element
3895      * @param {String/HTMLElement/Roo.Element} element The element to align to
3896      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3897      * the element (defaults to this.defaultAlign)
3898      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3899      */
3900     show : function(el, pos, parentMenu)
3901     {
3902         if (false === this.fireEvent("beforeshow", this)) {
3903             Roo.log("show canceled");
3904             return;
3905         }
3906         this.parentMenu = parentMenu;
3907         if(!this.el){
3908             this.render();
3909         }
3910         this.el.addClass('show'); // show otherwise we do not know how big we are..
3911          
3912         var xy = this.el.getAlignToXY(el, pos);
3913         
3914         // bl-tl << left align  below
3915         // tl-bl << left align 
3916         
3917         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3918             // if it goes to far to the right.. -> align left.
3919             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3920         }
3921         if(xy[0] < 0){
3922             // was left align - go right?
3923             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3924         }
3925         
3926         // goes down the bottom
3927         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3928            xy[1]  < 0 ){
3929             var a = this.align.replace('?', '').split('-');
3930             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3931             
3932         }
3933         
3934         this.showAt(  xy , parentMenu, false);
3935     },
3936      /**
3937      * Displays this menu at a specific xy position
3938      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3939      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3940      */
3941     showAt : function(xy, parentMenu, /* private: */_e){
3942         this.parentMenu = parentMenu;
3943         if(!this.el){
3944             this.render();
3945         }
3946         if(_e !== false){
3947             this.fireEvent("beforeshow", this);
3948             //xy = this.el.adjustForConstraints(xy);
3949         }
3950         
3951         //this.el.show();
3952         this.hideMenuItems();
3953         this.hidden = false;
3954         if (this.triggerEl) {
3955             this.triggerEl.addClass('open');
3956         }
3957         
3958         this.el.addClass('show');
3959         
3960         
3961         
3962         // reassign x when hitting right
3963         
3964         // reassign y when hitting bottom
3965         
3966         // but the list may align on trigger left or trigger top... should it be a properity?
3967         
3968         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3969             this.el.setXY(xy);
3970         }
3971         
3972         this.focus();
3973         this.fireEvent("show", this);
3974     },
3975     
3976     focus : function(){
3977         return;
3978         if(!this.hidden){
3979             this.doFocus.defer(50, this);
3980         }
3981     },
3982
3983     doFocus : function(){
3984         if(!this.hidden){
3985             this.focusEl.focus();
3986         }
3987     },
3988
3989     /**
3990      * Hides this menu and optionally all parent menus
3991      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3992      */
3993     hide : function(deep)
3994     {
3995         if (false === this.fireEvent("beforehide", this)) {
3996             Roo.log("hide canceled");
3997             return;
3998         }
3999         this.hideMenuItems();
4000         if(this.el && this.isVisible()){
4001            
4002             if(this.activeItem){
4003                 this.activeItem.deactivate();
4004                 this.activeItem = null;
4005             }
4006             if (this.triggerEl) {
4007                 this.triggerEl.removeClass('open');
4008             }
4009             
4010             this.el.removeClass('show');
4011             this.hidden = true;
4012             this.fireEvent("hide", this);
4013         }
4014         if(deep === true && this.parentMenu){
4015             this.parentMenu.hide(true);
4016         }
4017     },
4018     
4019     onTriggerClick : function(e)
4020     {
4021         Roo.log('trigger click');
4022         
4023         var target = e.getTarget();
4024         
4025         Roo.log(target.nodeName.toLowerCase());
4026         
4027         if(target.nodeName.toLowerCase() === 'i'){
4028             e.preventDefault();
4029         }
4030         
4031     },
4032     
4033     onTriggerPress  : function(e)
4034     {
4035         Roo.log('trigger press');
4036         //Roo.log(e.getTarget());
4037        // Roo.log(this.triggerEl.dom);
4038        
4039         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4040         var pel = Roo.get(e.getTarget());
4041         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4042             Roo.log('is treeview or dropdown?');
4043             return;
4044         }
4045         
4046         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4047             return;
4048         }
4049         
4050         if (this.isVisible()) {
4051             Roo.log('hide');
4052             this.hide();
4053         } else {
4054             Roo.log('show');
4055             
4056             this.show(this.triggerEl, this.align, false);
4057         }
4058         
4059         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4060             e.stopEvent();
4061         }
4062         
4063     },
4064        
4065     
4066     hideMenuItems : function()
4067     {
4068         Roo.log("hide Menu Items");
4069         if (!this.el) { 
4070             return;
4071         }
4072         
4073         this.el.select('.open',true).each(function(aa) {
4074             
4075             aa.removeClass('open');
4076          
4077         });
4078     },
4079     addxtypeChild : function (tree, cntr) {
4080         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4081           
4082         this.menuitems.add(comp);
4083         return comp;
4084
4085     },
4086     getEl : function()
4087     {
4088         Roo.log(this.el);
4089         return this.el;
4090     },
4091     
4092     clear : function()
4093     {
4094         this.getEl().dom.innerHTML = '';
4095         this.menuitems.clear();
4096     }
4097 });
4098
4099  
4100  /**
4101  * @class Roo.bootstrap.menu.Item
4102  * @extends Roo.bootstrap.Component
4103  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4104  * @parent Roo.bootstrap.menu.Menu
4105  * @licence LGPL
4106  * Bootstrap MenuItem class
4107  * 
4108  * @cfg {String} html the menu label
4109  * @cfg {String} href the link
4110  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4111  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4112  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4113  * @cfg {String} fa favicon to show on left of menu item.
4114  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4115  * 
4116  * 
4117  * @constructor
4118  * Create a new MenuItem
4119  * @param {Object} config The config object
4120  */
4121
4122
4123 Roo.bootstrap.menu.Item = function(config){
4124     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4125     this.addEvents({
4126         // raw events
4127         /**
4128          * @event click
4129          * The raw click event for the entire grid.
4130          * @param {Roo.bootstrap.menu.Item} this
4131          * @param {Roo.EventObject} e
4132          */
4133         "click" : true
4134     });
4135 };
4136
4137 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4138     
4139     href : false,
4140     html : false,
4141     preventDefault: false,
4142     isContainer : false,
4143     active : false,
4144     fa: false,
4145     
4146     getAutoCreate : function(){
4147         
4148         if(this.isContainer){
4149             return {
4150                 tag: 'li',
4151                 cls: 'dropdown-menu-item '
4152             };
4153         }
4154         var ctag = {
4155             tag: 'span',
4156             html: 'Link'
4157         };
4158         
4159         var anc = {
4160             tag : 'a',
4161             cls : 'dropdown-item',
4162             href : '#',
4163             cn : [  ]
4164         };
4165         
4166         if (this.fa !== false) {
4167             anc.cn.push({
4168                 tag : 'i',
4169                 cls : 'fa fa-' + this.fa
4170             });
4171         }
4172         
4173         anc.cn.push(ctag);
4174         
4175         
4176         var cfg= {
4177             tag: 'li',
4178             cls: 'dropdown-menu-item',
4179             cn: [ anc ]
4180         };
4181         if (this.parent().type == 'treeview') {
4182             cfg.cls = 'treeview-menu';
4183         }
4184         if (this.active) {
4185             cfg.cls += ' active';
4186         }
4187         
4188         
4189         
4190         anc.href = this.href || cfg.cn[0].href ;
4191         ctag.html = this.html || cfg.cn[0].html ;
4192         return cfg;
4193     },
4194     
4195     initEvents: function()
4196     {
4197         if (this.parent().type == 'treeview') {
4198             this.el.select('a').on('click', this.onClick, this);
4199         }
4200         
4201         if (this.menu) {
4202             this.menu.parentType = this.xtype;
4203             this.menu.triggerEl = this.el;
4204             this.menu = this.addxtype(Roo.apply({}, this.menu));
4205         }
4206         
4207     },
4208     onClick : function(e)
4209     {
4210         //Roo.log('item on click ');
4211         
4212         if(this.href === false || this.preventDefault){
4213             e.preventDefault();
4214         }
4215         //this.parent().hideMenuItems();
4216         
4217         this.fireEvent('click', this, e);
4218     },
4219     getEl : function()
4220     {
4221         return this.el;
4222     } 
4223 });
4224
4225  
4226
4227  
4228
4229   
4230 /**
4231  * @class Roo.bootstrap.menu.Separator
4232  * @extends Roo.bootstrap.Component
4233  * @licence LGPL
4234  * @parent Roo.bootstrap.menu.Menu
4235  * Bootstrap Separator class
4236  * 
4237  * @constructor
4238  * Create a new Separator
4239  * @param {Object} config The config object
4240  */
4241
4242
4243 Roo.bootstrap.menu.Separator = function(config){
4244     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4245 };
4246
4247 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4248     
4249     getAutoCreate : function(){
4250         var cfg = {
4251             tag : 'li',
4252             cls: 'dropdown-divider divider'
4253         };
4254         
4255         return cfg;
4256     }
4257    
4258 });
4259
4260  
4261
4262  
4263 /*
4264 * Licence: LGPL
4265 */
4266
4267 /**
4268  * @class Roo.bootstrap.Modal
4269  * @extends Roo.bootstrap.Component
4270  * @parent none builder
4271  * @children Roo.bootstrap.Component
4272  * Bootstrap Modal class
4273  * @cfg {String} title Title of dialog
4274  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4275  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4276  * @cfg {Boolean} specificTitle default false
4277  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4278  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4279  * @cfg {Boolean} animate default true
4280  * @cfg {Boolean} allow_close default true
4281  * @cfg {Boolean} fitwindow default false
4282  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4283  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4284  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4285  * @cfg {String} size (sm|lg|xl) default empty
4286  * @cfg {Number} max_width set the max width of modal
4287  * @cfg {Boolean} editableTitle can the title be edited
4288
4289  *
4290  *
4291  * @constructor
4292  * Create a new Modal Dialog
4293  * @param {Object} config The config object
4294  */
4295
4296 Roo.bootstrap.Modal = function(config){
4297     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4298     this.addEvents({
4299         // raw events
4300         /**
4301          * @event btnclick
4302          * The raw btnclick event for the button
4303          * @param {Roo.EventObject} e
4304          */
4305         "btnclick" : true,
4306         /**
4307          * @event resize
4308          * Fire when dialog resize
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} e
4311          */
4312         "resize" : true,
4313         /**
4314          * @event titlechanged
4315          * Fire when the editable title has been changed
4316          * @param {Roo.bootstrap.Modal} this
4317          * @param {Roo.EventObject} value
4318          */
4319         "titlechanged" : true 
4320         
4321     });
4322     this.buttons = this.buttons || [];
4323
4324     if (this.tmpl) {
4325         this.tmpl = Roo.factory(this.tmpl);
4326     }
4327
4328 };
4329
4330 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4331
4332     title : 'test dialog',
4333
4334     buttons : false,
4335
4336     // set on load...
4337
4338     html: false,
4339
4340     tmp: false,
4341
4342     specificTitle: false,
4343
4344     buttonPosition: 'right',
4345
4346     allow_close : true,
4347
4348     animate : true,
4349
4350     fitwindow: false,
4351     
4352      // private
4353     dialogEl: false,
4354     bodyEl:  false,
4355     footerEl:  false,
4356     titleEl:  false,
4357     closeEl:  false,
4358
4359     size: '',
4360     
4361     max_width: 0,
4362     
4363     max_height: 0,
4364     
4365     fit_content: false,
4366     editableTitle  : false,
4367
4368     onRender : function(ct, position)
4369     {
4370         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4371
4372         if(!this.el){
4373             var cfg = Roo.apply({},  this.getAutoCreate());
4374             cfg.id = Roo.id();
4375             //if(!cfg.name){
4376             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4377             //}
4378             //if (!cfg.name.length) {
4379             //    delete cfg.name;
4380            // }
4381             if (this.cls) {
4382                 cfg.cls += ' ' + this.cls;
4383             }
4384             if (this.style) {
4385                 cfg.style = this.style;
4386             }
4387             this.el = Roo.get(document.body).createChild(cfg, position);
4388         }
4389         //var type = this.el.dom.type;
4390
4391
4392         if(this.tabIndex !== undefined){
4393             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4394         }
4395
4396         this.dialogEl = this.el.select('.modal-dialog',true).first();
4397         this.bodyEl = this.el.select('.modal-body',true).first();
4398         this.closeEl = this.el.select('.modal-header .close', true).first();
4399         this.headerEl = this.el.select('.modal-header',true).first();
4400         this.titleEl = this.el.select('.modal-title',true).first();
4401         this.footerEl = this.el.select('.modal-footer',true).first();
4402
4403         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4404         
4405         //this.el.addClass("x-dlg-modal");
4406
4407         if (this.buttons.length) {
4408             Roo.each(this.buttons, function(bb) {
4409                 var b = Roo.apply({}, bb);
4410                 b.xns = b.xns || Roo.bootstrap;
4411                 b.xtype = b.xtype || 'Button';
4412                 if (typeof(b.listeners) == 'undefined') {
4413                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4414                 }
4415
4416                 var btn = Roo.factory(b);
4417
4418                 btn.render(this.getButtonContainer());
4419
4420             },this);
4421         }
4422         // render the children.
4423         var nitems = [];
4424
4425         if(typeof(this.items) != 'undefined'){
4426             var items = this.items;
4427             delete this.items;
4428
4429             for(var i =0;i < items.length;i++) {
4430                 // we force children not to montor widnow resize  - as we do that for them.
4431                 items[i].monitorWindowResize = false;
4432                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4433             }
4434         }
4435
4436         this.items = nitems;
4437
4438         // where are these used - they used to be body/close/footer
4439
4440
4441         this.initEvents();
4442         //this.el.addClass([this.fieldClass, this.cls]);
4443
4444     },
4445
4446     getAutoCreate : function()
4447     {
4448         // we will default to modal-body-overflow - might need to remove or make optional later.
4449         var bdy = {
4450                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4451                 html : this.html || ''
4452         };
4453
4454         var title = {
4455             tag: 'h5',
4456             cls : 'modal-title',
4457             html : this.title
4458         };
4459
4460         if(this.specificTitle){ // WTF is this?
4461             title = this.title;
4462         }
4463
4464         var header = [];
4465         if (this.allow_close && Roo.bootstrap.version == 3) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472
4473         header.push(title);
4474
4475         if (this.editableTitle) {
4476             header.push({
4477                 cls: 'form-control roo-editable-title d-none',
4478                 tag: 'input',
4479                 type: 'text'
4480             });
4481         }
4482         
4483         if (this.allow_close && Roo.bootstrap.version == 4) {
4484             header.push({
4485                 tag: 'button',
4486                 cls : 'close',
4487                 html : '&times'
4488             });
4489         }
4490         
4491         var size = '';
4492
4493         if(this.size.length){
4494             size = 'modal-' + this.size;
4495         }
4496         
4497         var footer = Roo.bootstrap.version == 3 ?
4498             {
4499                 cls : 'modal-footer',
4500                 cn : [
4501                     {
4502                         tag: 'div',
4503                         cls: 'btn-' + this.buttonPosition
4504                     }
4505                 ]
4506
4507             } :
4508             {  // BS4 uses mr-auto on left buttons....
4509                 cls : 'modal-footer'
4510             };
4511
4512             
4513
4514         
4515         
4516         var modal = {
4517             cls: "modal",
4518              cn : [
4519                 {
4520                     cls: "modal-dialog " + size,
4521                     cn : [
4522                         {
4523                             cls : "modal-content",
4524                             cn : [
4525                                 {
4526                                     cls : 'modal-header',
4527                                     cn : header
4528                                 },
4529                                 bdy,
4530                                 footer
4531                             ]
4532
4533                         }
4534                     ]
4535
4536                 }
4537             ]
4538         };
4539
4540         if(this.animate){
4541             modal.cls += ' fade';
4542         }
4543
4544         return modal;
4545
4546     },
4547     getChildContainer : function() {
4548
4549          return this.bodyEl;
4550
4551     },
4552     getButtonContainer : function() {
4553         
4554          return Roo.bootstrap.version == 4 ?
4555             this.el.select('.modal-footer',true).first()
4556             : this.el.select('.modal-footer div',true).first();
4557
4558     },
4559     
4560     closeClick : function()
4561     {
4562         this.hide();
4563     },
4564     
4565     initEvents : function()
4566     {
4567         if (this.allow_close) {
4568             this.closeEl.on('click', this.closeClick, this);
4569         }
4570         Roo.EventManager.onWindowResize(this.resize, this, true);
4571         if (this.editableTitle) {
4572             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4573             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4574             this.headerEditEl.on('keyup', function(e) {
4575                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4576                         this.toggleHeaderInput(false)
4577                     }
4578                 }, this);
4579             this.headerEditEl.on('blur', function(e) {
4580                 this.toggleHeaderInput(false)
4581             },this);
4582         }
4583
4584     },
4585   
4586
4587     resize : function()
4588     {
4589         this.maskEl.setSize(
4590             Roo.lib.Dom.getViewWidth(true),
4591             Roo.lib.Dom.getViewHeight(true)
4592         );
4593         
4594         if (this.fitwindow) {
4595             
4596            this.dialogEl.setStyle( { 'max-width' : '100%' });
4597             this.setSize(
4598                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4599                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4600             );
4601             return;
4602         }
4603         
4604         if(this.max_width !== 0) {
4605             
4606             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4607             
4608             if(this.height) {
4609                 this.setSize(w, this.height);
4610                 return;
4611             }
4612             
4613             if(this.max_height) {
4614                 this.setSize(w,Math.min(
4615                     this.max_height,
4616                     Roo.lib.Dom.getViewportHeight(true) - 60
4617                 ));
4618                 
4619                 return;
4620             }
4621             
4622             if(!this.fit_content) {
4623                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4624                 return;
4625             }
4626             
4627             this.setSize(w, Math.min(
4628                 60 +
4629                 this.headerEl.getHeight() + 
4630                 this.footerEl.getHeight() + 
4631                 this.getChildHeight(this.bodyEl.dom.childNodes),
4632                 Roo.lib.Dom.getViewportHeight(true) - 60)
4633             );
4634         }
4635         
4636     },
4637
4638     setSize : function(w,h)
4639     {
4640         if (!w && !h) {
4641             return;
4642         }
4643         
4644         this.resizeTo(w,h);
4645         // any layout/border etc.. resize..
4646         (function () {
4647             this.items.forEach( function(e) {
4648                 e.layout ? e.layout() : false;
4649
4650             });
4651         }).defer(100,this);
4652         
4653     },
4654
4655     show : function() {
4656
4657         if (!this.rendered) {
4658             this.render();
4659         }
4660         this.toggleHeaderInput(false);
4661         //this.el.setStyle('display', 'block');
4662         this.el.removeClass('hideing');
4663         this.el.dom.style.display='block';
4664         
4665         Roo.get(document.body).addClass('modal-open');
4666  
4667         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4668             
4669             (function(){
4670                 this.el.addClass('show');
4671                 this.el.addClass('in');
4672             }).defer(50, this);
4673         }else{
4674             this.el.addClass('show');
4675             this.el.addClass('in');
4676         }
4677
4678         // not sure how we can show data in here..
4679         //if (this.tmpl) {
4680         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4681         //}
4682
4683         Roo.get(document.body).addClass("x-body-masked");
4684         
4685         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4686         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4687         this.maskEl.dom.style.display = 'block';
4688         this.maskEl.addClass('show');
4689         
4690         
4691         this.resize();
4692         
4693         this.fireEvent('show', this);
4694
4695         // set zindex here - otherwise it appears to be ignored...
4696         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4697         
4698         
4699         // this is for children that are... layout.Border 
4700         (function () {
4701             this.items.forEach( function(e) {
4702                 e.layout ? e.layout() : false;
4703
4704             });
4705         }).defer(100,this);
4706
4707     },
4708     hide : function()
4709     {
4710         if(this.fireEvent("beforehide", this) !== false){
4711             
4712             this.maskEl.removeClass('show');
4713             
4714             this.maskEl.dom.style.display = '';
4715             Roo.get(document.body).removeClass("x-body-masked");
4716             this.el.removeClass('in');
4717             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4718
4719             if(this.animate){ // why
4720                 this.el.addClass('hideing');
4721                 this.el.removeClass('show');
4722                 (function(){
4723                     if (!this.el.hasClass('hideing')) {
4724                         return; // it's been shown again...
4725                     }
4726                     
4727                     this.el.dom.style.display='';
4728
4729                     Roo.get(document.body).removeClass('modal-open');
4730                     this.el.removeClass('hideing');
4731                 }).defer(150,this);
4732                 
4733             }else{
4734                 this.el.removeClass('show');
4735                 this.el.dom.style.display='';
4736                 Roo.get(document.body).removeClass('modal-open');
4737
4738             }
4739             this.fireEvent('hide', this);
4740         }
4741     },
4742     isVisible : function()
4743     {
4744         
4745         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4746         
4747     },
4748
4749     addButton : function(str, cb)
4750     {
4751
4752
4753         var b = Roo.apply({}, { html : str } );
4754         b.xns = b.xns || Roo.bootstrap;
4755         b.xtype = b.xtype || 'Button';
4756         if (typeof(b.listeners) == 'undefined') {
4757             b.listeners = { click : cb.createDelegate(this)  };
4758         }
4759
4760         var btn = Roo.factory(b);
4761
4762         btn.render(this.getButtonContainer());
4763
4764         return btn;
4765
4766     },
4767
4768     setDefaultButton : function(btn)
4769     {
4770         //this.el.select('.modal-footer').()
4771     },
4772
4773     resizeTo: function(w,h)
4774     {
4775         this.dialogEl.setWidth(w);
4776         
4777         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4778
4779         this.bodyEl.setHeight(h - diff);
4780         
4781         this.fireEvent('resize', this);
4782     },
4783     
4784     setContentSize  : function(w, h)
4785     {
4786
4787     },
4788     onButtonClick: function(btn,e)
4789     {
4790         //Roo.log([a,b,c]);
4791         this.fireEvent('btnclick', btn.name, e);
4792     },
4793      /**
4794      * Set the title of the Dialog
4795      * @param {String} str new Title
4796      */
4797     setTitle: function(str) {
4798         this.titleEl.dom.innerHTML = str;
4799         this.title = str;
4800     },
4801     /**
4802      * Set the body of the Dialog
4803      * @param {String} str new Title
4804      */
4805     setBody: function(str) {
4806         this.bodyEl.dom.innerHTML = str;
4807     },
4808     /**
4809      * Set the body of the Dialog using the template
4810      * @param {Obj} data - apply this data to the template and replace the body contents.
4811      */
4812     applyBody: function(obj)
4813     {
4814         if (!this.tmpl) {
4815             Roo.log("Error - using apply Body without a template");
4816             //code
4817         }
4818         this.tmpl.overwrite(this.bodyEl, obj);
4819     },
4820     
4821     getChildHeight : function(child_nodes)
4822     {
4823         if(
4824             !child_nodes ||
4825             child_nodes.length == 0
4826         ) {
4827             return 0;
4828         }
4829         
4830         var child_height = 0;
4831         
4832         for(var i = 0; i < child_nodes.length; i++) {
4833             
4834             /*
4835             * for modal with tabs...
4836             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4837                 
4838                 var layout_childs = child_nodes[i].childNodes;
4839                 
4840                 for(var j = 0; j < layout_childs.length; j++) {
4841                     
4842                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4843                         
4844                         var layout_body_childs = layout_childs[j].childNodes;
4845                         
4846                         for(var k = 0; k < layout_body_childs.length; k++) {
4847                             
4848                             if(layout_body_childs[k].classList.contains('navbar')) {
4849                                 child_height += layout_body_childs[k].offsetHeight;
4850                                 continue;
4851                             }
4852                             
4853                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4854                                 
4855                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4856                                 
4857                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4858                                     
4859                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4860                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4861                                         continue;
4862                                     }
4863                                     
4864                                 }
4865                                 
4866                             }
4867                             
4868                         }
4869                     }
4870                 }
4871                 continue;
4872             }
4873             */
4874             
4875             child_height += child_nodes[i].offsetHeight;
4876             // Roo.log(child_nodes[i].offsetHeight);
4877         }
4878         
4879         return child_height;
4880     },
4881     toggleHeaderInput : function(is_edit)
4882     {
4883         if (!this.editableTitle) {
4884             return; // not editable.
4885         }
4886         if (is_edit && this.is_header_editing) {
4887             return; // already editing..
4888         }
4889         if (is_edit) {
4890     
4891             this.headerEditEl.dom.value = this.title;
4892             this.headerEditEl.removeClass('d-none');
4893             this.headerEditEl.dom.focus();
4894             this.titleEl.addClass('d-none');
4895             
4896             this.is_header_editing = true;
4897             return
4898         }
4899         // flip back to not editing.
4900         this.title = this.headerEditEl.dom.value;
4901         this.headerEditEl.addClass('d-none');
4902         this.titleEl.removeClass('d-none');
4903         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4904         this.is_header_editing = false;
4905         this.fireEvent('titlechanged', this, this.title);
4906     
4907             
4908         
4909     }
4910
4911 });
4912
4913
4914 Roo.apply(Roo.bootstrap.Modal,  {
4915     /**
4916          * Button config that displays a single OK button
4917          * @type Object
4918          */
4919         OK :  [{
4920             name : 'ok',
4921             weight : 'primary',
4922             html : 'OK'
4923         }],
4924         /**
4925          * Button config that displays Yes and No buttons
4926          * @type Object
4927          */
4928         YESNO : [
4929             {
4930                 name  : 'no',
4931                 html : 'No'
4932             },
4933             {
4934                 name  :'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             }
4938         ],
4939
4940         /**
4941          * Button config that displays OK and Cancel buttons
4942          * @type Object
4943          */
4944         OKCANCEL : [
4945             {
4946                name : 'cancel',
4947                 html : 'Cancel'
4948             },
4949             {
4950                 name : 'ok',
4951                 weight : 'primary',
4952                 html : 'OK'
4953             }
4954         ],
4955         /**
4956          * Button config that displays Yes, No and Cancel buttons
4957          * @type Object
4958          */
4959         YESNOCANCEL : [
4960             {
4961                 name : 'yes',
4962                 weight : 'primary',
4963                 html : 'Yes'
4964             },
4965             {
4966                 name : 'no',
4967                 html : 'No'
4968             },
4969             {
4970                 name : 'cancel',
4971                 html : 'Cancel'
4972             }
4973         ],
4974         
4975         zIndex : 10001
4976 });
4977
4978 /*
4979  * - LGPL
4980  *
4981  * messagebox - can be used as a replace
4982  * 
4983  */
4984 /**
4985  * @class Roo.MessageBox
4986  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4987  * Example usage:
4988  *<pre><code>
4989 // Basic alert:
4990 Roo.Msg.alert('Status', 'Changes saved successfully.');
4991
4992 // Prompt for user data:
4993 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4994     if (btn == 'ok'){
4995         // process text value...
4996     }
4997 });
4998
4999 // Show a dialog using config options:
5000 Roo.Msg.show({
5001    title:'Save Changes?',
5002    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5003    buttons: Roo.Msg.YESNOCANCEL,
5004    fn: processResult,
5005    animEl: 'elId'
5006 });
5007 </code></pre>
5008  * @static
5009  */
5010 Roo.bootstrap.MessageBox = function(){
5011     var dlg, opt, mask, waitTimer;
5012     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5013     var buttons, activeTextEl, bwidth;
5014
5015     
5016     // private
5017     var handleButton = function(button){
5018         dlg.hide();
5019         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5020     };
5021
5022     // private
5023     var handleHide = function(){
5024         if(opt && opt.cls){
5025             dlg.el.removeClass(opt.cls);
5026         }
5027         //if(waitTimer){
5028         //    Roo.TaskMgr.stop(waitTimer);
5029         //    waitTimer = null;
5030         //}
5031     };
5032
5033     // private
5034     var updateButtons = function(b){
5035         var width = 0;
5036         if(!b){
5037             buttons["ok"].hide();
5038             buttons["cancel"].hide();
5039             buttons["yes"].hide();
5040             buttons["no"].hide();
5041             dlg.footerEl.hide();
5042             
5043             return width;
5044         }
5045         dlg.footerEl.show();
5046         for(var k in buttons){
5047             if(typeof buttons[k] != "function"){
5048                 if(b[k]){
5049                     buttons[k].show();
5050                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5051                     width += buttons[k].el.getWidth()+15;
5052                 }else{
5053                     buttons[k].hide();
5054                 }
5055             }
5056         }
5057         return width;
5058     };
5059
5060     // private
5061     var handleEsc = function(d, k, e){
5062         if(opt && opt.closable !== false){
5063             dlg.hide();
5064         }
5065         if(e){
5066             e.stopEvent();
5067         }
5068     };
5069
5070     return {
5071         /**
5072          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5073          * @return {Roo.BasicDialog} The BasicDialog element
5074          */
5075         getDialog : function(){
5076            if(!dlg){
5077                 dlg = new Roo.bootstrap.Modal( {
5078                     //draggable: true,
5079                     //resizable:false,
5080                     //constraintoviewport:false,
5081                     //fixedcenter:true,
5082                     //collapsible : false,
5083                     //shim:true,
5084                     //modal: true,
5085                 //    width: 'auto',
5086                   //  height:100,
5087                     //buttonAlign:"center",
5088                     closeClick : function(){
5089                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5090                             handleButton("no");
5091                         }else{
5092                             handleButton("cancel");
5093                         }
5094                     }
5095                 });
5096                 dlg.render();
5097                 dlg.on("hide", handleHide);
5098                 mask = dlg.mask;
5099                 //dlg.addKeyListener(27, handleEsc);
5100                 buttons = {};
5101                 this.buttons = buttons;
5102                 var bt = this.buttonText;
5103                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5104                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5105                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5106                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5107                 //Roo.log(buttons);
5108                 bodyEl = dlg.bodyEl.createChild({
5109
5110                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5111                         '<textarea class="roo-mb-textarea"></textarea>' +
5112                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5113                 });
5114                 msgEl = bodyEl.dom.firstChild;
5115                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5116                 textboxEl.enableDisplayMode();
5117                 textboxEl.addKeyListener([10,13], function(){
5118                     if(dlg.isVisible() && opt && opt.buttons){
5119                         if(opt.buttons.ok){
5120                             handleButton("ok");
5121                         }else if(opt.buttons.yes){
5122                             handleButton("yes");
5123                         }
5124                     }
5125                 });
5126                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5127                 textareaEl.enableDisplayMode();
5128                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5129                 progressEl.enableDisplayMode();
5130                 
5131                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5132                 var pf = progressEl.dom.firstChild;
5133                 if (pf) {
5134                     pp = Roo.get(pf.firstChild);
5135                     pp.setHeight(pf.offsetHeight);
5136                 }
5137                 
5138             }
5139             return dlg;
5140         },
5141
5142         /**
5143          * Updates the message box body text
5144          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5145          * the XHTML-compliant non-breaking space character '&amp;#160;')
5146          * @return {Roo.MessageBox} This message box
5147          */
5148         updateText : function(text)
5149         {
5150             if(!dlg.isVisible() && !opt.width){
5151                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5152                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5153             }
5154             msgEl.innerHTML = text || '&#160;';
5155       
5156             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5157             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5158             var w = Math.max(
5159                     Math.min(opt.width || cw , this.maxWidth), 
5160                     Math.max(opt.minWidth || this.minWidth, bwidth)
5161             );
5162             if(opt.prompt){
5163                 activeTextEl.setWidth(w);
5164             }
5165             if(dlg.isVisible()){
5166                 dlg.fixedcenter = false;
5167             }
5168             // to big, make it scroll. = But as usual stupid IE does not support
5169             // !important..
5170             
5171             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5172                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5173                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5174             } else {
5175                 bodyEl.dom.style.height = '';
5176                 bodyEl.dom.style.overflowY = '';
5177             }
5178             if (cw > w) {
5179                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5180             } else {
5181                 bodyEl.dom.style.overflowX = '';
5182             }
5183             
5184             dlg.setContentSize(w, bodyEl.getHeight());
5185             if(dlg.isVisible()){
5186                 dlg.fixedcenter = true;
5187             }
5188             return this;
5189         },
5190
5191         /**
5192          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5193          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5194          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5195          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5196          * @return {Roo.MessageBox} This message box
5197          */
5198         updateProgress : function(value, text){
5199             if(text){
5200                 this.updateText(text);
5201             }
5202             
5203             if (pp) { // weird bug on my firefox - for some reason this is not defined
5204                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5205                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5206             }
5207             return this;
5208         },        
5209
5210         /**
5211          * Returns true if the message box is currently displayed
5212          * @return {Boolean} True if the message box is visible, else false
5213          */
5214         isVisible : function(){
5215             return dlg && dlg.isVisible();  
5216         },
5217
5218         /**
5219          * Hides the message box if it is displayed
5220          */
5221         hide : function(){
5222             if(this.isVisible()){
5223                 dlg.hide();
5224             }  
5225         },
5226
5227         /**
5228          * Displays a new message box, or reinitializes an existing message box, based on the config options
5229          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5230          * The following config object properties are supported:
5231          * <pre>
5232 Property    Type             Description
5233 ----------  ---------------  ------------------------------------------------------------------------------------
5234 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5235                                    closes (defaults to undefined)
5236 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5237                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5238 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5239                                    progress and wait dialogs will ignore this property and always hide the
5240                                    close button as they can only be closed programmatically.
5241 cls               String           A custom CSS class to apply to the message box element
5242 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5243                                    displayed (defaults to 75)
5244 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5245                                    function will be btn (the name of the button that was clicked, if applicable,
5246                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5247                                    Progress and wait dialogs will ignore this option since they do not respond to
5248                                    user actions and can only be closed programmatically, so any required function
5249                                    should be called by the same code after it closes the dialog.
5250 icon              String           A CSS class that provides a background image to be used as an icon for
5251                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5252 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5253 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5254 modal             Boolean          False to allow user interaction with the page while the message box is
5255                                    displayed (defaults to true)
5256 msg               String           A string that will replace the existing message box body text (defaults
5257                                    to the XHTML-compliant non-breaking space character '&#160;')
5258 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5259 progress          Boolean          True to display a progress bar (defaults to false)
5260 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5261 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5262 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5263 title             String           The title text
5264 value             String           The string value to set into the active textbox element if displayed
5265 wait              Boolean          True to display a progress bar (defaults to false)
5266 width             Number           The width of the dialog in pixels
5267 </pre>
5268          *
5269          * Example usage:
5270          * <pre><code>
5271 Roo.Msg.show({
5272    title: 'Address',
5273    msg: 'Please enter your address:',
5274    width: 300,
5275    buttons: Roo.MessageBox.OKCANCEL,
5276    multiline: true,
5277    fn: saveAddress,
5278    animEl: 'addAddressBtn'
5279 });
5280 </code></pre>
5281          * @param {Object} config Configuration options
5282          * @return {Roo.MessageBox} This message box
5283          */
5284         show : function(options)
5285         {
5286             
5287             // this causes nightmares if you show one dialog after another
5288             // especially on callbacks..
5289              
5290             if(this.isVisible()){
5291                 
5292                 this.hide();
5293                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5294                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5295                 Roo.log("New Dialog Message:" +  options.msg )
5296                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5297                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5298                 
5299             }
5300             var d = this.getDialog();
5301             opt = options;
5302             d.setTitle(opt.title || "&#160;");
5303             d.closeEl.setDisplayed(opt.closable !== false);
5304             activeTextEl = textboxEl;
5305             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5306             if(opt.prompt){
5307                 if(opt.multiline){
5308                     textboxEl.hide();
5309                     textareaEl.show();
5310                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5311                         opt.multiline : this.defaultTextHeight);
5312                     activeTextEl = textareaEl;
5313                 }else{
5314                     textboxEl.show();
5315                     textareaEl.hide();
5316                 }
5317             }else{
5318                 textboxEl.hide();
5319                 textareaEl.hide();
5320             }
5321             progressEl.setDisplayed(opt.progress === true);
5322             if (opt.progress) {
5323                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5324             }
5325             this.updateProgress(0);
5326             activeTextEl.dom.value = opt.value || "";
5327             if(opt.prompt){
5328                 dlg.setDefaultButton(activeTextEl);
5329             }else{
5330                 var bs = opt.buttons;
5331                 var db = null;
5332                 if(bs && bs.ok){
5333                     db = buttons["ok"];
5334                 }else if(bs && bs.yes){
5335                     db = buttons["yes"];
5336                 }
5337                 dlg.setDefaultButton(db);
5338             }
5339             bwidth = updateButtons(opt.buttons);
5340             this.updateText(opt.msg);
5341             if(opt.cls){
5342                 d.el.addClass(opt.cls);
5343             }
5344             d.proxyDrag = opt.proxyDrag === true;
5345             d.modal = opt.modal !== false;
5346             d.mask = opt.modal !== false ? mask : false;
5347             if(!d.isVisible()){
5348                 // force it to the end of the z-index stack so it gets a cursor in FF
5349                 document.body.appendChild(dlg.el.dom);
5350                 d.animateTarget = null;
5351                 d.show(options.animEl);
5352             }
5353             return this;
5354         },
5355
5356         /**
5357          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5358          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5359          * and closing the message box when the process is complete.
5360          * @param {String} title The title bar text
5361          * @param {String} msg The message box body text
5362          * @return {Roo.MessageBox} This message box
5363          */
5364         progress : function(title, msg){
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: false,
5369                 progress:true,
5370                 closable:false,
5371                 minWidth: this.minProgressWidth,
5372                 modal : true
5373             });
5374             return this;
5375         },
5376
5377         /**
5378          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5379          * If a callback function is passed it will be called after the user clicks the button, and the
5380          * id of the button that was clicked will be passed as the only parameter to the callback
5381          * (could also be the top-right close button).
5382          * @param {String} title The title bar text
5383          * @param {String} msg The message box body text
5384          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5385          * @param {Object} scope (optional) The scope of the callback function
5386          * @return {Roo.MessageBox} This message box
5387          */
5388         alert : function(title, msg, fn, scope)
5389         {
5390             this.show({
5391                 title : title,
5392                 msg : msg,
5393                 buttons: this.OK,
5394                 fn: fn,
5395                 closable : false,
5396                 scope : scope,
5397                 modal : true
5398             });
5399             return this;
5400         },
5401
5402         /**
5403          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5404          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5405          * You are responsible for closing the message box when the process is complete.
5406          * @param {String} msg The message box body text
5407          * @param {String} title (optional) The title bar text
5408          * @return {Roo.MessageBox} This message box
5409          */
5410         wait : function(msg, title){
5411             this.show({
5412                 title : title,
5413                 msg : msg,
5414                 buttons: false,
5415                 closable:false,
5416                 progress:true,
5417                 modal:true,
5418                 width:300,
5419                 wait:true
5420             });
5421             waitTimer = Roo.TaskMgr.start({
5422                 run: function(i){
5423                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5424                 },
5425                 interval: 1000
5426             });
5427             return this;
5428         },
5429
5430         /**
5431          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5432          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5433          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5434          * @param {String} title The title bar text
5435          * @param {String} msg The message box body text
5436          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5437          * @param {Object} scope (optional) The scope of the callback function
5438          * @return {Roo.MessageBox} This message box
5439          */
5440         confirm : function(title, msg, fn, scope){
5441             this.show({
5442                 title : title,
5443                 msg : msg,
5444                 buttons: this.YESNO,
5445                 fn: fn,
5446                 scope : scope,
5447                 modal : true
5448             });
5449             return this;
5450         },
5451
5452         /**
5453          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5454          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5455          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5456          * (could also be the top-right close button) and the text that was entered will be passed as the two
5457          * parameters to the callback.
5458          * @param {String} title The title bar text
5459          * @param {String} msg The message box body text
5460          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5461          * @param {Object} scope (optional) The scope of the callback function
5462          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5463          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5464          * @return {Roo.MessageBox} This message box
5465          */
5466         prompt : function(title, msg, fn, scope, multiline){
5467             this.show({
5468                 title : title,
5469                 msg : msg,
5470                 buttons: this.OKCANCEL,
5471                 fn: fn,
5472                 minWidth:250,
5473                 scope : scope,
5474                 prompt:true,
5475                 multiline: multiline,
5476                 modal : true
5477             });
5478             return this;
5479         },
5480
5481         /**
5482          * Button config that displays a single OK button
5483          * @type Object
5484          */
5485         OK : {ok:true},
5486         /**
5487          * Button config that displays Yes and No buttons
5488          * @type Object
5489          */
5490         YESNO : {yes:true, no:true},
5491         /**
5492          * Button config that displays OK and Cancel buttons
5493          * @type Object
5494          */
5495         OKCANCEL : {ok:true, cancel:true},
5496         /**
5497          * Button config that displays Yes, No and Cancel buttons
5498          * @type Object
5499          */
5500         YESNOCANCEL : {yes:true, no:true, cancel:true},
5501
5502         /**
5503          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5504          * @type Number
5505          */
5506         defaultTextHeight : 75,
5507         /**
5508          * The maximum width in pixels of the message box (defaults to 600)
5509          * @type Number
5510          */
5511         maxWidth : 600,
5512         /**
5513          * The minimum width in pixels of the message box (defaults to 100)
5514          * @type Number
5515          */
5516         minWidth : 100,
5517         /**
5518          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5519          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5520          * @type Number
5521          */
5522         minProgressWidth : 250,
5523         /**
5524          * An object containing the default button text strings that can be overriden for localized language support.
5525          * Supported properties are: ok, cancel, yes and no.
5526          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5527          * @type Object
5528          */
5529         buttonText : {
5530             ok : "OK",
5531             cancel : "Cancel",
5532             yes : "Yes",
5533             no : "No"
5534         }
5535     };
5536 }();
5537
5538 /**
5539  * Shorthand for {@link Roo.MessageBox}
5540  */
5541 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5542 Roo.Msg = Roo.Msg || Roo.MessageBox;
5543 /*
5544  * - LGPL
5545  *
5546  * navbar
5547  * 
5548  */
5549
5550 /**
5551  * @class Roo.bootstrap.nav.Bar
5552  * @extends Roo.bootstrap.Component
5553  * @abstract
5554  * Bootstrap Navbar class
5555
5556  * @constructor
5557  * Create a new Navbar
5558  * @param {Object} config The config object
5559  */
5560
5561
5562 Roo.bootstrap.nav.Bar = function(config){
5563     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5564     this.addEvents({
5565         // raw events
5566         /**
5567          * @event beforetoggle
5568          * Fire before toggle the menu
5569          * @param {Roo.EventObject} e
5570          */
5571         "beforetoggle" : true
5572     });
5573 };
5574
5575 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5576     
5577     
5578    
5579     // private
5580     navItems : false,
5581     loadMask : false,
5582     
5583     
5584     getAutoCreate : function(){
5585         
5586         
5587         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5588         
5589     },
5590     
5591     initEvents :function ()
5592     {
5593         //Roo.log(this.el.select('.navbar-toggle',true));
5594         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5595         
5596         var mark = {
5597             tag: "div",
5598             cls:"x-dlg-mask"
5599         };
5600         
5601         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5602         
5603         var size = this.el.getSize();
5604         this.maskEl.setSize(size.width, size.height);
5605         this.maskEl.enableDisplayMode("block");
5606         this.maskEl.hide();
5607         
5608         if(this.loadMask){
5609             this.maskEl.show();
5610         }
5611     },
5612     
5613     
5614     getChildContainer : function()
5615     {
5616         if (this.el && this.el.select('.collapse').getCount()) {
5617             return this.el.select('.collapse',true).first();
5618         }
5619         
5620         return this.el;
5621     },
5622     
5623     mask : function()
5624     {
5625         this.maskEl.show();
5626     },
5627     
5628     unmask : function()
5629     {
5630         this.maskEl.hide();
5631     },
5632     onToggle : function()
5633     {
5634         
5635         if(this.fireEvent('beforetoggle', this) === false){
5636             return;
5637         }
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639       
5640         if (!ce.hasClass('show')) {
5641            this.expand();
5642         } else {
5643             this.collapse();
5644         }
5645         
5646         
5647     
5648     },
5649     /**
5650      * Expand the navbar pulldown 
5651      */
5652     expand : function ()
5653     {
5654        
5655         var ce = this.el.select('.navbar-collapse',true).first();
5656         if (ce.hasClass('collapsing')) {
5657             return;
5658         }
5659         ce.dom.style.height = '';
5660                // show it...
5661         ce.addClass('in'); // old...
5662         ce.removeClass('collapse');
5663         ce.addClass('show');
5664         var h = ce.getHeight();
5665         Roo.log(h);
5666         ce.removeClass('show');
5667         // at this point we should be able to see it..
5668         ce.addClass('collapsing');
5669         
5670         ce.setHeight(0); // resize it ...
5671         ce.on('transitionend', function() {
5672             //Roo.log('done transition');
5673             ce.removeClass('collapsing');
5674             ce.addClass('show');
5675             ce.removeClass('collapse');
5676
5677             ce.dom.style.height = '';
5678         }, this, { single: true} );
5679         ce.setHeight(h);
5680         ce.dom.scrollTop = 0;
5681     },
5682     /**
5683      * Collapse the navbar pulldown 
5684      */
5685     collapse : function()
5686     {
5687          var ce = this.el.select('.navbar-collapse',true).first();
5688        
5689         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5690             // it's collapsed or collapsing..
5691             return;
5692         }
5693         ce.removeClass('in'); // old...
5694         ce.setHeight(ce.getHeight());
5695         ce.removeClass('show');
5696         ce.addClass('collapsing');
5697         
5698         ce.on('transitionend', function() {
5699             ce.dom.style.height = '';
5700             ce.removeClass('collapsing');
5701             ce.addClass('collapse');
5702         }, this, { single: true} );
5703         ce.setHeight(0);
5704     }
5705     
5706     
5707     
5708 });
5709
5710
5711
5712  
5713
5714  /*
5715  * - LGPL
5716  *
5717  * navbar
5718  * 
5719  */
5720
5721 /**
5722  * @class Roo.bootstrap.nav.Simplebar
5723  * @extends Roo.bootstrap.nav.Bar
5724  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5725  * Bootstrap Sidebar class
5726  *
5727  * @cfg {Boolean} inverse is inverted color
5728  * 
5729  * @cfg {String} type (nav | pills | tabs)
5730  * @cfg {Boolean} arrangement stacked | justified
5731  * @cfg {String} align (left | right) alignment
5732  * 
5733  * @cfg {Boolean} main (true|false) main nav bar? default false
5734  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5735  * 
5736  * @cfg {String} tag (header|footer|nav|div) default is nav 
5737
5738  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5739  * 
5740  * 
5741  * @constructor
5742  * Create a new Sidebar
5743  * @param {Object} config The config object
5744  */
5745
5746
5747 Roo.bootstrap.nav.Simplebar = function(config){
5748     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5749 };
5750
5751 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5752     
5753     inverse: false,
5754     
5755     type: false,
5756     arrangement: '',
5757     align : false,
5758     
5759     weight : 'light',
5760     
5761     main : false,
5762     
5763     
5764     tag : false,
5765     
5766     
5767     getAutoCreate : function(){
5768         
5769         
5770         var cfg = {
5771             tag : this.tag || 'div',
5772             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5773         };
5774         if (['light','white'].indexOf(this.weight) > -1) {
5775             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5776         }
5777         cfg.cls += ' bg-' + this.weight;
5778         
5779         if (this.inverse) {
5780             cfg.cls += ' navbar-inverse';
5781             
5782         }
5783         
5784         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5785         
5786         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5787             return cfg;
5788         }
5789         
5790         
5791     
5792         
5793         cfg.cn = [
5794             {
5795                 cls: 'nav nav-' + this.xtype,
5796                 tag : 'ul'
5797             }
5798         ];
5799         
5800          
5801         this.type = this.type || 'nav';
5802         if (['tabs','pills'].indexOf(this.type) != -1) {
5803             cfg.cn[0].cls += ' nav-' + this.type
5804         
5805         
5806         } else {
5807             if (this.type!=='nav') {
5808                 Roo.log('nav type must be nav/tabs/pills')
5809             }
5810             cfg.cn[0].cls += ' navbar-nav'
5811         }
5812         
5813         
5814         
5815         
5816         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5817             cfg.cn[0].cls += ' nav-' + this.arrangement;
5818         }
5819         
5820         
5821         if (this.align === 'right') {
5822             cfg.cn[0].cls += ' navbar-right';
5823         }
5824         
5825         
5826         
5827         
5828         return cfg;
5829     
5830         
5831     }
5832     
5833     
5834     
5835 });
5836
5837
5838
5839  
5840
5841  
5842        /*
5843  * - LGPL
5844  *
5845  * navbar
5846  * navbar-fixed-top
5847  * navbar-expand-md  fixed-top 
5848  */
5849
5850 /**
5851  * @class Roo.bootstrap.nav.Headerbar
5852  * @extends Roo.bootstrap.nav.Simplebar
5853  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5854  * Bootstrap Sidebar class
5855  *
5856  * @cfg {String} brand what is brand
5857  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5858  * @cfg {String} brand_href href of the brand
5859  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5860  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5861  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5862  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5863  * 
5864  * @constructor
5865  * Create a new Sidebar
5866  * @param {Object} config The config object
5867  */
5868
5869
5870 Roo.bootstrap.nav.Headerbar = function(config){
5871     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5872       
5873 };
5874
5875 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5876     
5877     position: '',
5878     brand: '',
5879     brand_href: false,
5880     srButton : true,
5881     autohide : false,
5882     desktopCenter : false,
5883    
5884     
5885     getAutoCreate : function(){
5886         
5887         var   cfg = {
5888             tag: this.nav || 'nav',
5889             cls: 'navbar navbar-expand-md',
5890             role: 'navigation',
5891             cn: []
5892         };
5893         
5894         var cn = cfg.cn;
5895         if (this.desktopCenter) {
5896             cn.push({cls : 'container', cn : []});
5897             cn = cn[0].cn;
5898         }
5899         
5900         if(this.srButton){
5901             var btn = {
5902                 tag: 'button',
5903                 type: 'button',
5904                 cls: 'navbar-toggle navbar-toggler',
5905                 'data-toggle': 'collapse',
5906                 cn: [
5907                     {
5908                         tag: 'span',
5909                         cls: 'sr-only',
5910                         html: 'Toggle navigation'
5911                     },
5912                     {
5913                         tag: 'span',
5914                         cls: 'icon-bar navbar-toggler-icon'
5915                     },
5916                     {
5917                         tag: 'span',
5918                         cls: 'icon-bar'
5919                     },
5920                     {
5921                         tag: 'span',
5922                         cls: 'icon-bar'
5923                     }
5924                 ]
5925             };
5926             
5927             cn.push( Roo.bootstrap.version == 4 ? btn : {
5928                 tag: 'div',
5929                 cls: 'navbar-header',
5930                 cn: [
5931                     btn
5932                 ]
5933             });
5934         }
5935         
5936         cn.push({
5937             tag: 'div',
5938             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5939             cn : []
5940         });
5941         
5942         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5943         
5944         if (['light','white'].indexOf(this.weight) > -1) {
5945             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5946         }
5947         cfg.cls += ' bg-' + this.weight;
5948         
5949         
5950         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5951             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5952             
5953             // tag can override this..
5954             
5955             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5956         }
5957         
5958         if (this.brand !== '') {
5959             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5960             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5961                 tag: 'a',
5962                 href: this.brand_href ? this.brand_href : '#',
5963                 cls: 'navbar-brand',
5964                 cn: [
5965                 this.brand
5966                 ]
5967             });
5968         }
5969         
5970         if(this.main){
5971             cfg.cls += ' main-nav';
5972         }
5973         
5974         
5975         return cfg;
5976
5977         
5978     },
5979     getHeaderChildContainer : function()
5980     {
5981         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5982             return this.el.select('.navbar-header',true).first();
5983         }
5984         
5985         return this.getChildContainer();
5986     },
5987     
5988     getChildContainer : function()
5989     {
5990          
5991         return this.el.select('.roo-navbar-collapse',true).first();
5992          
5993         
5994     },
5995     
5996     initEvents : function()
5997     {
5998         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5999         
6000         if (this.autohide) {
6001             
6002             var prevScroll = 0;
6003             var ft = this.el;
6004             
6005             Roo.get(document).on('scroll',function(e) {
6006                 var ns = Roo.get(document).getScroll().top;
6007                 var os = prevScroll;
6008                 prevScroll = ns;
6009                 
6010                 if(ns > os){
6011                     ft.removeClass('slideDown');
6012                     ft.addClass('slideUp');
6013                     return;
6014                 }
6015                 ft.removeClass('slideUp');
6016                 ft.addClass('slideDown');
6017                  
6018               
6019           },this);
6020         }
6021     }    
6022     
6023 });
6024
6025
6026
6027  
6028
6029  /*
6030  * - LGPL
6031  *
6032  * navbar
6033  * 
6034  */
6035
6036 /**
6037  * @class Roo.bootstrap.nav.Sidebar
6038  * @extends Roo.bootstrap.nav.Bar
6039  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6040  * Bootstrap Sidebar class
6041  * 
6042  * @constructor
6043  * Create a new Sidebar
6044  * @param {Object} config The config object
6045  */
6046
6047
6048 Roo.bootstrap.nav.Sidebar = function(config){
6049     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6050 };
6051
6052 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6053     
6054     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6055     
6056     getAutoCreate : function(){
6057         
6058         
6059         return  {
6060             tag: 'div',
6061             cls: 'sidebar sidebar-nav'
6062         };
6063     
6064         
6065     }
6066     
6067     
6068     
6069 });
6070
6071
6072
6073  
6074
6075  /*
6076  * - LGPL
6077  *
6078  * nav group
6079  * 
6080  */
6081
6082 /**
6083  * @class Roo.bootstrap.nav.Group
6084  * @extends Roo.bootstrap.Component
6085  * @children Roo.bootstrap.nav.Item
6086  * Bootstrap NavGroup class
6087  * @cfg {String} align (left|right)
6088  * @cfg {Boolean} inverse
6089  * @cfg {String} type (nav|pills|tab) default nav
6090  * @cfg {String} navId - reference Id for navbar.
6091  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6092  * 
6093  * @constructor
6094  * Create a new nav group
6095  * @param {Object} config The config object
6096  */
6097
6098 Roo.bootstrap.nav.Group = function(config){
6099     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6100     this.navItems = [];
6101    
6102     Roo.bootstrap.nav.Group.register(this);
6103      this.addEvents({
6104         /**
6105              * @event changed
6106              * Fires when the active item changes
6107              * @param {Roo.bootstrap.nav.Group} this
6108              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6109              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6110          */
6111         'changed': true
6112      });
6113     
6114 };
6115
6116 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6117     
6118     align: '',
6119     inverse: false,
6120     form: false,
6121     type: 'nav',
6122     navId : '',
6123     // private
6124     pilltype : true,
6125     
6126     navItems : false, 
6127     
6128     getAutoCreate : function()
6129     {
6130         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6131         
6132         cfg = {
6133             tag : 'ul',
6134             cls: 'nav' 
6135         };
6136         if (Roo.bootstrap.version == 4) {
6137             if (['tabs','pills'].indexOf(this.type) != -1) {
6138                 cfg.cls += ' nav-' + this.type; 
6139             } else {
6140                 // trying to remove so header bar can right align top?
6141                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6142                     // do not use on header bar... 
6143                     cfg.cls += ' navbar-nav';
6144                 }
6145             }
6146             
6147         } else {
6148             if (['tabs','pills'].indexOf(this.type) != -1) {
6149                 cfg.cls += ' nav-' + this.type
6150             } else {
6151                 if (this.type !== 'nav') {
6152                     Roo.log('nav type must be nav/tabs/pills')
6153                 }
6154                 cfg.cls += ' navbar-nav'
6155             }
6156         }
6157         
6158         if (this.parent() && this.parent().sidebar) {
6159             cfg = {
6160                 tag: 'ul',
6161                 cls: 'dashboard-menu sidebar-menu'
6162             };
6163             
6164             return cfg;
6165         }
6166         
6167         if (this.form === true) {
6168             cfg = {
6169                 tag: 'form',
6170                 cls: 'navbar-form form-inline'
6171             };
6172             //nav navbar-right ml-md-auto
6173             if (this.align === 'right') {
6174                 cfg.cls += ' navbar-right ml-md-auto';
6175             } else {
6176                 cfg.cls += ' navbar-left';
6177             }
6178         }
6179         
6180         if (this.align === 'right') {
6181             cfg.cls += ' navbar-right ml-md-auto';
6182         } else {
6183             cfg.cls += ' mr-auto';
6184         }
6185         
6186         if (this.inverse) {
6187             cfg.cls += ' navbar-inverse';
6188             
6189         }
6190         
6191         
6192         return cfg;
6193     },
6194     /**
6195     * sets the active Navigation item
6196     * @param {Roo.bootstrap.nav.Item} the new current navitem
6197     */
6198     setActiveItem : function(item)
6199     {
6200         var prev = false;
6201         Roo.each(this.navItems, function(v){
6202             if (v == item) {
6203                 return ;
6204             }
6205             if (v.isActive()) {
6206                 v.setActive(false, true);
6207                 prev = v;
6208                 
6209             }
6210             
6211         });
6212
6213         item.setActive(true, true);
6214         this.fireEvent('changed', this, item, prev);
6215         
6216         
6217     },
6218     /**
6219     * gets the active Navigation item
6220     * @return {Roo.bootstrap.nav.Item} the current navitem
6221     */
6222     getActive : function()
6223     {
6224         
6225         var prev = false;
6226         Roo.each(this.navItems, function(v){
6227             
6228             if (v.isActive()) {
6229                 prev = v;
6230                 
6231             }
6232             
6233         });
6234         return prev;
6235     },
6236     
6237     indexOfNav : function()
6238     {
6239         
6240         var prev = false;
6241         Roo.each(this.navItems, function(v,i){
6242             
6243             if (v.isActive()) {
6244                 prev = i;
6245                 
6246             }
6247             
6248         });
6249         return prev;
6250     },
6251     /**
6252     * adds a Navigation item
6253     * @param {Roo.bootstrap.nav.Item} the navitem to add
6254     */
6255     addItem : function(cfg)
6256     {
6257         if (this.form && Roo.bootstrap.version == 4) {
6258             cfg.tag = 'div';
6259         }
6260         var cn = new Roo.bootstrap.nav.Item(cfg);
6261         this.register(cn);
6262         cn.parentId = this.id;
6263         cn.onRender(this.el, null);
6264         return cn;
6265     },
6266     /**
6267     * register a Navigation item
6268     * @param {Roo.bootstrap.nav.Item} the navitem to add
6269     */
6270     register : function(item)
6271     {
6272         this.navItems.push( item);
6273         item.navId = this.navId;
6274     
6275     },
6276     
6277     /**
6278     * clear all the Navigation item
6279     */
6280    
6281     clearAll : function()
6282     {
6283         this.navItems = [];
6284         this.el.dom.innerHTML = '';
6285     },
6286     
6287     getNavItem: function(tabId)
6288     {
6289         var ret = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.tabId == tabId) {
6292                ret =  e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return ret;
6299     },
6300     
6301     setActiveNext : function()
6302     {
6303         var i = this.indexOfNav(this.getActive());
6304         if (i > this.navItems.length) {
6305             return;
6306         }
6307         this.setActiveItem(this.navItems[i+1]);
6308     },
6309     setActivePrev : function()
6310     {
6311         var i = this.indexOfNav(this.getActive());
6312         if (i  < 1) {
6313             return;
6314         }
6315         this.setActiveItem(this.navItems[i-1]);
6316     },
6317     clearWasActive : function(except) {
6318         Roo.each(this.navItems, function(e) {
6319             if (e.tabId != except.tabId && e.was_active) {
6320                e.was_active = false;
6321                return false;
6322             }
6323             return true;
6324             
6325         });
6326     },
6327     getWasActive : function ()
6328     {
6329         var r = false;
6330         Roo.each(this.navItems, function(e) {
6331             if (e.was_active) {
6332                r = e;
6333                return false;
6334             }
6335             return true;
6336             
6337         });
6338         return r;
6339     }
6340     
6341     
6342 });
6343
6344  
6345 Roo.apply(Roo.bootstrap.nav.Group, {
6346     
6347     groups: {},
6348      /**
6349     * register a Navigation Group
6350     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6351     */
6352     register : function(navgrp)
6353     {
6354         this.groups[navgrp.navId] = navgrp;
6355         
6356     },
6357     /**
6358     * fetch a Navigation Group based on the navigation ID
6359     * @param {string} the navgroup to add
6360     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6361     */
6362     get: function(navId) {
6363         if (typeof(this.groups[navId]) == 'undefined') {
6364             return false;
6365             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6366         }
6367         return this.groups[navId] ;
6368     }
6369     
6370     
6371     
6372 });
6373
6374  /**
6375  * @class Roo.bootstrap.nav.Item
6376  * @extends Roo.bootstrap.Component
6377  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6378  * @parent Roo.bootstrap.nav.Group
6379  * @licence LGPL
6380  * Bootstrap Navbar.NavItem class
6381  * 
6382  * @cfg {String} href  link to
6383  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6384  * @cfg {Boolean} button_outline show and outlined button
6385  * @cfg {String} html content of button
6386  * @cfg {String} badge text inside badge
6387  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6388  * @cfg {String} glyphicon DEPRICATED - use fa
6389  * @cfg {String} icon DEPRICATED - use fa
6390  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6391  * @cfg {Boolean} active Is item active
6392  * @cfg {Boolean} disabled Is item disabled
6393  * @cfg {String} linkcls  Link Class
6394  * @cfg {Boolean} preventDefault (true | false) default false
6395  * @cfg {String} tabId the tab that this item activates.
6396  * @cfg {String} tagtype (a|span) render as a href or span?
6397  * @cfg {Boolean} animateRef (true|false) link to element default false  
6398  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6399   
6400  * @constructor
6401  * Create a new Navbar Item
6402  * @param {Object} config The config object
6403  */
6404 Roo.bootstrap.nav.Item = function(config){
6405     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6406     this.addEvents({
6407         // raw events
6408         /**
6409          * @event click
6410          * The raw click event for the entire grid.
6411          * @param {Roo.EventObject} e
6412          */
6413         "click" : true,
6414          /**
6415             * @event changed
6416             * Fires when the active item active state changes
6417             * @param {Roo.bootstrap.nav.Item} this
6418             * @param {boolean} state the new state
6419              
6420          */
6421         'changed': true,
6422         /**
6423             * @event scrollto
6424             * Fires when scroll to element
6425             * @param {Roo.bootstrap.nav.Item} this
6426             * @param {Object} options
6427             * @param {Roo.EventObject} e
6428              
6429          */
6430         'scrollto': true
6431     });
6432    
6433 };
6434
6435 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6436     
6437     href: false,
6438     html: '',
6439     badge: '',
6440     icon: false,
6441     fa : false,
6442     glyphicon: false,
6443     active: false,
6444     preventDefault : false,
6445     tabId : false,
6446     tagtype : 'a',
6447     tag: 'li',
6448     disabled : false,
6449     animateRef : false,
6450     was_active : false,
6451     button_weight : '',
6452     button_outline : false,
6453     linkcls : '',
6454     navLink: false,
6455     
6456     getAutoCreate : function(){
6457          
6458         var cfg = {
6459             tag: this.tag,
6460             cls: 'nav-item'
6461         };
6462         
6463         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6464         
6465         if (this.active) {
6466             cfg.cls +=  ' active' ;
6467         }
6468         if (this.disabled) {
6469             cfg.cls += ' disabled';
6470         }
6471         
6472         // BS4 only?
6473         if (this.button_weight.length) {
6474             cfg.tag = this.href ? 'a' : 'button';
6475             cfg.html = this.html || '';
6476             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6477             if (this.href) {
6478                 cfg.href = this.href;
6479             }
6480             if (this.fa) {
6481                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6482             } else {
6483                 cfg.cls += " nav-html";
6484             }
6485             
6486             // menu .. should add dropdown-menu class - so no need for carat..
6487             
6488             if (this.badge !== '') {
6489                  
6490                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6491             }
6492             return cfg;
6493         }
6494         
6495         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6496             cfg.cn = [
6497                 {
6498                     tag: this.tagtype,
6499                     href : this.href || "#",
6500                     html: this.html || '',
6501                     cls : ''
6502                 }
6503             ];
6504             if (this.tagtype == 'a') {
6505                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6506         
6507             }
6508             if (this.icon) {
6509                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6510             } else  if (this.fa) {
6511                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6512             } else if(this.glyphicon) {
6513                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6514             } else {
6515                 cfg.cn[0].cls += " nav-html";
6516             }
6517             
6518             if (this.menu) {
6519                 cfg.cn[0].html += " <span class='caret'></span>";
6520              
6521             }
6522             
6523             if (this.badge !== '') {
6524                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6525             }
6526         }
6527         
6528         
6529         
6530         return cfg;
6531     },
6532     onRender : function(ct, position)
6533     {
6534        // Roo.log("Call onRender: " + this.xtype);
6535         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6536             this.tag = 'div';
6537         }
6538         
6539         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6540         this.navLink = this.el.select('.nav-link',true).first();
6541         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6542         return ret;
6543     },
6544       
6545     
6546     initEvents: function() 
6547     {
6548         if (typeof (this.menu) != 'undefined') {
6549             this.menu.parentType = this.xtype;
6550             this.menu.triggerEl = this.el;
6551             this.menu = this.addxtype(Roo.apply({}, this.menu));
6552         }
6553         
6554         this.el.on('click', this.onClick, this);
6555         
6556         //if(this.tagtype == 'span'){
6557         //    this.el.select('span',true).on('click', this.onClick, this);
6558         //}
6559        
6560         // at this point parent should be available..
6561         this.parent().register(this);
6562     },
6563     
6564     onClick : function(e)
6565     {
6566         if (e.getTarget('.dropdown-menu-item')) {
6567             // did you click on a menu itemm.... - then don't trigger onclick..
6568             return;
6569         }
6570         
6571         if(
6572                 this.preventDefault ||
6573                                 this.href === false ||
6574                 this.href === '#' 
6575         ){
6576             //Roo.log("NavItem - prevent Default?");
6577             e.preventDefault();
6578         }
6579         
6580         if (this.disabled) {
6581             return;
6582         }
6583         
6584         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6585         if (tg && tg.transition) {
6586             Roo.log("waiting for the transitionend");
6587             return;
6588         }
6589         
6590         
6591         
6592         //Roo.log("fire event clicked");
6593         if(this.fireEvent('click', this, e) === false){
6594             return;
6595         };
6596         
6597         if(this.tagtype == 'span'){
6598             return;
6599         }
6600         
6601         //Roo.log(this.href);
6602         var ael = this.el.select('a',true).first();
6603         //Roo.log(ael);
6604         
6605         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6606             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6607             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6608                 return; // ignore... - it's a 'hash' to another page.
6609             }
6610             Roo.log("NavItem - prevent Default?");
6611             e.preventDefault();
6612             this.scrollToElement(e);
6613         }
6614         
6615         
6616         var p =  this.parent();
6617    
6618         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6619             if (typeof(p.setActiveItem) !== 'undefined') {
6620                 p.setActiveItem(this);
6621             }
6622         }
6623         
6624         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6625         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6626             // remove the collapsed menu expand...
6627             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6628         }
6629     },
6630     
6631     isActive: function () {
6632         return this.active
6633     },
6634     setActive : function(state, fire, is_was_active)
6635     {
6636         if (this.active && !state && this.navId) {
6637             this.was_active = true;
6638             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6639             if (nv) {
6640                 nv.clearWasActive(this);
6641             }
6642             
6643         }
6644         this.active = state;
6645         
6646         if (!state ) {
6647             this.el.removeClass('active');
6648             this.navLink ? this.navLink.removeClass('active') : false;
6649         } else if (!this.el.hasClass('active')) {
6650             
6651             this.el.addClass('active');
6652             if (Roo.bootstrap.version == 4 && this.navLink ) {
6653                 this.navLink.addClass('active');
6654             }
6655             
6656         }
6657         if (fire) {
6658             this.fireEvent('changed', this, state);
6659         }
6660         
6661         // show a panel if it's registered and related..
6662         
6663         if (!this.navId || !this.tabId || !state || is_was_active) {
6664             return;
6665         }
6666         
6667         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6668         if (!tg) {
6669             return;
6670         }
6671         var pan = tg.getPanelByName(this.tabId);
6672         if (!pan) {
6673             return;
6674         }
6675         // if we can not flip to new panel - go back to old nav highlight..
6676         if (false == tg.showPanel(pan)) {
6677             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6678             if (nv) {
6679                 var onav = nv.getWasActive();
6680                 if (onav) {
6681                     onav.setActive(true, false, true);
6682                 }
6683             }
6684             
6685         }
6686         
6687         
6688         
6689     },
6690      // this should not be here...
6691     setDisabled : function(state)
6692     {
6693         this.disabled = state;
6694         if (!state ) {
6695             this.el.removeClass('disabled');
6696         } else if (!this.el.hasClass('disabled')) {
6697             this.el.addClass('disabled');
6698         }
6699         
6700     },
6701     
6702     /**
6703      * Fetch the element to display the tooltip on.
6704      * @return {Roo.Element} defaults to this.el
6705      */
6706     tooltipEl : function()
6707     {
6708         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6709     },
6710     
6711     scrollToElement : function(e)
6712     {
6713         var c = document.body;
6714         
6715         /*
6716          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6717          */
6718         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6719             c = document.documentElement;
6720         }
6721         
6722         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6723         
6724         if(!target){
6725             return;
6726         }
6727
6728         var o = target.calcOffsetsTo(c);
6729         
6730         var options = {
6731             target : target,
6732             value : o[1]
6733         };
6734         
6735         this.fireEvent('scrollto', this, options, e);
6736         
6737         Roo.get(c).scrollTo('top', options.value, true);
6738         
6739         return;
6740     },
6741     /**
6742      * Set the HTML (text content) of the item
6743      * @param {string} html  content for the nav item
6744      */
6745     setHtml : function(html)
6746     {
6747         this.html = html;
6748         this.htmlEl.dom.innerHTML = html;
6749         
6750     } 
6751 });
6752  
6753
6754  /*
6755  * - LGPL
6756  *
6757  * sidebar item
6758  *
6759  *  li
6760  *    <span> icon </span>
6761  *    <span> text </span>
6762  *    <span>badge </span>
6763  */
6764
6765 /**
6766  * @class Roo.bootstrap.nav.SidebarItem
6767  * @extends Roo.bootstrap.nav.Item
6768  * Bootstrap Navbar.NavSidebarItem class
6769  * 
6770  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6771  * {Boolean} open is the menu open
6772  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6773  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6774  * {String} buttonSize (sm|md|lg)the extra classes for the button
6775  * {Boolean} showArrow show arrow next to the text (default true)
6776  * @constructor
6777  * Create a new Navbar Button
6778  * @param {Object} config The config object
6779  */
6780 Roo.bootstrap.nav.SidebarItem = function(config){
6781     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6782     this.addEvents({
6783         // raw events
6784         /**
6785          * @event click
6786          * The raw click event for the entire grid.
6787          * @param {Roo.EventObject} e
6788          */
6789         "click" : true,
6790          /**
6791             * @event changed
6792             * Fires when the active item active state changes
6793             * @param {Roo.bootstrap.nav.SidebarItem} this
6794             * @param {boolean} state the new state
6795              
6796          */
6797         'changed': true
6798     });
6799    
6800 };
6801
6802 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6803     
6804     badgeWeight : 'default',
6805     
6806     open: false,
6807     
6808     buttonView : false,
6809     
6810     buttonWeight : 'default',
6811     
6812     buttonSize : 'md',
6813     
6814     showArrow : true,
6815     
6816     getAutoCreate : function(){
6817         
6818         
6819         var a = {
6820                 tag: 'a',
6821                 href : this.href || '#',
6822                 cls: '',
6823                 html : '',
6824                 cn : []
6825         };
6826         
6827         if(this.buttonView){
6828             a = {
6829                 tag: 'button',
6830                 href : this.href || '#',
6831                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6832                 html : this.html,
6833                 cn : []
6834             };
6835         }
6836         
6837         var cfg = {
6838             tag: 'li',
6839             cls: '',
6840             cn: [ a ]
6841         };
6842         
6843         if (this.active) {
6844             cfg.cls += ' active';
6845         }
6846         
6847         if (this.disabled) {
6848             cfg.cls += ' disabled';
6849         }
6850         if (this.open) {
6851             cfg.cls += ' open x-open';
6852         }
6853         // left icon..
6854         if (this.glyphicon || this.icon) {
6855             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6856             a.cn.push({ tag : 'i', cls : c }) ;
6857         }
6858         
6859         if(!this.buttonView){
6860             var span = {
6861                 tag: 'span',
6862                 html : this.html || ''
6863             };
6864
6865             a.cn.push(span);
6866             
6867         }
6868         
6869         if (this.badge !== '') {
6870             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6871         }
6872         
6873         if (this.menu) {
6874             
6875             if(this.showArrow){
6876                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6877             }
6878             
6879             a.cls += ' dropdown-toggle treeview' ;
6880         }
6881         
6882         return cfg;
6883     },
6884     
6885     initEvents : function()
6886     { 
6887         if (typeof (this.menu) != 'undefined') {
6888             this.menu.parentType = this.xtype;
6889             this.menu.triggerEl = this.el;
6890             this.menu = this.addxtype(Roo.apply({}, this.menu));
6891         }
6892         
6893         this.el.on('click', this.onClick, this);
6894         
6895         if(this.badge !== ''){
6896             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6897         }
6898         
6899     },
6900     
6901     onClick : function(e)
6902     {
6903         if(this.disabled){
6904             e.preventDefault();
6905             return;
6906         }
6907         
6908         if(this.preventDefault){
6909             e.preventDefault();
6910         }
6911         
6912         this.fireEvent('click', this, e);
6913     },
6914     
6915     disable : function()
6916     {
6917         this.setDisabled(true);
6918     },
6919     
6920     enable : function()
6921     {
6922         this.setDisabled(false);
6923     },
6924     
6925     setDisabled : function(state)
6926     {
6927         if(this.disabled == state){
6928             return;
6929         }
6930         
6931         this.disabled = state;
6932         
6933         if (state) {
6934             this.el.addClass('disabled');
6935             return;
6936         }
6937         
6938         this.el.removeClass('disabled');
6939         
6940         return;
6941     },
6942     
6943     setActive : function(state)
6944     {
6945         if(this.active == state){
6946             return;
6947         }
6948         
6949         this.active = state;
6950         
6951         if (state) {
6952             this.el.addClass('active');
6953             return;
6954         }
6955         
6956         this.el.removeClass('active');
6957         
6958         return;
6959     },
6960     
6961     isActive: function () 
6962     {
6963         return this.active;
6964     },
6965     
6966     setBadge : function(str)
6967     {
6968         if(!this.badgeEl){
6969             return;
6970         }
6971         
6972         this.badgeEl.dom.innerHTML = str;
6973     }
6974     
6975    
6976      
6977  
6978 });
6979  
6980
6981  /*
6982  * - LGPL
6983  *
6984  * nav progress bar
6985  * 
6986  */
6987
6988 /**
6989  * @class Roo.bootstrap.nav.ProgressBar
6990  * @extends Roo.bootstrap.Component
6991  * @children Roo.bootstrap.nav.ProgressBarItem
6992  * Bootstrap NavProgressBar class
6993  * 
6994  * @constructor
6995  * Create a new nav progress bar - a bar indicating step along a process
6996  * @param {Object} config The config object
6997  */
6998
6999 Roo.bootstrap.nav.ProgressBar = function(config){
7000     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
7001
7002     this.bullets = this.bullets || [];
7003    
7004 //    Roo.bootstrap.nav.ProgressBar.register(this);
7005      this.addEvents({
7006         /**
7007              * @event changed
7008              * Fires when the active item changes
7009              * @param {Roo.bootstrap.nav.ProgressBar} this
7010              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7011              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7012          */
7013         'changed': true
7014      });
7015     
7016 };
7017
7018 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7019     /**
7020      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7021      * Bullets for the Nav Progress bar for the toolbar
7022      */
7023     bullets : [],
7024     barItems : [],
7025     
7026     getAutoCreate : function()
7027     {
7028         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7029         
7030         cfg = {
7031             tag : 'div',
7032             cls : 'roo-navigation-bar-group',
7033             cn : [
7034                 {
7035                     tag : 'div',
7036                     cls : 'roo-navigation-top-bar'
7037                 },
7038                 {
7039                     tag : 'div',
7040                     cls : 'roo-navigation-bullets-bar',
7041                     cn : [
7042                         {
7043                             tag : 'ul',
7044                             cls : 'roo-navigation-bar'
7045                         }
7046                     ]
7047                 },
7048                 
7049                 {
7050                     tag : 'div',
7051                     cls : 'roo-navigation-bottom-bar'
7052                 }
7053             ]
7054             
7055         };
7056         
7057         return cfg;
7058         
7059     },
7060     
7061     initEvents: function() 
7062     {
7063         
7064     },
7065     
7066     onRender : function(ct, position) 
7067     {
7068         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7069         
7070         if(this.bullets.length){
7071             Roo.each(this.bullets, function(b){
7072                this.addItem(b);
7073             }, this);
7074         }
7075         
7076         this.format();
7077         
7078     },
7079     
7080     addItem : function(cfg)
7081     {
7082         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7083         
7084         item.parentId = this.id;
7085         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7086         
7087         if(cfg.html){
7088             var top = new Roo.bootstrap.Element({
7089                 tag : 'div',
7090                 cls : 'roo-navigation-bar-text'
7091             });
7092             
7093             var bottom = new Roo.bootstrap.Element({
7094                 tag : 'div',
7095                 cls : 'roo-navigation-bar-text'
7096             });
7097             
7098             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7099             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7100             
7101             var topText = new Roo.bootstrap.Element({
7102                 tag : 'span',
7103                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7104             });
7105             
7106             var bottomText = new Roo.bootstrap.Element({
7107                 tag : 'span',
7108                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7109             });
7110             
7111             topText.onRender(top.el, null);
7112             bottomText.onRender(bottom.el, null);
7113             
7114             item.topEl = top;
7115             item.bottomEl = bottom;
7116         }
7117         
7118         this.barItems.push(item);
7119         
7120         return item;
7121     },
7122     
7123     getActive : function()
7124     {
7125         var active = false;
7126         
7127         Roo.each(this.barItems, function(v){
7128             
7129             if (!v.isActive()) {
7130                 return;
7131             }
7132             
7133             active = v;
7134             return false;
7135             
7136         });
7137         
7138         return active;
7139     },
7140     
7141     setActiveItem : function(item)
7142     {
7143         var prev = false;
7144         
7145         Roo.each(this.barItems, function(v){
7146             if (v.rid == item.rid) {
7147                 return ;
7148             }
7149             
7150             if (v.isActive()) {
7151                 v.setActive(false);
7152                 prev = v;
7153             }
7154         });
7155
7156         item.setActive(true);
7157         
7158         this.fireEvent('changed', this, item, prev);
7159     },
7160     
7161     getBarItem: function(rid)
7162     {
7163         var ret = false;
7164         
7165         Roo.each(this.barItems, function(e) {
7166             if (e.rid != rid) {
7167                 return;
7168             }
7169             
7170             ret =  e;
7171             return false;
7172         });
7173         
7174         return ret;
7175     },
7176     
7177     indexOfItem : function(item)
7178     {
7179         var index = false;
7180         
7181         Roo.each(this.barItems, function(v, i){
7182             
7183             if (v.rid != item.rid) {
7184                 return;
7185             }
7186             
7187             index = i;
7188             return false
7189         });
7190         
7191         return index;
7192     },
7193     
7194     setActiveNext : function()
7195     {
7196         var i = this.indexOfItem(this.getActive());
7197         
7198         if (i > this.barItems.length) {
7199             return;
7200         }
7201         
7202         this.setActiveItem(this.barItems[i+1]);
7203     },
7204     
7205     setActivePrev : function()
7206     {
7207         var i = this.indexOfItem(this.getActive());
7208         
7209         if (i  < 1) {
7210             return;
7211         }
7212         
7213         this.setActiveItem(this.barItems[i-1]);
7214     },
7215     
7216     format : function()
7217     {
7218         if(!this.barItems.length){
7219             return;
7220         }
7221      
7222         var width = 100 / this.barItems.length;
7223         
7224         Roo.each(this.barItems, function(i){
7225             i.el.setStyle('width', width + '%');
7226             i.topEl.el.setStyle('width', width + '%');
7227             i.bottomEl.el.setStyle('width', width + '%');
7228         }, this);
7229         
7230     }
7231     
7232 });
7233 /*
7234  * - LGPL
7235  *
7236  * Nav Progress Item
7237  * 
7238  */
7239
7240 /**
7241  * @class Roo.bootstrap.nav.ProgressBarItem
7242  * @extends Roo.bootstrap.Component
7243  * Bootstrap NavProgressBarItem class
7244  * @cfg {String} rid the reference id
7245  * @cfg {Boolean} active (true|false) Is item active default false
7246  * @cfg {Boolean} disabled (true|false) Is item active default false
7247  * @cfg {String} html
7248  * @cfg {String} position (top|bottom) text position default bottom
7249  * @cfg {String} icon show icon instead of number
7250  * 
7251  * @constructor
7252  * Create a new NavProgressBarItem
7253  * @param {Object} config The config object
7254  */
7255 Roo.bootstrap.nav.ProgressBarItem = function(config){
7256     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7257     this.addEvents({
7258         // raw events
7259         /**
7260          * @event click
7261          * The raw click event for the entire grid.
7262          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7263          * @param {Roo.EventObject} e
7264          */
7265         "click" : true
7266     });
7267    
7268 };
7269
7270 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7271     
7272     rid : '',
7273     active : false,
7274     disabled : false,
7275     html : '',
7276     position : 'bottom',
7277     icon : false,
7278     
7279     getAutoCreate : function()
7280     {
7281         var iconCls = 'roo-navigation-bar-item-icon';
7282         
7283         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7284         
7285         var cfg = {
7286             tag: 'li',
7287             cls: 'roo-navigation-bar-item',
7288             cn : [
7289                 {
7290                     tag : 'i',
7291                     cls : iconCls
7292                 }
7293             ]
7294         };
7295         
7296         if(this.active){
7297             cfg.cls += ' active';
7298         }
7299         if(this.disabled){
7300             cfg.cls += ' disabled';
7301         }
7302         
7303         return cfg;
7304     },
7305     
7306     disable : function()
7307     {
7308         this.setDisabled(true);
7309     },
7310     
7311     enable : function()
7312     {
7313         this.setDisabled(false);
7314     },
7315     
7316     initEvents: function() 
7317     {
7318         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7319         
7320         this.iconEl.on('click', this.onClick, this);
7321     },
7322     
7323     onClick : function(e)
7324     {
7325         e.preventDefault();
7326         
7327         if(this.disabled){
7328             return;
7329         }
7330         
7331         if(this.fireEvent('click', this, e) === false){
7332             return;
7333         };
7334         
7335         this.parent().setActiveItem(this);
7336     },
7337     
7338     isActive: function () 
7339     {
7340         return this.active;
7341     },
7342     
7343     setActive : function(state)
7344     {
7345         if(this.active == state){
7346             return;
7347         }
7348         
7349         this.active = state;
7350         
7351         if (state) {
7352             this.el.addClass('active');
7353             return;
7354         }
7355         
7356         this.el.removeClass('active');
7357         
7358         return;
7359     },
7360     
7361     setDisabled : function(state)
7362     {
7363         if(this.disabled == state){
7364             return;
7365         }
7366         
7367         this.disabled = state;
7368         
7369         if (state) {
7370             this.el.addClass('disabled');
7371             return;
7372         }
7373         
7374         this.el.removeClass('disabled');
7375     },
7376     
7377     tooltipEl : function()
7378     {
7379         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7380     }
7381 });
7382  
7383
7384  /*
7385  * - LGPL
7386  *
7387  *  Breadcrumb Nav
7388  * 
7389  */
7390 Roo.namespace('Roo.bootstrap.breadcrumb');
7391
7392
7393 /**
7394  * @class Roo.bootstrap.breadcrumb.Nav
7395  * @extends Roo.bootstrap.Component
7396  * Bootstrap Breadcrumb Nav Class
7397  *  
7398  * @children Roo.bootstrap.breadcrumb.Item
7399  * 
7400  * @constructor
7401  * Create a new breadcrumb.Nav
7402  * @param {Object} config The config object
7403  */
7404
7405
7406 Roo.bootstrap.breadcrumb.Nav = function(config){
7407     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7408     
7409     
7410 };
7411
7412 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7413     
7414     getAutoCreate : function()
7415     {
7416
7417         var cfg = {
7418             tag: 'nav',
7419             cn : [
7420                 {
7421                     tag : 'ol',
7422                     cls : 'breadcrumb'
7423                 }
7424             ]
7425             
7426         };
7427           
7428         return cfg;
7429     },
7430     
7431     initEvents: function()
7432     {
7433         this.olEl = this.el.select('ol',true).first();    
7434     },
7435     getChildContainer : function()
7436     {
7437         return this.olEl;  
7438     }
7439     
7440 });
7441
7442  /*
7443  * - LGPL
7444  *
7445  *  Breadcrumb Item
7446  * 
7447  */
7448
7449
7450 /**
7451  * @class Roo.bootstrap.breadcrumb.Nav
7452  * @extends Roo.bootstrap.Component
7453  * @children Roo.bootstrap.Component
7454  * @parent Roo.bootstrap.breadcrumb.Nav
7455  * Bootstrap Breadcrumb Nav Class
7456  *  
7457  * 
7458  * @cfg {String} html the content of the link.
7459  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7460  * @cfg {Boolean} active is it active
7461
7462  * 
7463  * @constructor
7464  * Create a new breadcrumb.Nav
7465  * @param {Object} config The config object
7466  */
7467
7468 Roo.bootstrap.breadcrumb.Item = function(config){
7469     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7470     this.addEvents({
7471         // img events
7472         /**
7473          * @event click
7474          * The img click event for the img.
7475          * @param {Roo.EventObject} e
7476          */
7477         "click" : true
7478     });
7479     
7480 };
7481
7482 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7483     
7484     href: false,
7485     html : '',
7486     
7487     getAutoCreate : function()
7488     {
7489
7490         var cfg = {
7491             tag: 'li',
7492             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7493         };
7494         if (this.href !== false) {
7495             cfg.cn = [{
7496                 tag : 'a',
7497                 href : this.href,
7498                 html : this.html
7499             }];
7500         } else {
7501             cfg.html = this.html;
7502         }
7503         
7504         return cfg;
7505     },
7506     
7507     initEvents: function()
7508     {
7509         if (this.href) {
7510             this.el.select('a', true).first().on('click',this.onClick, this)
7511         }
7512         
7513     },
7514     onClick : function(e)
7515     {
7516         e.preventDefault();
7517         this.fireEvent('click',this,  e);
7518     }
7519     
7520 });
7521
7522  /*
7523  * - LGPL
7524  *
7525  * row
7526  * 
7527  */
7528
7529 /**
7530  * @class Roo.bootstrap.Row
7531  * @extends Roo.bootstrap.Component
7532  * @children Roo.bootstrap.Component
7533  * Bootstrap Row class (contains columns...)
7534  * 
7535  * @constructor
7536  * Create a new Row
7537  * @param {Object} config The config object
7538  */
7539
7540 Roo.bootstrap.Row = function(config){
7541     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7542 };
7543
7544 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7545     
7546     getAutoCreate : function(){
7547        return {
7548             cls: 'row clearfix'
7549        };
7550     }
7551     
7552     
7553 });
7554
7555  
7556
7557  /*
7558  * - LGPL
7559  *
7560  * pagination
7561  * 
7562  */
7563
7564 /**
7565  * @class Roo.bootstrap.Pagination
7566  * @extends Roo.bootstrap.Component
7567  * @children Roo.bootstrap.Pagination
7568  * Bootstrap Pagination class
7569  * 
7570  * @cfg {String} size (xs|sm|md|lg|xl)
7571  * @cfg {Boolean} inverse 
7572  * 
7573  * @constructor
7574  * Create a new Pagination
7575  * @param {Object} config The config object
7576  */
7577
7578 Roo.bootstrap.Pagination = function(config){
7579     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7580 };
7581
7582 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7583     
7584     cls: false,
7585     size: false,
7586     inverse: false,
7587     
7588     getAutoCreate : function(){
7589         var cfg = {
7590             tag: 'ul',
7591                 cls: 'pagination'
7592         };
7593         if (this.inverse) {
7594             cfg.cls += ' inverse';
7595         }
7596         if (this.html) {
7597             cfg.html=this.html;
7598         }
7599         if (this.cls) {
7600             cfg.cls += " " + this.cls;
7601         }
7602         return cfg;
7603     }
7604    
7605 });
7606
7607  
7608
7609  /*
7610  * - LGPL
7611  *
7612  * Pagination item
7613  * 
7614  */
7615
7616
7617 /**
7618  * @class Roo.bootstrap.PaginationItem
7619  * @extends Roo.bootstrap.Component
7620  * Bootstrap PaginationItem class
7621  * @cfg {String} html text
7622  * @cfg {String} href the link
7623  * @cfg {Boolean} preventDefault (true | false) default true
7624  * @cfg {Boolean} active (true | false) default false
7625  * @cfg {Boolean} disabled default false
7626  * 
7627  * 
7628  * @constructor
7629  * Create a new PaginationItem
7630  * @param {Object} config The config object
7631  */
7632
7633
7634 Roo.bootstrap.PaginationItem = function(config){
7635     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7636     this.addEvents({
7637         // raw events
7638         /**
7639          * @event click
7640          * The raw click event for the entire grid.
7641          * @param {Roo.EventObject} e
7642          */
7643         "click" : true
7644     });
7645 };
7646
7647 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7648     
7649     href : false,
7650     html : false,
7651     preventDefault: true,
7652     active : false,
7653     cls : false,
7654     disabled: false,
7655     
7656     getAutoCreate : function(){
7657         var cfg= {
7658             tag: 'li',
7659             cn: [
7660                 {
7661                     tag : 'a',
7662                     href : this.href ? this.href : '#',
7663                     html : this.html ? this.html : ''
7664                 }
7665             ]
7666         };
7667         
7668         if(this.cls){
7669             cfg.cls = this.cls;
7670         }
7671         
7672         if(this.disabled){
7673             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7674         }
7675         
7676         if(this.active){
7677             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7678         }
7679         
7680         return cfg;
7681     },
7682     
7683     initEvents: function() {
7684         
7685         this.el.on('click', this.onClick, this);
7686         
7687     },
7688     onClick : function(e)
7689     {
7690         Roo.log('PaginationItem on click ');
7691         if(this.preventDefault){
7692             e.preventDefault();
7693         }
7694         
7695         if(this.disabled){
7696             return;
7697         }
7698         
7699         this.fireEvent('click', this, e);
7700     }
7701    
7702 });
7703
7704  
7705
7706  /*
7707  * - LGPL
7708  *
7709  * slider
7710  * 
7711  */
7712
7713
7714 /**
7715  * @class Roo.bootstrap.Slider
7716  * @extends Roo.bootstrap.Component
7717  * Bootstrap Slider class
7718  *    
7719  * @constructor
7720  * Create a new Slider
7721  * @param {Object} config The config object
7722  */
7723
7724 Roo.bootstrap.Slider = function(config){
7725     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7726 };
7727
7728 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7729     
7730     getAutoCreate : function(){
7731         
7732         var cfg = {
7733             tag: 'div',
7734             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7735             cn: [
7736                 {
7737                     tag: 'a',
7738                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7739                 }
7740             ]
7741         };
7742         
7743         return cfg;
7744     }
7745    
7746 });
7747
7748  /*
7749  * Based on:
7750  * Ext JS Library 1.1.1
7751  * Copyright(c) 2006-2007, Ext JS, LLC.
7752  *
7753  * Originally Released Under LGPL - original licence link has changed is not relivant.
7754  *
7755  * Fork - LGPL
7756  * <script type="text/javascript">
7757  */
7758  /**
7759  * @extends Roo.dd.DDProxy
7760  * @class Roo.grid.SplitDragZone
7761  * Support for Column Header resizing
7762  * @constructor
7763  * @param {Object} config
7764  */
7765 // private
7766 // This is a support class used internally by the Grid components
7767 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7768     this.grid = grid;
7769     this.view = grid.getView();
7770     this.proxy = this.view.resizeProxy;
7771     Roo.grid.SplitDragZone.superclass.constructor.call(
7772         this,
7773         hd, // ID
7774         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7775         {  // CONFIG
7776             dragElId : Roo.id(this.proxy.dom),
7777             resizeFrame:false
7778         }
7779     );
7780     
7781     this.setHandleElId(Roo.id(hd));
7782     if (hd2 !== false) {
7783         this.setOuterHandleElId(Roo.id(hd2));
7784     }
7785     
7786     this.scroll = false;
7787 };
7788 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7789     fly: Roo.Element.fly,
7790
7791     b4StartDrag : function(x, y){
7792         this.view.headersDisabled = true;
7793         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7794                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7795         );
7796         this.proxy.setHeight(h);
7797         
7798         // for old system colWidth really stored the actual width?
7799         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7800         // which in reality did not work.. - it worked only for fixed sizes
7801         // for resizable we need to use actual sizes.
7802         var w = this.cm.getColumnWidth(this.cellIndex);
7803         if (!this.view.mainWrap) {
7804             // bootstrap.
7805             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7806         }
7807         
7808         
7809         
7810         // this was w-this.grid.minColumnWidth;
7811         // doesnt really make sense? - w = thie curren width or the rendered one?
7812         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7813         this.resetConstraints();
7814         this.setXConstraint(minw, 1000);
7815         this.setYConstraint(0, 0);
7816         this.minX = x - minw;
7817         this.maxX = x + 1000;
7818         this.startPos = x;
7819         if (!this.view.mainWrap) { // this is Bootstrap code..
7820             this.getDragEl().style.display='block';
7821         }
7822         
7823         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7824     },
7825
7826
7827     handleMouseDown : function(e){
7828         ev = Roo.EventObject.setEvent(e);
7829         var t = this.fly(ev.getTarget());
7830         if(t.hasClass("x-grid-split")){
7831             this.cellIndex = this.view.getCellIndex(t.dom);
7832             this.split = t.dom;
7833             this.cm = this.grid.colModel;
7834             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7835                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7836             }
7837         }
7838     },
7839
7840     endDrag : function(e){
7841         this.view.headersDisabled = false;
7842         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7843         var diff = endX - this.startPos;
7844         // 
7845         var w = this.cm.getColumnWidth(this.cellIndex);
7846         if (!this.view.mainWrap) {
7847             w = 0;
7848         }
7849         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7850     },
7851
7852     autoOffset : function(){
7853         this.setDelta(0,0);
7854     }
7855 });/*
7856  * Based on:
7857  * Ext JS Library 1.1.1
7858  * Copyright(c) 2006-2007, Ext JS, LLC.
7859  *
7860  * Originally Released Under LGPL - original licence link has changed is not relivant.
7861  *
7862  * Fork - LGPL
7863  * <script type="text/javascript">
7864  */
7865
7866 /**
7867  * @class Roo.grid.AbstractSelectionModel
7868  * @extends Roo.util.Observable
7869  * @abstract
7870  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7871  * implemented by descendant classes.  This class should not be directly instantiated.
7872  * @constructor
7873  */
7874 Roo.grid.AbstractSelectionModel = function(){
7875     this.locked = false;
7876     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7877 };
7878
7879 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7880     /** @ignore Called by the grid automatically. Do not call directly. */
7881     init : function(grid){
7882         this.grid = grid;
7883         this.initEvents();
7884     },
7885
7886     /**
7887      * Locks the selections.
7888      */
7889     lock : function(){
7890         this.locked = true;
7891     },
7892
7893     /**
7894      * Unlocks the selections.
7895      */
7896     unlock : function(){
7897         this.locked = false;
7898     },
7899
7900     /**
7901      * Returns true if the selections are locked.
7902      * @return {Boolean}
7903      */
7904     isLocked : function(){
7905         return this.locked;
7906     }
7907 });/*
7908  * Based on:
7909  * Ext JS Library 1.1.1
7910  * Copyright(c) 2006-2007, Ext JS, LLC.
7911  *
7912  * Originally Released Under LGPL - original licence link has changed is not relivant.
7913  *
7914  * Fork - LGPL
7915  * <script type="text/javascript">
7916  */
7917 /**
7918  * @extends Roo.grid.AbstractSelectionModel
7919  * @class Roo.grid.RowSelectionModel
7920  * The default SelectionModel used by {@link Roo.grid.Grid}.
7921  * It supports multiple selections and keyboard selection/navigation. 
7922  * @constructor
7923  * @param {Object} config
7924  */
7925 Roo.grid.RowSelectionModel = function(config){
7926     Roo.apply(this, config);
7927     this.selections = new Roo.util.MixedCollection(false, function(o){
7928         return o.id;
7929     });
7930
7931     this.last = false;
7932     this.lastActive = false;
7933
7934     this.addEvents({
7935         /**
7936         * @event selectionchange
7937         * Fires when the selection changes
7938         * @param {SelectionModel} this
7939         */
7940        "selectionchange" : true,
7941        /**
7942         * @event afterselectionchange
7943         * Fires after the selection changes (eg. by key press or clicking)
7944         * @param {SelectionModel} this
7945         */
7946        "afterselectionchange" : true,
7947        /**
7948         * @event beforerowselect
7949         * Fires when a row is selected being selected, return false to cancel.
7950         * @param {SelectionModel} this
7951         * @param {Number} rowIndex The selected index
7952         * @param {Boolean} keepExisting False if other selections will be cleared
7953         */
7954        "beforerowselect" : true,
7955        /**
7956         * @event rowselect
7957         * Fires when a row is selected.
7958         * @param {SelectionModel} this
7959         * @param {Number} rowIndex The selected index
7960         * @param {Roo.data.Record} r The record
7961         */
7962        "rowselect" : true,
7963        /**
7964         * @event rowdeselect
7965         * Fires when a row is deselected.
7966         * @param {SelectionModel} this
7967         * @param {Number} rowIndex The selected index
7968         */
7969         "rowdeselect" : true
7970     });
7971     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7972     this.locked = false;
7973 };
7974
7975 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7976     /**
7977      * @cfg {Boolean} singleSelect
7978      * True to allow selection of only one row at a time (defaults to false)
7979      */
7980     singleSelect : false,
7981
7982     // private
7983     initEvents : function(){
7984
7985         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7986             this.grid.on("mousedown", this.handleMouseDown, this);
7987         }else{ // allow click to work like normal
7988             this.grid.on("rowclick", this.handleDragableRowClick, this);
7989         }
7990         // bootstrap does not have a view..
7991         var view = this.grid.view ? this.grid.view : this.grid;
7992         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7993             "up" : function(e){
7994                 if(!e.shiftKey){
7995                     this.selectPrevious(e.shiftKey);
7996                 }else if(this.last !== false && this.lastActive !== false){
7997                     var last = this.last;
7998                     this.selectRange(this.last,  this.lastActive-1);
7999                     view.focusRow(this.lastActive);
8000                     if(last !== false){
8001                         this.last = last;
8002                     }
8003                 }else{
8004                     this.selectFirstRow();
8005                 }
8006                 this.fireEvent("afterselectionchange", this);
8007             },
8008             "down" : function(e){
8009                 if(!e.shiftKey){
8010                     this.selectNext(e.shiftKey);
8011                 }else if(this.last !== false && this.lastActive !== false){
8012                     var last = this.last;
8013                     this.selectRange(this.last,  this.lastActive+1);
8014                     view.focusRow(this.lastActive);
8015                     if(last !== false){
8016                         this.last = last;
8017                     }
8018                 }else{
8019                     this.selectFirstRow();
8020                 }
8021                 this.fireEvent("afterselectionchange", this);
8022             },
8023             scope: this
8024         });
8025
8026          
8027         view.on("refresh", this.onRefresh, this);
8028         view.on("rowupdated", this.onRowUpdated, this);
8029         view.on("rowremoved", this.onRemove, this);
8030     },
8031
8032     // private
8033     onRefresh : function(){
8034         var ds = this.grid.ds, i, v = this.grid.view;
8035         var s = this.selections;
8036         s.each(function(r){
8037             if((i = ds.indexOfId(r.id)) != -1){
8038                 v.onRowSelect(i);
8039                 s.add(ds.getAt(i)); // updating the selection relate data
8040             }else{
8041                 s.remove(r);
8042             }
8043         });
8044     },
8045
8046     // private
8047     onRemove : function(v, index, r){
8048         this.selections.remove(r);
8049     },
8050
8051     // private
8052     onRowUpdated : function(v, index, r){
8053         if(this.isSelected(r)){
8054             v.onRowSelect(index);
8055         }
8056     },
8057
8058     /**
8059      * Select records.
8060      * @param {Array} records The records to select
8061      * @param {Boolean} keepExisting (optional) True to keep existing selections
8062      */
8063     selectRecords : function(records, keepExisting){
8064         if(!keepExisting){
8065             this.clearSelections();
8066         }
8067         var ds = this.grid.ds;
8068         for(var i = 0, len = records.length; i < len; i++){
8069             this.selectRow(ds.indexOf(records[i]), true);
8070         }
8071     },
8072
8073     /**
8074      * Gets the number of selected rows.
8075      * @return {Number}
8076      */
8077     getCount : function(){
8078         return this.selections.length;
8079     },
8080
8081     /**
8082      * Selects the first row in the grid.
8083      */
8084     selectFirstRow : function(){
8085         this.selectRow(0);
8086     },
8087
8088     /**
8089      * Select the last row.
8090      * @param {Boolean} keepExisting (optional) True to keep existing selections
8091      */
8092     selectLastRow : function(keepExisting){
8093         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8094     },
8095
8096     /**
8097      * Selects the row immediately following the last selected row.
8098      * @param {Boolean} keepExisting (optional) True to keep existing selections
8099      */
8100     selectNext : function(keepExisting){
8101         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8102             this.selectRow(this.last+1, keepExisting);
8103             var view = this.grid.view ? this.grid.view : this.grid;
8104             view.focusRow(this.last);
8105         }
8106     },
8107
8108     /**
8109      * Selects the row that precedes the last selected row.
8110      * @param {Boolean} keepExisting (optional) True to keep existing selections
8111      */
8112     selectPrevious : function(keepExisting){
8113         if(this.last){
8114             this.selectRow(this.last-1, keepExisting);
8115             var view = this.grid.view ? this.grid.view : this.grid;
8116             view.focusRow(this.last);
8117         }
8118     },
8119
8120     /**
8121      * Returns the selected records
8122      * @return {Array} Array of selected records
8123      */
8124     getSelections : function(){
8125         return [].concat(this.selections.items);
8126     },
8127
8128     /**
8129      * Returns the first selected record.
8130      * @return {Record}
8131      */
8132     getSelected : function(){
8133         return this.selections.itemAt(0);
8134     },
8135
8136
8137     /**
8138      * Clears all selections.
8139      */
8140     clearSelections : function(fast){
8141         if(this.locked) {
8142             return;
8143         }
8144         if(fast !== true){
8145             var ds = this.grid.ds;
8146             var s = this.selections;
8147             s.each(function(r){
8148                 this.deselectRow(ds.indexOfId(r.id));
8149             }, this);
8150             s.clear();
8151         }else{
8152             this.selections.clear();
8153         }
8154         this.last = false;
8155     },
8156
8157
8158     /**
8159      * Selects all rows.
8160      */
8161     selectAll : function(){
8162         if(this.locked) {
8163             return;
8164         }
8165         this.selections.clear();
8166         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8167             this.selectRow(i, true);
8168         }
8169     },
8170
8171     /**
8172      * Returns True if there is a selection.
8173      * @return {Boolean}
8174      */
8175     hasSelection : function(){
8176         return this.selections.length > 0;
8177     },
8178
8179     /**
8180      * Returns True if the specified row is selected.
8181      * @param {Number/Record} record The record or index of the record to check
8182      * @return {Boolean}
8183      */
8184     isSelected : function(index){
8185         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8186         return (r && this.selections.key(r.id) ? true : false);
8187     },
8188
8189     /**
8190      * Returns True if the specified record id is selected.
8191      * @param {String} id The id of record to check
8192      * @return {Boolean}
8193      */
8194     isIdSelected : function(id){
8195         return (this.selections.key(id) ? true : false);
8196     },
8197
8198     // private
8199     handleMouseDown : function(e, t)
8200     {
8201         var view = this.grid.view ? this.grid.view : this.grid;
8202         var rowIndex;
8203         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8204             return;
8205         };
8206         if(e.shiftKey && this.last !== false){
8207             var last = this.last;
8208             this.selectRange(last, rowIndex, e.ctrlKey);
8209             this.last = last; // reset the last
8210             view.focusRow(rowIndex);
8211         }else{
8212             var isSelected = this.isSelected(rowIndex);
8213             if(e.button !== 0 && isSelected){
8214                 view.focusRow(rowIndex);
8215             }else if(e.ctrlKey && isSelected){
8216                 this.deselectRow(rowIndex);
8217             }else if(!isSelected){
8218                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8219                 view.focusRow(rowIndex);
8220             }
8221         }
8222         this.fireEvent("afterselectionchange", this);
8223     },
8224     // private
8225     handleDragableRowClick :  function(grid, rowIndex, e) 
8226     {
8227         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8228             this.selectRow(rowIndex, false);
8229             var view = this.grid.view ? this.grid.view : this.grid;
8230             view.focusRow(rowIndex);
8231              this.fireEvent("afterselectionchange", this);
8232         }
8233     },
8234     
8235     /**
8236      * Selects multiple rows.
8237      * @param {Array} rows Array of the indexes of the row to select
8238      * @param {Boolean} keepExisting (optional) True to keep existing selections
8239      */
8240     selectRows : function(rows, keepExisting){
8241         if(!keepExisting){
8242             this.clearSelections();
8243         }
8244         for(var i = 0, len = rows.length; i < len; i++){
8245             this.selectRow(rows[i], true);
8246         }
8247     },
8248
8249     /**
8250      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8251      * @param {Number} startRow The index of the first row in the range
8252      * @param {Number} endRow The index of the last row in the range
8253      * @param {Boolean} keepExisting (optional) True to retain existing selections
8254      */
8255     selectRange : function(startRow, endRow, keepExisting){
8256         if(this.locked) {
8257             return;
8258         }
8259         if(!keepExisting){
8260             this.clearSelections();
8261         }
8262         if(startRow <= endRow){
8263             for(var i = startRow; i <= endRow; i++){
8264                 this.selectRow(i, true);
8265             }
8266         }else{
8267             for(var i = startRow; i >= endRow; i--){
8268                 this.selectRow(i, true);
8269             }
8270         }
8271     },
8272
8273     /**
8274      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8275      * @param {Number} startRow The index of the first row in the range
8276      * @param {Number} endRow The index of the last row in the range
8277      */
8278     deselectRange : function(startRow, endRow, preventViewNotify){
8279         if(this.locked) {
8280             return;
8281         }
8282         for(var i = startRow; i <= endRow; i++){
8283             this.deselectRow(i, preventViewNotify);
8284         }
8285     },
8286
8287     /**
8288      * Selects a row.
8289      * @param {Number} row The index of the row to select
8290      * @param {Boolean} keepExisting (optional) True to keep existing selections
8291      */
8292     selectRow : function(index, keepExisting, preventViewNotify){
8293         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8294             return;
8295         }
8296         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8297             if(!keepExisting || this.singleSelect){
8298                 this.clearSelections();
8299             }
8300             var r = this.grid.ds.getAt(index);
8301             this.selections.add(r);
8302             this.last = this.lastActive = index;
8303             if(!preventViewNotify){
8304                 var view = this.grid.view ? this.grid.view : this.grid;
8305                 view.onRowSelect(index);
8306             }
8307             this.fireEvent("rowselect", this, index, r);
8308             this.fireEvent("selectionchange", this);
8309         }
8310     },
8311
8312     /**
8313      * Deselects a row.
8314      * @param {Number} row The index of the row to deselect
8315      */
8316     deselectRow : function(index, preventViewNotify){
8317         if(this.locked) {
8318             return;
8319         }
8320         if(this.last == index){
8321             this.last = false;
8322         }
8323         if(this.lastActive == index){
8324             this.lastActive = false;
8325         }
8326         var r = this.grid.ds.getAt(index);
8327         this.selections.remove(r);
8328         if(!preventViewNotify){
8329             var view = this.grid.view ? this.grid.view : this.grid;
8330             view.onRowDeselect(index);
8331         }
8332         this.fireEvent("rowdeselect", this, index);
8333         this.fireEvent("selectionchange", this);
8334     },
8335
8336     // private
8337     restoreLast : function(){
8338         if(this._last){
8339             this.last = this._last;
8340         }
8341     },
8342
8343     // private
8344     acceptsNav : function(row, col, cm){
8345         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8346     },
8347
8348     // private
8349     onEditorKey : function(field, e){
8350         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8351         if(k == e.TAB){
8352             e.stopEvent();
8353             ed.completeEdit();
8354             if(e.shiftKey){
8355                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8356             }else{
8357                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8358             }
8359         }else if(k == e.ENTER && !e.ctrlKey){
8360             e.stopEvent();
8361             ed.completeEdit();
8362             if(e.shiftKey){
8363                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8364             }else{
8365                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8366             }
8367         }else if(k == e.ESC){
8368             ed.cancelEdit();
8369         }
8370         if(newCell){
8371             g.startEditing(newCell[0], newCell[1]);
8372         }
8373     }
8374 });/*
8375  * Based on:
8376  * Ext JS Library 1.1.1
8377  * Copyright(c) 2006-2007, Ext JS, LLC.
8378  *
8379  * Originally Released Under LGPL - original licence link has changed is not relivant.
8380  *
8381  * Fork - LGPL
8382  * <script type="text/javascript">
8383  */
8384  
8385
8386 /**
8387  * @class Roo.grid.ColumnModel
8388  * @extends Roo.util.Observable
8389  * This is the default implementation of a ColumnModel used by the Grid. It defines
8390  * the columns in the grid.
8391  * <br>Usage:<br>
8392  <pre><code>
8393  var colModel = new Roo.grid.ColumnModel([
8394         {header: "Ticker", width: 60, sortable: true, locked: true},
8395         {header: "Company Name", width: 150, sortable: true},
8396         {header: "Market Cap.", width: 100, sortable: true},
8397         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8398         {header: "Employees", width: 100, sortable: true, resizable: false}
8399  ]);
8400  </code></pre>
8401  * <p>
8402  
8403  * The config options listed for this class are options which may appear in each
8404  * individual column definition.
8405  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8406  * @constructor
8407  * @param {Object} config An Array of column config objects. See this class's
8408  * config objects for details.
8409 */
8410 Roo.grid.ColumnModel = function(config){
8411         /**
8412      * The config passed into the constructor
8413      */
8414     this.config = []; //config;
8415     this.lookup = {};
8416
8417     // if no id, create one
8418     // if the column does not have a dataIndex mapping,
8419     // map it to the order it is in the config
8420     for(var i = 0, len = config.length; i < len; i++){
8421         this.addColumn(config[i]);
8422         
8423     }
8424
8425     /**
8426      * The width of columns which have no width specified (defaults to 100)
8427      * @type Number
8428      */
8429     this.defaultWidth = 100;
8430
8431     /**
8432      * Default sortable of columns which have no sortable specified (defaults to false)
8433      * @type Boolean
8434      */
8435     this.defaultSortable = false;
8436
8437     this.addEvents({
8438         /**
8439              * @event widthchange
8440              * Fires when the width of a column changes.
8441              * @param {ColumnModel} this
8442              * @param {Number} columnIndex The column index
8443              * @param {Number} newWidth The new width
8444              */
8445             "widthchange": true,
8446         /**
8447              * @event headerchange
8448              * Fires when the text of a header changes.
8449              * @param {ColumnModel} this
8450              * @param {Number} columnIndex The column index
8451              * @param {Number} newText The new header text
8452              */
8453             "headerchange": true,
8454         /**
8455              * @event hiddenchange
8456              * Fires when a column is hidden or "unhidden".
8457              * @param {ColumnModel} this
8458              * @param {Number} columnIndex The column index
8459              * @param {Boolean} hidden true if hidden, false otherwise
8460              */
8461             "hiddenchange": true,
8462             /**
8463          * @event columnmoved
8464          * Fires when a column is moved.
8465          * @param {ColumnModel} this
8466          * @param {Number} oldIndex
8467          * @param {Number} newIndex
8468          */
8469         "columnmoved" : true,
8470         /**
8471          * @event columlockchange
8472          * Fires when a column's locked state is changed
8473          * @param {ColumnModel} this
8474          * @param {Number} colIndex
8475          * @param {Boolean} locked true if locked
8476          */
8477         "columnlockchange" : true
8478     });
8479     Roo.grid.ColumnModel.superclass.constructor.call(this);
8480 };
8481 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8482     /**
8483      * @cfg {String} header [required] The header text to display in the Grid view.
8484      */
8485         /**
8486      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8487      */
8488         /**
8489      * @cfg {String} smHeader Header at Bootsrap Small width
8490      */
8491         /**
8492      * @cfg {String} mdHeader Header at Bootsrap Medium width
8493      */
8494         /**
8495      * @cfg {String} lgHeader Header at Bootsrap Large width
8496      */
8497         /**
8498      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8499      */
8500     /**
8501      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8502      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8503      * specified, the column's index is used as an index into the Record's data Array.
8504      */
8505     /**
8506      * @cfg {Number} width  The initial width in pixels of the column. Using this
8507      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8508      */
8509     /**
8510      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8511      * Defaults to the value of the {@link #defaultSortable} property.
8512      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8513      */
8514     /**
8515      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8516      */
8517     /**
8518      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8519      */
8520     /**
8521      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8522      */
8523     /**
8524      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8525      */
8526     /**
8527      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8528      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8529      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8530      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8531      */
8532        /**
8533      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8534      */
8535     /**
8536      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8537      */
8538     /**
8539      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8540      */
8541     /**
8542      * @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)
8543      */
8544     /**
8545      * @cfg {String} tooltip mouse over tooltip text
8546      */
8547     /**
8548      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8549      */
8550     /**
8551      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8552      */
8553     /**
8554      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8555      */
8556     /**
8557      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8558      */
8559         /**
8560      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8561      */
8562     /**
8563      * Returns the id of the column at the specified index.
8564      * @param {Number} index The column index
8565      * @return {String} the id
8566      */
8567     getColumnId : function(index){
8568         return this.config[index].id;
8569     },
8570
8571     /**
8572      * Returns the column for a specified id.
8573      * @param {String} id The column id
8574      * @return {Object} the column
8575      */
8576     getColumnById : function(id){
8577         return this.lookup[id];
8578     },
8579
8580     
8581     /**
8582      * Returns the column Object for a specified dataIndex.
8583      * @param {String} dataIndex The column dataIndex
8584      * @return {Object|Boolean} the column or false if not found
8585      */
8586     getColumnByDataIndex: function(dataIndex){
8587         var index = this.findColumnIndex(dataIndex);
8588         return index > -1 ? this.config[index] : false;
8589     },
8590     
8591     /**
8592      * Returns the index for a specified column id.
8593      * @param {String} id The column id
8594      * @return {Number} the index, or -1 if not found
8595      */
8596     getIndexById : function(id){
8597         for(var i = 0, len = this.config.length; i < len; i++){
8598             if(this.config[i].id == id){
8599                 return i;
8600             }
8601         }
8602         return -1;
8603     },
8604     
8605     /**
8606      * Returns the index for a specified column dataIndex.
8607      * @param {String} dataIndex The column dataIndex
8608      * @return {Number} the index, or -1 if not found
8609      */
8610     
8611     findColumnIndex : function(dataIndex){
8612         for(var i = 0, len = this.config.length; i < len; i++){
8613             if(this.config[i].dataIndex == dataIndex){
8614                 return i;
8615             }
8616         }
8617         return -1;
8618     },
8619     
8620     
8621     moveColumn : function(oldIndex, newIndex){
8622         var c = this.config[oldIndex];
8623         this.config.splice(oldIndex, 1);
8624         this.config.splice(newIndex, 0, c);
8625         this.dataMap = null;
8626         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8627     },
8628
8629     isLocked : function(colIndex){
8630         return this.config[colIndex].locked === true;
8631     },
8632
8633     setLocked : function(colIndex, value, suppressEvent){
8634         if(this.isLocked(colIndex) == value){
8635             return;
8636         }
8637         this.config[colIndex].locked = value;
8638         if(!suppressEvent){
8639             this.fireEvent("columnlockchange", this, colIndex, value);
8640         }
8641     },
8642
8643     getTotalLockedWidth : function(){
8644         var totalWidth = 0;
8645         for(var i = 0; i < this.config.length; i++){
8646             if(this.isLocked(i) && !this.isHidden(i)){
8647                 this.totalWidth += this.getColumnWidth(i);
8648             }
8649         }
8650         return totalWidth;
8651     },
8652
8653     getLockedCount : function(){
8654         for(var i = 0, len = this.config.length; i < len; i++){
8655             if(!this.isLocked(i)){
8656                 return i;
8657             }
8658         }
8659         
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the number of columns.
8665      * @return {Number}
8666      */
8667     getColumnCount : function(visibleOnly){
8668         if(visibleOnly === true){
8669             var c = 0;
8670             for(var i = 0, len = this.config.length; i < len; i++){
8671                 if(!this.isHidden(i)){
8672                     c++;
8673                 }
8674             }
8675             return c;
8676         }
8677         return this.config.length;
8678     },
8679
8680     /**
8681      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8682      * @param {Function} fn
8683      * @param {Object} scope (optional)
8684      * @return {Array} result
8685      */
8686     getColumnsBy : function(fn, scope){
8687         var r = [];
8688         for(var i = 0, len = this.config.length; i < len; i++){
8689             var c = this.config[i];
8690             if(fn.call(scope||this, c, i) === true){
8691                 r[r.length] = c;
8692             }
8693         }
8694         return r;
8695     },
8696
8697     /**
8698      * Returns true if the specified column is sortable.
8699      * @param {Number} col The column index
8700      * @return {Boolean}
8701      */
8702     isSortable : function(col){
8703         if(typeof this.config[col].sortable == "undefined"){
8704             return this.defaultSortable;
8705         }
8706         return this.config[col].sortable;
8707     },
8708
8709     /**
8710      * Returns the rendering (formatting) function defined for the column.
8711      * @param {Number} col The column index.
8712      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8713      */
8714     getRenderer : function(col){
8715         if(!this.config[col].renderer){
8716             return Roo.grid.ColumnModel.defaultRenderer;
8717         }
8718         return this.config[col].renderer;
8719     },
8720
8721     /**
8722      * Sets the rendering (formatting) function for a column.
8723      * @param {Number} col The column index
8724      * @param {Function} fn The function to use to process the cell's raw data
8725      * to return HTML markup for the grid view. The render function is called with
8726      * the following parameters:<ul>
8727      * <li>Data value.</li>
8728      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8729      * <li>css A CSS style string to apply to the table cell.</li>
8730      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8731      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8732      * <li>Row index</li>
8733      * <li>Column index</li>
8734      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8735      */
8736     setRenderer : function(col, fn){
8737         this.config[col].renderer = fn;
8738     },
8739
8740     /**
8741      * Returns the width for the specified column.
8742      * @param {Number} col The column index
8743      * @param (optional) {String} gridSize bootstrap width size.
8744      * @return {Number}
8745      */
8746     getColumnWidth : function(col, gridSize)
8747         {
8748                 var cfg = this.config[col];
8749                 
8750                 if (typeof(gridSize) == 'undefined') {
8751                         return cfg.width * 1 || this.defaultWidth;
8752                 }
8753                 if (gridSize === false) { // if we set it..
8754                         return cfg.width || false;
8755                 }
8756                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8757                 
8758                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8759                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8760                                 continue;
8761                         }
8762                         return cfg[ sizes[i] ];
8763                 }
8764                 return 1;
8765                 
8766     },
8767
8768     /**
8769      * Sets the width for a column.
8770      * @param {Number} col The column index
8771      * @param {Number} width The new width
8772      */
8773     setColumnWidth : function(col, width, suppressEvent){
8774         this.config[col].width = width;
8775         this.totalWidth = null;
8776         if(!suppressEvent){
8777              this.fireEvent("widthchange", this, col, width);
8778         }
8779     },
8780
8781     /**
8782      * Returns the total width of all columns.
8783      * @param {Boolean} includeHidden True to include hidden column widths
8784      * @return {Number}
8785      */
8786     getTotalWidth : function(includeHidden){
8787         if(!this.totalWidth){
8788             this.totalWidth = 0;
8789             for(var i = 0, len = this.config.length; i < len; i++){
8790                 if(includeHidden || !this.isHidden(i)){
8791                     this.totalWidth += this.getColumnWidth(i);
8792                 }
8793             }
8794         }
8795         return this.totalWidth;
8796     },
8797
8798     /**
8799      * Returns the header for the specified column.
8800      * @param {Number} col The column index
8801      * @return {String}
8802      */
8803     getColumnHeader : function(col){
8804         return this.config[col].header;
8805     },
8806
8807     /**
8808      * Sets the header for a column.
8809      * @param {Number} col The column index
8810      * @param {String} header The new header
8811      */
8812     setColumnHeader : function(col, header){
8813         this.config[col].header = header;
8814         this.fireEvent("headerchange", this, col, header);
8815     },
8816
8817     /**
8818      * Returns the tooltip for the specified column.
8819      * @param {Number} col The column index
8820      * @return {String}
8821      */
8822     getColumnTooltip : function(col){
8823             return this.config[col].tooltip;
8824     },
8825     /**
8826      * Sets the tooltip for a column.
8827      * @param {Number} col The column index
8828      * @param {String} tooltip The new tooltip
8829      */
8830     setColumnTooltip : function(col, tooltip){
8831             this.config[col].tooltip = tooltip;
8832     },
8833
8834     /**
8835      * Returns the dataIndex for the specified column.
8836      * @param {Number} col The column index
8837      * @return {Number}
8838      */
8839     getDataIndex : function(col){
8840         return this.config[col].dataIndex;
8841     },
8842
8843     /**
8844      * Sets the dataIndex for a column.
8845      * @param {Number} col The column index
8846      * @param {Number} dataIndex The new dataIndex
8847      */
8848     setDataIndex : function(col, dataIndex){
8849         this.config[col].dataIndex = dataIndex;
8850     },
8851
8852     
8853     
8854     /**
8855      * Returns true if the cell is editable.
8856      * @param {Number} colIndex The column index
8857      * @param {Number} rowIndex The row index - this is nto actually used..?
8858      * @return {Boolean}
8859      */
8860     isCellEditable : function(colIndex, rowIndex){
8861         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8862     },
8863
8864     /**
8865      * Returns the editor defined for the cell/column.
8866      * return false or null to disable editing.
8867      * @param {Number} colIndex The column index
8868      * @param {Number} rowIndex The row index
8869      * @return {Object}
8870      */
8871     getCellEditor : function(colIndex, rowIndex){
8872         return this.config[colIndex].editor;
8873     },
8874
8875     /**
8876      * Sets if a column is editable.
8877      * @param {Number} col The column index
8878      * @param {Boolean} editable True if the column is editable
8879      */
8880     setEditable : function(col, editable){
8881         this.config[col].editable = editable;
8882     },
8883
8884
8885     /**
8886      * Returns true if the column is hidden.
8887      * @param {Number} colIndex The column index
8888      * @return {Boolean}
8889      */
8890     isHidden : function(colIndex){
8891         return this.config[colIndex].hidden;
8892     },
8893
8894
8895     /**
8896      * Returns true if the column width cannot be changed
8897      */
8898     isFixed : function(colIndex){
8899         return this.config[colIndex].fixed;
8900     },
8901
8902     /**
8903      * Returns true if the column can be resized
8904      * @return {Boolean}
8905      */
8906     isResizable : function(colIndex){
8907         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8908     },
8909     /**
8910      * Sets if a column is hidden.
8911      * @param {Number} colIndex The column index
8912      * @param {Boolean} hidden True if the column is hidden
8913      */
8914     setHidden : function(colIndex, hidden){
8915         this.config[colIndex].hidden = hidden;
8916         this.totalWidth = null;
8917         this.fireEvent("hiddenchange", this, colIndex, hidden);
8918     },
8919
8920     /**
8921      * Sets the editor for a column.
8922      * @param {Number} col The column index
8923      * @param {Object} editor The editor object
8924      */
8925     setEditor : function(col, editor){
8926         this.config[col].editor = editor;
8927     },
8928     /**
8929      * Add a column (experimental...) - defaults to adding to the end..
8930      * @param {Object} config 
8931     */
8932     addColumn : function(c)
8933     {
8934     
8935         var i = this.config.length;
8936         this.config[i] = c;
8937         
8938         if(typeof c.dataIndex == "undefined"){
8939             c.dataIndex = i;
8940         }
8941         if(typeof c.renderer == "string"){
8942             c.renderer = Roo.util.Format[c.renderer];
8943         }
8944         if(typeof c.id == "undefined"){
8945             c.id = Roo.id();
8946         }
8947         if(c.editor && c.editor.xtype){
8948             c.editor  = Roo.factory(c.editor, Roo.grid);
8949         }
8950         if(c.editor && c.editor.isFormField){
8951             c.editor = new Roo.grid.GridEditor(c.editor);
8952         }
8953         this.lookup[c.id] = c;
8954     }
8955     
8956 });
8957
8958 Roo.grid.ColumnModel.defaultRenderer = function(value)
8959 {
8960     if(typeof value == "object") {
8961         return value;
8962     }
8963         if(typeof value == "string" && value.length < 1){
8964             return "&#160;";
8965         }
8966     
8967         return String.format("{0}", value);
8968 };
8969
8970 // Alias for backwards compatibility
8971 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8972 /*
8973  * Based on:
8974  * Ext JS Library 1.1.1
8975  * Copyright(c) 2006-2007, Ext JS, LLC.
8976  *
8977  * Originally Released Under LGPL - original licence link has changed is not relivant.
8978  *
8979  * Fork - LGPL
8980  * <script type="text/javascript">
8981  */
8982  
8983 /**
8984  * @class Roo.LoadMask
8985  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8986  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8987  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8988  * element's UpdateManager load indicator and will be destroyed after the initial load.
8989  * @constructor
8990  * Create a new LoadMask
8991  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8992  * @param {Object} config The config object
8993  */
8994 Roo.LoadMask = function(el, config){
8995     this.el = Roo.get(el);
8996     Roo.apply(this, config);
8997     if(this.store){
8998         this.store.on('beforeload', this.onBeforeLoad, this);
8999         this.store.on('load', this.onLoad, this);
9000         this.store.on('loadexception', this.onLoadException, this);
9001         this.removeMask = false;
9002     }else{
9003         var um = this.el.getUpdateManager();
9004         um.showLoadIndicator = false; // disable the default indicator
9005         um.on('beforeupdate', this.onBeforeLoad, this);
9006         um.on('update', this.onLoad, this);
9007         um.on('failure', this.onLoad, this);
9008         this.removeMask = true;
9009     }
9010 };
9011
9012 Roo.LoadMask.prototype = {
9013     /**
9014      * @cfg {Boolean} removeMask
9015      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9016      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9017      */
9018     removeMask : false,
9019     /**
9020      * @cfg {String} msg
9021      * The text to display in a centered loading message box (defaults to 'Loading...')
9022      */
9023     msg : 'Loading...',
9024     /**
9025      * @cfg {String} msgCls
9026      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9027      */
9028     msgCls : 'x-mask-loading',
9029
9030     /**
9031      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9032      * @type Boolean
9033      */
9034     disabled: false,
9035
9036     /**
9037      * Disables the mask to prevent it from being displayed
9038      */
9039     disable : function(){
9040        this.disabled = true;
9041     },
9042
9043     /**
9044      * Enables the mask so that it can be displayed
9045      */
9046     enable : function(){
9047         this.disabled = false;
9048     },
9049     
9050     onLoadException : function()
9051     {
9052         Roo.log(arguments);
9053         
9054         if (typeof(arguments[3]) != 'undefined') {
9055             Roo.MessageBox.alert("Error loading",arguments[3]);
9056         } 
9057         /*
9058         try {
9059             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9060                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9061             }   
9062         } catch(e) {
9063             
9064         }
9065         */
9066     
9067         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9068     },
9069     // private
9070     onLoad : function()
9071     {
9072         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9073     },
9074
9075     // private
9076     onBeforeLoad : function(){
9077         if(!this.disabled){
9078             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9079         }
9080     },
9081
9082     // private
9083     destroy : function(){
9084         if(this.store){
9085             this.store.un('beforeload', this.onBeforeLoad, this);
9086             this.store.un('load', this.onLoad, this);
9087             this.store.un('loadexception', this.onLoadException, this);
9088         }else{
9089             var um = this.el.getUpdateManager();
9090             um.un('beforeupdate', this.onBeforeLoad, this);
9091             um.un('update', this.onLoad, this);
9092             um.un('failure', this.onLoad, this);
9093         }
9094     }
9095 };/**
9096  * @class Roo.bootstrap.Table
9097  * @licence LGBL
9098  * @extends Roo.bootstrap.Component
9099  * @children Roo.bootstrap.TableBody
9100  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9101  * Similar to Roo.grid.Grid
9102  * <pre><code>
9103  var table = Roo.factory({
9104     xtype : 'Table',
9105     xns : Roo.bootstrap,
9106     autoSizeColumns: true,
9107     
9108     
9109     store : {
9110         xtype : 'Store',
9111         xns : Roo.data,
9112         remoteSort : true,
9113         sortInfo : { direction : 'ASC', field: 'name' },
9114         proxy : {
9115            xtype : 'HttpProxy',
9116            xns : Roo.data,
9117            method : 'GET',
9118            url : 'https://example.com/some.data.url.json'
9119         },
9120         reader : {
9121            xtype : 'JsonReader',
9122            xns : Roo.data,
9123            fields : [ 'id', 'name', whatever' ],
9124            id : 'id',
9125            root : 'data'
9126         }
9127     },
9128     cm : [
9129         {
9130             xtype : 'ColumnModel',
9131             xns : Roo.grid,
9132             align : 'center',
9133             cursor : 'pointer',
9134             dataIndex : 'is_in_group',
9135             header : "Name",
9136             sortable : true,
9137             renderer : function(v, x , r) {  
9138             
9139                 return String.format("{0}", v)
9140             }
9141             width : 3
9142         } // more columns..
9143     ],
9144     selModel : {
9145         xtype : 'RowSelectionModel',
9146         xns : Roo.bootstrap.Table
9147         // you can add listeners to catch selection change here....
9148     }
9149      
9150
9151  });
9152  // set any options
9153  grid.render(Roo.get("some-div"));
9154 </code></pre>
9155
9156 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9157
9158
9159
9160  *
9161  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9162  * @cfg {Roo.data.Store} store The data store to use
9163  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9164  * 
9165  * @cfg {String} cls table class
9166  *
9167  *
9168  * @cfg {string} empty_results  Text to display for no results 
9169  * @cfg {boolean} striped Should the rows be alternative striped
9170  * @cfg {boolean} bordered Add borders to the table
9171  * @cfg {boolean} hover Add hover highlighting
9172  * @cfg {boolean} condensed Format condensed
9173  * @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,
9174  *                also adds table-responsive (see bootstrap docs for details)
9175  * @cfg {Boolean} loadMask (true|false) default false
9176  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9177  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9178  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9179  * @cfg {Boolean} rowSelection (true|false) default false
9180  * @cfg {Boolean} cellSelection (true|false) default false
9181  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9182  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9183  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9184  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9185  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9186  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9187  *
9188  * 
9189  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9190  * 
9191  * @constructor
9192  * Create a new Table
9193  * @param {Object} config The config object
9194  */
9195
9196 Roo.bootstrap.Table = function(config)
9197 {
9198     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9199      
9200     // BC...
9201     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9202     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9203     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9204     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9205     
9206     this.view = this; // compat with grid.
9207     
9208     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9209     if (this.sm) {
9210         this.sm.grid = this;
9211         this.selModel = Roo.factory(this.sm, Roo.grid);
9212         this.sm = this.selModel;
9213         this.sm.xmodule = this.xmodule || false;
9214     }
9215     
9216     if (this.cm && typeof(this.cm.config) == 'undefined') {
9217         this.colModel = new Roo.grid.ColumnModel(this.cm);
9218         this.cm = this.colModel;
9219         this.cm.xmodule = this.xmodule || false;
9220     }
9221     if (this.store) {
9222         this.store= Roo.factory(this.store, Roo.data);
9223         this.ds = this.store;
9224         this.ds.xmodule = this.xmodule || false;
9225          
9226     }
9227     if (this.footer && this.store) {
9228         this.footer.dataSource = this.ds;
9229         this.footer = Roo.factory(this.footer);
9230     }
9231     
9232     /** @private */
9233     this.addEvents({
9234         /**
9235          * @event cellclick
9236          * Fires when a cell is clicked
9237          * @param {Roo.bootstrap.Table} this
9238          * @param {Roo.Element} el
9239          * @param {Number} rowIndex
9240          * @param {Number} columnIndex
9241          * @param {Roo.EventObject} e
9242          */
9243         "cellclick" : true,
9244         /**
9245          * @event celldblclick
9246          * Fires when a cell is double clicked
9247          * @param {Roo.bootstrap.Table} this
9248          * @param {Roo.Element} el
9249          * @param {Number} rowIndex
9250          * @param {Number} columnIndex
9251          * @param {Roo.EventObject} e
9252          */
9253         "celldblclick" : true,
9254         /**
9255          * @event rowclick
9256          * Fires when a row is clicked
9257          * @param {Roo.bootstrap.Table} this
9258          * @param {Roo.Element} el
9259          * @param {Number} rowIndex
9260          * @param {Roo.EventObject} e
9261          */
9262         "rowclick" : true,
9263         /**
9264          * @event rowdblclick
9265          * Fires when a row is double clicked
9266          * @param {Roo.bootstrap.Table} this
9267          * @param {Roo.Element} el
9268          * @param {Number} rowIndex
9269          * @param {Roo.EventObject} e
9270          */
9271         "rowdblclick" : true,
9272         /**
9273          * @event mouseover
9274          * Fires when a mouseover occur
9275          * @param {Roo.bootstrap.Table} this
9276          * @param {Roo.Element} el
9277          * @param {Number} rowIndex
9278          * @param {Number} columnIndex
9279          * @param {Roo.EventObject} e
9280          */
9281         "mouseover" : true,
9282         /**
9283          * @event mouseout
9284          * Fires when a mouseout occur
9285          * @param {Roo.bootstrap.Table} this
9286          * @param {Roo.Element} el
9287          * @param {Number} rowIndex
9288          * @param {Number} columnIndex
9289          * @param {Roo.EventObject} e
9290          */
9291         "mouseout" : true,
9292         /**
9293          * @event rowclass
9294          * Fires when a row is rendered, so you can change add a style to it.
9295          * @param {Roo.bootstrap.Table} this
9296          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9297          */
9298         'rowclass' : true,
9299           /**
9300          * @event rowsrendered
9301          * Fires when all the  rows have been rendered
9302          * @param {Roo.bootstrap.Table} this
9303          */
9304         'rowsrendered' : true,
9305         /**
9306          * @event contextmenu
9307          * The raw contextmenu event for the entire grid.
9308          * @param {Roo.EventObject} e
9309          */
9310         "contextmenu" : true,
9311         /**
9312          * @event rowcontextmenu
9313          * Fires when a row is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} rowIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "rowcontextmenu" : true,
9319         /**
9320          * @event cellcontextmenu
9321          * Fires when a cell is right clicked
9322          * @param {Roo.bootstrap.Table} this
9323          * @param {Number} rowIndex
9324          * @param {Number} cellIndex
9325          * @param {Roo.EventObject} e
9326          */
9327          "cellcontextmenu" : true,
9328          /**
9329          * @event headercontextmenu
9330          * Fires when a header is right clicked
9331          * @param {Roo.bootstrap.Table} this
9332          * @param {Number} columnIndex
9333          * @param {Roo.EventObject} e
9334          */
9335         "headercontextmenu" : true,
9336         /**
9337          * @event mousedown
9338          * The raw mousedown event for the entire grid.
9339          * @param {Roo.EventObject} e
9340          */
9341         "mousedown" : true
9342         
9343     });
9344 };
9345
9346 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9347     
9348     cls: false,
9349     
9350     empty_results : '',
9351     striped : false,
9352     scrollBody : false,
9353     bordered: false,
9354     hover:  false,
9355     condensed : false,
9356     responsive : false,
9357     sm : false,
9358     cm : false,
9359     store : false,
9360     loadMask : false,
9361     footerShow : true,
9362     footerRow : false,
9363     headerShow : true,
9364     enableColumnResize: true,
9365     disableAutoSize: false,
9366   
9367     rowSelection : false,
9368     cellSelection : false,
9369     layout : false,
9370
9371     minColumnWidth : 50,
9372     
9373     // Roo.Element - the tbody
9374     bodyEl: false,  // <tbody> Roo.Element - thead element    
9375     headEl: false,  // <thead> Roo.Element - thead element
9376     resizeProxy : false, // proxy element for dragging?
9377
9378
9379     
9380     container: false, // used by gridpanel...
9381     
9382     lazyLoad : false,
9383     
9384     CSS : Roo.util.CSS,
9385     
9386     auto_hide_footer : false,
9387     
9388     view: false, // actually points to this..
9389     
9390     getAutoCreate : function()
9391     {
9392         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9393         
9394         cfg = {
9395             tag: 'table',
9396             cls : 'table', 
9397             cn : []
9398         };
9399         // this get's auto added by panel.Grid
9400         if (this.scrollBody) {
9401             cfg.cls += ' table-body-fixed';
9402         }    
9403         if (this.striped) {
9404             cfg.cls += ' table-striped';
9405         }
9406         
9407         if (this.hover) {
9408             cfg.cls += ' table-hover';
9409         }
9410         if (this.bordered) {
9411             cfg.cls += ' table-bordered';
9412         }
9413         if (this.condensed) {
9414             cfg.cls += ' table-condensed';
9415         }
9416         
9417         if (this.responsive) {
9418             cfg.cls += ' table-responsive';
9419         }
9420         
9421         if (this.cls) {
9422             cfg.cls+=  ' ' +this.cls;
9423         }
9424         
9425         
9426         
9427         if (this.layout) {
9428             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9429         }
9430         
9431         if(this.store || this.cm){
9432             if(this.headerShow){
9433                 cfg.cn.push(this.renderHeader());
9434             }
9435             
9436             cfg.cn.push(this.renderBody());
9437             
9438             if(this.footerShow || this.footerRow){
9439                 cfg.cn.push(this.renderFooter());
9440             }
9441
9442             // where does this come from?
9443             //cfg.cls+=  ' TableGrid';
9444         }
9445         
9446         return { cn : [ cfg ] };
9447     },
9448     
9449     initEvents : function()
9450     {   
9451         if(!this.store || !this.cm){
9452             return;
9453         }
9454         if (this.selModel) {
9455             this.selModel.initEvents();
9456         }
9457         
9458         
9459         //Roo.log('initEvents with ds!!!!');
9460         
9461         this.bodyEl = this.el.select('tbody', true).first();
9462         this.headEl = this.el.select('thead', true).first();
9463         this.mainFoot = this.el.select('tfoot', true).first();
9464         
9465         
9466         
9467         
9468         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9469             e.on('click', this.sort, this);
9470         }, this);
9471         
9472         
9473         // why is this done????? = it breaks dialogs??
9474         //this.parent().el.setStyle('position', 'relative');
9475         
9476         
9477         if (this.footer) {
9478             this.footer.parentId = this.id;
9479             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9480             
9481             if(this.lazyLoad){
9482                 this.el.select('tfoot tr td').first().addClass('hide');
9483             }
9484         } 
9485         
9486         if(this.loadMask) {
9487             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9488         }
9489         
9490         this.store.on('load', this.onLoad, this);
9491         this.store.on('beforeload', this.onBeforeLoad, this);
9492         this.store.on('update', this.onUpdate, this);
9493         this.store.on('add', this.onAdd, this);
9494         this.store.on("clear", this.clear, this);
9495         
9496         this.el.on("contextmenu", this.onContextMenu, this);
9497         
9498         
9499         this.cm.on("headerchange", this.onHeaderChange, this);
9500         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9501
9502  //?? does bodyEl get replaced on render?
9503         this.bodyEl.on("click", this.onClick, this);
9504         this.bodyEl.on("dblclick", this.onDblClick, this);        
9505         this.bodyEl.on('scroll', this.onBodyScroll, this);
9506
9507         // guessing mainbody will work - this relays usually caught by selmodel at present.
9508         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9509   
9510   
9511         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9512         
9513   
9514         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9515             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9516         }
9517         
9518         this.initCSS();
9519     },
9520     // Compatibility with grid - we implement all the view features at present.
9521     getView : function()
9522     {
9523         return this;
9524     },
9525     
9526     initCSS : function()
9527     {
9528         if(this.disableAutoSize) {
9529             return;
9530         }
9531         
9532         var cm = this.cm, styles = [];
9533         this.CSS.removeStyleSheet(this.id + '-cssrules');
9534         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9535         // we can honour xs/sm/md/xl  as widths...
9536         // we first have to decide what widht we are currently at...
9537         var sz = Roo.getGridSize();
9538         
9539         var total = 0;
9540         var last = -1;
9541         var cols = []; // visable cols.
9542         var total_abs = 0;
9543         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9544             var w = cm.getColumnWidth(i, false);
9545             if(cm.isHidden(i)){
9546                 cols.push( { rel : false, abs : 0 });
9547                 continue;
9548             }
9549             if (w !== false) {
9550                 cols.push( { rel : false, abs : w });
9551                 total_abs += w;
9552                 last = i; // not really..
9553                 continue;
9554             }
9555             var w = cm.getColumnWidth(i, sz);
9556             if (w > 0) {
9557                 last = i
9558             }
9559             total += w;
9560             cols.push( { rel : w, abs : false });
9561         }
9562         
9563         var avail = this.bodyEl.dom.clientWidth - total_abs;
9564         
9565         var unitWidth = Math.floor(avail / total);
9566         var rem = avail - (unitWidth * total);
9567         
9568         var hidden, width, pos = 0 , splithide , left;
9569         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9570             
9571             hidden = 'display:none;';
9572             left = '';
9573             width  = 'width:0px;';
9574             splithide = '';
9575             if(!cm.isHidden(i)){
9576                 hidden = '';
9577                 
9578                 
9579                 // we can honour xs/sm/md/xl ?
9580                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9581                 if (w===0) {
9582                     hidden = 'display:none;';
9583                 }
9584                 // width should return a small number...
9585                 if (i == last) {
9586                     w+=rem; // add the remaining with..
9587                 }
9588                 pos += w;
9589                 left = "left:" + (pos -4) + "px;";
9590                 width = "width:" + w+ "px;";
9591                 
9592             }
9593             if (this.responsive) {
9594                 width = '';
9595                 left = '';
9596                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9597                 splithide = 'display: none;';
9598             }
9599             
9600             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9601             if (this.headEl) {
9602                 if (i == last) {
9603                     splithide = 'display:none;';
9604                 }
9605                 
9606                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9607                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9608                             // this is the popover version..
9609                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9610                 );
9611             }
9612             
9613         }
9614         //Roo.log(styles.join(''));
9615         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9616         
9617     },
9618     
9619     
9620     
9621     onContextMenu : function(e, t)
9622     {
9623         this.processEvent("contextmenu", e);
9624     },
9625     
9626     processEvent : function(name, e)
9627     {
9628         if (name != 'touchstart' ) {
9629             this.fireEvent(name, e);    
9630         }
9631         
9632         var t = e.getTarget();
9633         
9634         var cell = Roo.get(t);
9635         
9636         if(!cell){
9637             return;
9638         }
9639         
9640         if(cell.findParent('tfoot', false, true)){
9641             return;
9642         }
9643         
9644         if(cell.findParent('thead', false, true)){
9645             
9646             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9647                 cell = Roo.get(t).findParent('th', false, true);
9648                 if (!cell) {
9649                     Roo.log("failed to find th in thead?");
9650                     Roo.log(e.getTarget());
9651                     return;
9652                 }
9653             }
9654             
9655             var cellIndex = cell.dom.cellIndex;
9656             
9657             var ename = name == 'touchstart' ? 'click' : name;
9658             this.fireEvent("header" + ename, this, cellIndex, e);
9659             
9660             return;
9661         }
9662         
9663         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9664             cell = Roo.get(t).findParent('td', false, true);
9665             if (!cell) {
9666                 Roo.log("failed to find th in tbody?");
9667                 Roo.log(e.getTarget());
9668                 return;
9669             }
9670         }
9671         
9672         var row = cell.findParent('tr', false, true);
9673         var cellIndex = cell.dom.cellIndex;
9674         var rowIndex = row.dom.rowIndex - 1;
9675         
9676         if(row !== false){
9677             
9678             this.fireEvent("row" + name, this, rowIndex, e);
9679             
9680             if(cell !== false){
9681             
9682                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9683             }
9684         }
9685         
9686     },
9687     
9688     onMouseover : function(e, el)
9689     {
9690         var cell = Roo.get(el);
9691         
9692         if(!cell){
9693             return;
9694         }
9695         
9696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697             cell = cell.findParent('td', false, true);
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         var cellIndex = cell.dom.cellIndex;
9702         var rowIndex = row.dom.rowIndex - 1; // start from 0
9703         
9704         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9705         
9706     },
9707     
9708     onMouseout : function(e, el)
9709     {
9710         var cell = Roo.get(el);
9711         
9712         if(!cell){
9713             return;
9714         }
9715         
9716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717             cell = cell.findParent('td', false, true);
9718         }
9719         
9720         var row = cell.findParent('tr', false, true);
9721         var cellIndex = cell.dom.cellIndex;
9722         var rowIndex = row.dom.rowIndex - 1; // start from 0
9723         
9724         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9725         
9726     },
9727     
9728     onClick : function(e, el)
9729     {
9730         var cell = Roo.get(el);
9731         
9732         if(!cell || (!this.cellSelection && !this.rowSelection)){
9733             return;
9734         }
9735         
9736         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9737             cell = cell.findParent('td', false, true);
9738         }
9739         
9740         if(!cell || typeof(cell) == 'undefined'){
9741             return;
9742         }
9743         
9744         var row = cell.findParent('tr', false, true);
9745         
9746         if(!row || typeof(row) == 'undefined'){
9747             return;
9748         }
9749         
9750         var cellIndex = cell.dom.cellIndex;
9751         var rowIndex = this.getRowIndex(row);
9752         
9753         // why??? - should these not be based on SelectionModel?
9754         //if(this.cellSelection){
9755             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9756         //}
9757         
9758         //if(this.rowSelection){
9759             this.fireEvent('rowclick', this, row, rowIndex, e);
9760         //}
9761          
9762     },
9763         
9764     onDblClick : function(e,el)
9765     {
9766         var cell = Roo.get(el);
9767         
9768         if(!cell || (!this.cellSelection && !this.rowSelection)){
9769             return;
9770         }
9771         
9772         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9773             cell = cell.findParent('td', false, true);
9774         }
9775         
9776         if(!cell || typeof(cell) == 'undefined'){
9777             return;
9778         }
9779         
9780         var row = cell.findParent('tr', false, true);
9781         
9782         if(!row || typeof(row) == 'undefined'){
9783             return;
9784         }
9785         
9786         var cellIndex = cell.dom.cellIndex;
9787         var rowIndex = this.getRowIndex(row);
9788         
9789         if(this.cellSelection){
9790             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9791         }
9792         
9793         if(this.rowSelection){
9794             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9795         }
9796     },
9797     findRowIndex : function(el)
9798     {
9799         var cell = Roo.get(el);
9800         if(!cell) {
9801             return false;
9802         }
9803         var row = cell.findParent('tr', false, true);
9804         
9805         if(!row || typeof(row) == 'undefined'){
9806             return false;
9807         }
9808         return this.getRowIndex(row);
9809     },
9810     sort : function(e,el)
9811     {
9812         var col = Roo.get(el);
9813         
9814         if(!col.hasClass('sortable')){
9815             return;
9816         }
9817         
9818         var sort = col.attr('sort');
9819         var dir = 'ASC';
9820         
9821         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9822             dir = 'DESC';
9823         }
9824         
9825         this.store.sortInfo = {field : sort, direction : dir};
9826         
9827         if (this.footer) {
9828             Roo.log("calling footer first");
9829             this.footer.onClick('first');
9830         } else {
9831         
9832             this.store.load({ params : { start : 0 } });
9833         }
9834     },
9835     
9836     renderHeader : function()
9837     {
9838         var header = {
9839             tag: 'thead',
9840             cn : []
9841         };
9842         
9843         var cm = this.cm;
9844         this.totalWidth = 0;
9845         
9846         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9847             
9848             var config = cm.config[i];
9849             
9850             var c = {
9851                 tag: 'th',
9852                 cls : 'x-hcol-' + i,
9853                 style : '',
9854                 
9855                 html: cm.getColumnHeader(i)
9856             };
9857             
9858             var tooltip = cm.getColumnTooltip(i);
9859             if (tooltip) {
9860                 c.tooltip = tooltip;
9861             }
9862             
9863             
9864             var hh = '';
9865             
9866             if(typeof(config.sortable) != 'undefined' && config.sortable){
9867                 c.cls += ' sortable';
9868                 c.html = '<i class="fa"></i>' + c.html;
9869             }
9870             
9871             // could use BS4 hidden-..-down 
9872             
9873             if(typeof(config.lgHeader) != 'undefined'){
9874                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9875             }
9876             
9877             if(typeof(config.mdHeader) != 'undefined'){
9878                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9879             }
9880             
9881             if(typeof(config.smHeader) != 'undefined'){
9882                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9883             }
9884             
9885             if(typeof(config.xsHeader) != 'undefined'){
9886                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9887             }
9888             
9889             if(hh.length){
9890                 c.html = hh;
9891             }
9892             
9893             if(typeof(config.tooltip) != 'undefined'){
9894                 c.tooltip = config.tooltip;
9895             }
9896             
9897             if(typeof(config.colspan) != 'undefined'){
9898                 c.colspan = config.colspan;
9899             }
9900             
9901             // hidden is handled by CSS now
9902             
9903             if(typeof(config.dataIndex) != 'undefined'){
9904                 c.sort = config.dataIndex;
9905             }
9906             
9907            
9908             
9909             if(typeof(config.align) != 'undefined' && config.align.length){
9910                 c.style += ' text-align:' + config.align + ';';
9911             }
9912             
9913             /* width is done in CSS
9914              *if(typeof(config.width) != 'undefined'){
9915                 c.style += ' width:' + config.width + 'px;';
9916                 this.totalWidth += config.width;
9917             } else {
9918                 this.totalWidth += 100; // assume minimum of 100 per column?
9919             }
9920             */
9921             
9922             if(typeof(config.cls) != 'undefined'){
9923                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9924             }
9925             // this is the bit that doesnt reall work at all...
9926             
9927             if (this.responsive) {
9928                  
9929             
9930                 ['xs','sm','md','lg'].map(function(size){
9931                     
9932                     if(typeof(config[size]) == 'undefined'){
9933                         return;
9934                     }
9935                      
9936                     if (!config[size]) { // 0 = hidden
9937                         // BS 4 '0' is treated as hide that column and below.
9938                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9939                         return;
9940                     }
9941                     
9942                     c.cls += ' col-' + size + '-' + config[size] + (
9943                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9944                     );
9945                     
9946                     
9947                 });
9948             }
9949             // at the end?
9950             
9951             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9952             
9953             
9954             
9955             
9956             header.cn.push(c)
9957         }
9958         
9959         return header;
9960     },
9961     
9962     renderBody : function()
9963     {
9964         var body = {
9965             tag: 'tbody',
9966             cn : [
9967                 {
9968                     tag: 'tr',
9969                     cn : [
9970                         {
9971                             tag : 'td',
9972                             colspan :  this.cm.getColumnCount()
9973                         }
9974                     ]
9975                 }
9976             ]
9977         };
9978         
9979         return body;
9980     },
9981     
9982     renderFooter : function()
9983     {
9984         var footer = {
9985             tag: 'tfoot',
9986             cn : [
9987                 {
9988                     tag: 'tr',
9989                     cn : [
9990                         {
9991                             tag : 'td',
9992                             colspan :  this.cm.getColumnCount()
9993                         }
9994                     ]
9995                 }
9996             ]
9997         };
9998         
9999         return footer;
10000     },
10001     
10002     onLoad : function()
10003     {
10004 //        Roo.log('ds onload');
10005         this.clear();
10006         
10007         var _this = this;
10008         var cm = this.cm;
10009         var ds = this.store;
10010         
10011         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10012             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10013             if (_this.store.sortInfo) {
10014                     
10015                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10016                     e.select('i', true).addClass(['fa-arrow-up']);
10017                 }
10018                 
10019                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10020                     e.select('i', true).addClass(['fa-arrow-down']);
10021                 }
10022             }
10023         });
10024         
10025         var tbody =  this.bodyEl;
10026               
10027         if(ds.getCount() > 0){
10028             ds.data.each(function(d,rowIndex){
10029                 var row =  this.renderRow(cm, ds, rowIndex);
10030                 
10031                 tbody.createChild(row);
10032                 
10033                 var _this = this;
10034                 
10035                 if(row.cellObjects.length){
10036                     Roo.each(row.cellObjects, function(r){
10037                         _this.renderCellObject(r);
10038                     })
10039                 }
10040                 
10041             }, this);
10042         } else if (this.empty_results.length) {
10043             this.el.mask(this.empty_results, 'no-spinner');
10044         }
10045         
10046         var tfoot = this.el.select('tfoot', true).first();
10047         
10048         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10049             
10050             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10051             
10052             var total = this.ds.getTotalCount();
10053             
10054             if(this.footer.pageSize < total){
10055                 this.mainFoot.show();
10056             }
10057         }
10058
10059         if(!this.footerShow && this.footerRow) {
10060
10061             var tr = {
10062                 tag : 'tr',
10063                 cn : []
10064             };
10065
10066             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10067                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10068                 var td = {
10069                     tag: 'td',
10070                     cls : ' x-fcol-' + i,
10071                     html: footer
10072                 };
10073
10074                 tr.cn.push(td);
10075                 
10076             }
10077             
10078             tfoot.dom.innerHTML = '';
10079
10080             tfoot.createChild(tr);
10081         }
10082         
10083         Roo.each(this.el.select('tbody td', true).elements, function(e){
10084             e.on('mouseover', _this.onMouseover, _this);
10085         });
10086         
10087         Roo.each(this.el.select('tbody td', true).elements, function(e){
10088             e.on('mouseout', _this.onMouseout, _this);
10089         });
10090         this.fireEvent('rowsrendered', this);
10091         
10092         this.autoSize();
10093         
10094         this.initCSS(); /// resize cols
10095
10096         
10097     },
10098     
10099     
10100     onUpdate : function(ds,record)
10101     {
10102         this.refreshRow(record);
10103         this.autoSize();
10104     },
10105     
10106     onRemove : function(ds, record, index, isUpdate){
10107         if(isUpdate !== true){
10108             this.fireEvent("beforerowremoved", this, index, record);
10109         }
10110         var bt = this.bodyEl.dom;
10111         
10112         var rows = this.el.select('tbody > tr', true).elements;
10113         
10114         if(typeof(rows[index]) != 'undefined'){
10115             bt.removeChild(rows[index].dom);
10116         }
10117         
10118 //        if(bt.rows[index]){
10119 //            bt.removeChild(bt.rows[index]);
10120 //        }
10121         
10122         if(isUpdate !== true){
10123             //this.stripeRows(index);
10124             //this.syncRowHeights(index, index);
10125             //this.layout();
10126             this.fireEvent("rowremoved", this, index, record);
10127         }
10128     },
10129     
10130     onAdd : function(ds, records, rowIndex)
10131     {
10132         //Roo.log('on Add called');
10133         // - note this does not handle multiple adding very well..
10134         var bt = this.bodyEl.dom;
10135         for (var i =0 ; i < records.length;i++) {
10136             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10137             //Roo.log(records[i]);
10138             //Roo.log(this.store.getAt(rowIndex+i));
10139             this.insertRow(this.store, rowIndex + i, false);
10140             return;
10141         }
10142         
10143     },
10144     
10145     
10146     refreshRow : function(record){
10147         var ds = this.store, index;
10148         if(typeof record == 'number'){
10149             index = record;
10150             record = ds.getAt(index);
10151         }else{
10152             index = ds.indexOf(record);
10153             if (index < 0) {
10154                 return; // should not happen - but seems to 
10155             }
10156         }
10157         this.insertRow(ds, index, true);
10158         this.autoSize();
10159         this.onRemove(ds, record, index+1, true);
10160         this.autoSize();
10161         //this.syncRowHeights(index, index);
10162         //this.layout();
10163         this.fireEvent("rowupdated", this, index, record);
10164     },
10165     // private - called by RowSelection
10166     onRowSelect : function(rowIndex){
10167         var row = this.getRowDom(rowIndex);
10168         row.addClass(['bg-info','info']);
10169     },
10170     // private - called by RowSelection
10171     onRowDeselect : function(rowIndex)
10172     {
10173         if (rowIndex < 0) {
10174             return;
10175         }
10176         var row = this.getRowDom(rowIndex);
10177         row.removeClass(['bg-info','info']);
10178     },
10179       /**
10180      * Focuses the specified row.
10181      * @param {Number} row The row index
10182      */
10183     focusRow : function(row)
10184     {
10185         //Roo.log('GridView.focusRow');
10186         var x = this.bodyEl.dom.scrollLeft;
10187         this.focusCell(row, 0, false);
10188         this.bodyEl.dom.scrollLeft = x;
10189
10190     },
10191      /**
10192      * Focuses the specified cell.
10193      * @param {Number} row The row index
10194      * @param {Number} col The column index
10195      * @param {Boolean} hscroll false to disable horizontal scrolling
10196      */
10197     focusCell : function(row, col, hscroll)
10198     {
10199         //Roo.log('GridView.focusCell');
10200         var el = this.ensureVisible(row, col, hscroll);
10201         // not sure what focusEL achives = it's a <a> pos relative 
10202         //this.focusEl.alignTo(el, "tl-tl");
10203         //if(Roo.isGecko){
10204         //    this.focusEl.focus();
10205         //}else{
10206         //    this.focusEl.focus.defer(1, this.focusEl);
10207         //}
10208     },
10209     
10210      /**
10211      * Scrolls the specified cell into view
10212      * @param {Number} row The row index
10213      * @param {Number} col The column index
10214      * @param {Boolean} hscroll false to disable horizontal scrolling
10215      */
10216     ensureVisible : function(row, col, hscroll)
10217     {
10218         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10219         //return null; //disable for testing.
10220         if(typeof row != "number"){
10221             row = row.rowIndex;
10222         }
10223         if(row < 0 && row >= this.ds.getCount()){
10224             return  null;
10225         }
10226         col = (col !== undefined ? col : 0);
10227         var cm = this.cm;
10228         while(cm.isHidden(col)){
10229             col++;
10230         }
10231
10232         var el = this.getCellDom(row, col);
10233         if(!el){
10234             return null;
10235         }
10236         var c = this.bodyEl.dom;
10237
10238         var ctop = parseInt(el.offsetTop, 10);
10239         var cleft = parseInt(el.offsetLeft, 10);
10240         var cbot = ctop + el.offsetHeight;
10241         var cright = cleft + el.offsetWidth;
10242
10243         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10244         var ch = 0; //?? header is not withing the area?
10245         var stop = parseInt(c.scrollTop, 10);
10246         var sleft = parseInt(c.scrollLeft, 10);
10247         var sbot = stop + ch;
10248         var sright = sleft + c.clientWidth;
10249         /*
10250         Roo.log('GridView.ensureVisible:' +
10251                 ' ctop:' + ctop +
10252                 ' c.clientHeight:' + c.clientHeight +
10253                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10254                 ' stop:' + stop +
10255                 ' cbot:' + cbot +
10256                 ' sbot:' + sbot +
10257                 ' ch:' + ch  
10258                 );
10259         */
10260         if(ctop < stop){
10261             c.scrollTop = ctop;
10262             //Roo.log("set scrolltop to ctop DISABLE?");
10263         }else if(cbot > sbot){
10264             //Roo.log("set scrolltop to cbot-ch");
10265             c.scrollTop = cbot-ch;
10266         }
10267
10268         if(hscroll !== false){
10269             if(cleft < sleft){
10270                 c.scrollLeft = cleft;
10271             }else if(cright > sright){
10272                 c.scrollLeft = cright-c.clientWidth;
10273             }
10274         }
10275
10276         return el;
10277     },
10278     
10279     
10280     insertRow : function(dm, rowIndex, isUpdate){
10281         
10282         if(!isUpdate){
10283             this.fireEvent("beforerowsinserted", this, rowIndex);
10284         }
10285             //var s = this.getScrollState();
10286         var row = this.renderRow(this.cm, this.store, rowIndex);
10287         // insert before rowIndex..
10288         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10289         
10290         var _this = this;
10291                 
10292         if(row.cellObjects.length){
10293             Roo.each(row.cellObjects, function(r){
10294                 _this.renderCellObject(r);
10295             })
10296         }
10297             
10298         if(!isUpdate){
10299             this.fireEvent("rowsinserted", this, rowIndex);
10300             //this.syncRowHeights(firstRow, lastRow);
10301             //this.stripeRows(firstRow);
10302             //this.layout();
10303         }
10304         
10305     },
10306     
10307     
10308     getRowDom : function(rowIndex)
10309     {
10310         var rows = this.el.select('tbody > tr', true).elements;
10311         
10312         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10313         
10314     },
10315     getCellDom : function(rowIndex, colIndex)
10316     {
10317         var row = this.getRowDom(rowIndex);
10318         if (row === false) {
10319             return false;
10320         }
10321         var cols = row.select('td', true).elements;
10322         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10323         
10324     },
10325     
10326     // returns the object tree for a tr..
10327   
10328     
10329     renderRow : function(cm, ds, rowIndex) 
10330     {
10331         var d = ds.getAt(rowIndex);
10332         
10333         var row = {
10334             tag : 'tr',
10335             cls : 'x-row-' + rowIndex,
10336             cn : []
10337         };
10338             
10339         var cellObjects = [];
10340         
10341         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10342             var config = cm.config[i];
10343             
10344             var renderer = cm.getRenderer(i);
10345             var value = '';
10346             var id = false;
10347             
10348             if(typeof(renderer) !== 'undefined'){
10349                 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10350             }
10351             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10352             // and are rendered into the cells after the row is rendered - using the id for the element.
10353             
10354             if(typeof(value) === 'object'){
10355                 id = Roo.id();
10356                 cellObjects.push({
10357                     container : id,
10358                     cfg : value 
10359                 })
10360             }
10361             
10362             var rowcfg = {
10363                 record: d,
10364                 rowIndex : rowIndex,
10365                 colIndex : i,
10366                 rowClass : ''
10367             };
10368
10369             this.fireEvent('rowclass', this, rowcfg);
10370             
10371             var td = {
10372                 tag: 'td',
10373                 // this might end up displaying HTML?
10374                 // this is too messy... - better to only do it on columsn you know are going to be too long
10375                 //tooltip : (typeof(value) === 'object') ? '' : value,
10376                 cls : rowcfg.rowClass + ' x-col-' + i,
10377                 style: '',
10378                 html: (typeof(value) === 'object') ? '' : value
10379             };
10380             
10381             if (id) {
10382                 td.id = id;
10383             }
10384             
10385             if(typeof(config.colspan) != 'undefined'){
10386                 td.colspan = config.colspan;
10387             }
10388             
10389             
10390             
10391             if(typeof(config.align) != 'undefined' && config.align.length){
10392                 td.style += ' text-align:' + config.align + ';';
10393             }
10394             if(typeof(config.valign) != 'undefined' && config.valign.length){
10395                 td.style += ' vertical-align:' + config.valign + ';';
10396             }
10397             /*
10398             if(typeof(config.width) != 'undefined'){
10399                 td.style += ' width:' +  config.width + 'px;';
10400             }
10401             */
10402             
10403             if(typeof(config.cursor) != 'undefined'){
10404                 td.style += ' cursor:' +  config.cursor + ';';
10405             }
10406             
10407             if(typeof(config.cls) != 'undefined'){
10408                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10409             }
10410             if (this.responsive) {
10411                 ['xs','sm','md','lg'].map(function(size){
10412                     
10413                     if(typeof(config[size]) == 'undefined'){
10414                         return;
10415                     }
10416                     
10417                     
10418                       
10419                     if (!config[size]) { // 0 = hidden
10420                         // BS 4 '0' is treated as hide that column and below.
10421                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10422                         return;
10423                     }
10424                     
10425                     td.cls += ' col-' + size + '-' + config[size] + (
10426                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10427                     );
10428                      
10429     
10430                 });
10431             }
10432             row.cn.push(td);
10433            
10434         }
10435         
10436         row.cellObjects = cellObjects;
10437         
10438         return row;
10439           
10440     },
10441     
10442     
10443     
10444     onBeforeLoad : function()
10445     {
10446         this.el.unmask(); // if needed.
10447     },
10448      /**
10449      * Remove all rows
10450      */
10451     clear : function()
10452     {
10453         this.el.select('tbody', true).first().dom.innerHTML = '';
10454     },
10455     /**
10456      * Show or hide a row.
10457      * @param {Number} rowIndex to show or hide
10458      * @param {Boolean} state hide
10459      */
10460     setRowVisibility : function(rowIndex, state)
10461     {
10462         var bt = this.bodyEl.dom;
10463         
10464         var rows = this.el.select('tbody > tr', true).elements;
10465         
10466         if(typeof(rows[rowIndex]) == 'undefined'){
10467             return;
10468         }
10469         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10470         
10471     },
10472     
10473     
10474     getSelectionModel : function(){
10475         if(!this.selModel){
10476             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10477         }
10478         return this.selModel;
10479     },
10480     /*
10481      * Render the Roo.bootstrap object from renderder
10482      */
10483     renderCellObject : function(r)
10484     {
10485         var _this = this;
10486         
10487         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10488         
10489         var t = r.cfg.render(r.container);
10490         
10491         if(r.cfg.cn){
10492             Roo.each(r.cfg.cn, function(c){
10493                 var child = {
10494                     container: t.getChildContainer(),
10495                     cfg: c
10496                 };
10497                 _this.renderCellObject(child);
10498             })
10499         }
10500     },
10501     /**
10502      * get the Row Index from a dom element.
10503      * @param {Roo.Element} row The row to look for
10504      * @returns {Number} the row
10505      */
10506     getRowIndex : function(row)
10507     {
10508         var rowIndex = -1;
10509         
10510         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10511             if(el != row){
10512                 return;
10513             }
10514             
10515             rowIndex = index;
10516         });
10517         
10518         return rowIndex;
10519     },
10520     /**
10521      * get the header TH element for columnIndex
10522      * @param {Number} columnIndex
10523      * @returns {Roo.Element}
10524      */
10525     getHeaderIndex: function(colIndex)
10526     {
10527         var cols = this.headEl.select('th', true).elements;
10528         return cols[colIndex]; 
10529     },
10530     /**
10531      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10532      * @param {domElement} cell to look for
10533      * @returns {Number} the column
10534      */
10535     getCellIndex : function(cell)
10536     {
10537         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10538         if(id){
10539             return parseInt(id[1], 10);
10540         }
10541         return 0;
10542     },
10543      /**
10544      * Returns the grid's underlying element = used by panel.Grid
10545      * @return {Element} The element
10546      */
10547     getGridEl : function(){
10548         return this.el;
10549     },
10550      /**
10551      * Forces a resize - used by panel.Grid
10552      * @return {Element} The element
10553      */
10554     autoSize : function()
10555     {
10556         if(this.disableAutoSize) {
10557             return;
10558         }
10559         //var ctr = Roo.get(this.container.dom.parentElement);
10560         var ctr = Roo.get(this.el.dom);
10561         
10562         var thd = this.getGridEl().select('thead',true).first();
10563         var tbd = this.getGridEl().select('tbody', true).first();
10564         var tfd = this.getGridEl().select('tfoot', true).first();
10565         
10566         var cw = ctr.getWidth();
10567         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10568         
10569         if (tbd) {
10570             
10571             tbd.setWidth(ctr.getWidth());
10572             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10573             // this needs fixing for various usage - currently only hydra job advers I think..
10574             //tdb.setHeight(
10575             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10576             //); 
10577             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10578             cw -= barsize;
10579         }
10580         cw = Math.max(cw, this.totalWidth);
10581         this.getGridEl().select('tbody tr',true).setWidth(cw);
10582         this.initCSS();
10583         
10584         // resize 'expandable coloumn?
10585         
10586         return; // we doe not have a view in this design..
10587         
10588     },
10589     onBodyScroll: function()
10590     {
10591         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10592         if(this.headEl){
10593             this.headEl.setStyle({
10594                 'position' : 'relative',
10595                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10596             });
10597         }
10598         
10599         if(this.lazyLoad){
10600             
10601             var scrollHeight = this.bodyEl.dom.scrollHeight;
10602             
10603             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10604             
10605             var height = this.bodyEl.getHeight();
10606             
10607             if(scrollHeight - height == scrollTop) {
10608                 
10609                 var total = this.ds.getTotalCount();
10610                 
10611                 if(this.footer.cursor + this.footer.pageSize < total){
10612                     
10613                     this.footer.ds.load({
10614                         params : {
10615                             start : this.footer.cursor + this.footer.pageSize,
10616                             limit : this.footer.pageSize
10617                         },
10618                         add : true
10619                     });
10620                 }
10621             }
10622             
10623         }
10624     },
10625     onColumnSplitterMoved : function(i, diff)
10626     {
10627         this.userResized = true;
10628         
10629         var cm = this.colModel;
10630         
10631         var w = this.getHeaderIndex(i).getWidth() + diff;
10632         
10633         
10634         cm.setColumnWidth(i, w, true);
10635         this.initCSS();
10636         //var cid = cm.getColumnId(i); << not used in this version?
10637        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10638         
10639         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10640         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10641         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10642 */
10643         //this.updateSplitters();
10644         //this.layout(); << ??
10645         this.fireEvent("columnresize", i, w);
10646     },
10647     onHeaderChange : function()
10648     {
10649         var header = this.renderHeader();
10650         var table = this.el.select('table', true).first();
10651         
10652         this.headEl.remove();
10653         this.headEl = table.createChild(header, this.bodyEl, false);
10654         
10655         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10656             e.on('click', this.sort, this);
10657         }, this);
10658         
10659         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10660             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10661         }
10662         
10663     },
10664     
10665     onHiddenChange : function(colModel, colIndex, hidden)
10666     {
10667         /*
10668         this.cm.setHidden()
10669         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10670         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10671         
10672         this.CSS.updateRule(thSelector, "display", "");
10673         this.CSS.updateRule(tdSelector, "display", "");
10674         
10675         if(hidden){
10676             this.CSS.updateRule(thSelector, "display", "none");
10677             this.CSS.updateRule(tdSelector, "display", "none");
10678         }
10679         */
10680         // onload calls initCSS()
10681         this.onHeaderChange();
10682         this.onLoad();
10683     },
10684     
10685     setColumnWidth: function(col_index, width)
10686     {
10687         // width = "md-2 xs-2..."
10688         if(!this.colModel.config[col_index]) {
10689             return;
10690         }
10691         
10692         var w = width.split(" ");
10693         
10694         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10695         
10696         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10697         
10698         
10699         for(var j = 0; j < w.length; j++) {
10700             
10701             if(!w[j]) {
10702                 continue;
10703             }
10704             
10705             var size_cls = w[j].split("-");
10706             
10707             if(!Number.isInteger(size_cls[1] * 1)) {
10708                 continue;
10709             }
10710             
10711             if(!this.colModel.config[col_index][size_cls[0]]) {
10712                 continue;
10713             }
10714             
10715             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10716                 continue;
10717             }
10718             
10719             h_row[0].classList.replace(
10720                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10721                 "col-"+size_cls[0]+"-"+size_cls[1]
10722             );
10723             
10724             for(var i = 0; i < rows.length; i++) {
10725                 
10726                 var size_cls = w[j].split("-");
10727                 
10728                 if(!Number.isInteger(size_cls[1] * 1)) {
10729                     continue;
10730                 }
10731                 
10732                 if(!this.colModel.config[col_index][size_cls[0]]) {
10733                     continue;
10734                 }
10735                 
10736                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10737                     continue;
10738                 }
10739                 
10740                 rows[i].classList.replace(
10741                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10742                     "col-"+size_cls[0]+"-"+size_cls[1]
10743                 );
10744             }
10745             
10746             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10747         }
10748     }
10749 });
10750
10751 // currently only used to find the split on drag.. 
10752 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10753
10754 /**
10755  * @depricated
10756 */
10757 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10758 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10759 /*
10760  * - LGPL
10761  *
10762  * table cell
10763  * 
10764  */
10765
10766 /**
10767  * @class Roo.bootstrap.TableCell
10768  * @extends Roo.bootstrap.Component
10769  * @children Roo.bootstrap.Component
10770  * @parent Roo.bootstrap.TableRow
10771  * Bootstrap TableCell class
10772  * 
10773  * @cfg {String} html cell contain text
10774  * @cfg {String} cls cell class
10775  * @cfg {String} tag cell tag (td|th) default td
10776  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10777  * @cfg {String} align Aligns the content in a cell
10778  * @cfg {String} axis Categorizes cells
10779  * @cfg {String} bgcolor Specifies the background color of a cell
10780  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10781  * @cfg {Number} colspan Specifies the number of columns a cell should span
10782  * @cfg {String} headers Specifies one or more header cells a cell is related to
10783  * @cfg {Number} height Sets the height of a cell
10784  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10785  * @cfg {Number} rowspan Sets the number of rows a cell should span
10786  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10787  * @cfg {String} valign Vertical aligns the content in a cell
10788  * @cfg {Number} width Specifies the width of a cell
10789  * 
10790  * @constructor
10791  * Create a new TableCell
10792  * @param {Object} config The config object
10793  */
10794
10795 Roo.bootstrap.TableCell = function(config){
10796     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10797 };
10798
10799 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10800     
10801     html: false,
10802     cls: false,
10803     tag: false,
10804     abbr: false,
10805     align: false,
10806     axis: false,
10807     bgcolor: false,
10808     charoff: false,
10809     colspan: false,
10810     headers: false,
10811     height: false,
10812     nowrap: false,
10813     rowspan: false,
10814     scope: false,
10815     valign: false,
10816     width: false,
10817     
10818     
10819     getAutoCreate : function(){
10820         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10821         
10822         cfg = {
10823             tag: 'td'
10824         };
10825         
10826         if(this.tag){
10827             cfg.tag = this.tag;
10828         }
10829         
10830         if (this.html) {
10831             cfg.html=this.html
10832         }
10833         if (this.cls) {
10834             cfg.cls=this.cls
10835         }
10836         if (this.abbr) {
10837             cfg.abbr=this.abbr
10838         }
10839         if (this.align) {
10840             cfg.align=this.align
10841         }
10842         if (this.axis) {
10843             cfg.axis=this.axis
10844         }
10845         if (this.bgcolor) {
10846             cfg.bgcolor=this.bgcolor
10847         }
10848         if (this.charoff) {
10849             cfg.charoff=this.charoff
10850         }
10851         if (this.colspan) {
10852             cfg.colspan=this.colspan
10853         }
10854         if (this.headers) {
10855             cfg.headers=this.headers
10856         }
10857         if (this.height) {
10858             cfg.height=this.height
10859         }
10860         if (this.nowrap) {
10861             cfg.nowrap=this.nowrap
10862         }
10863         if (this.rowspan) {
10864             cfg.rowspan=this.rowspan
10865         }
10866         if (this.scope) {
10867             cfg.scope=this.scope
10868         }
10869         if (this.valign) {
10870             cfg.valign=this.valign
10871         }
10872         if (this.width) {
10873             cfg.width=this.width
10874         }
10875         
10876         
10877         return cfg;
10878     }
10879    
10880 });
10881
10882  
10883
10884  /*
10885  * - LGPL
10886  *
10887  * table row
10888  * 
10889  */
10890
10891 /**
10892  * @class Roo.bootstrap.TableRow
10893  * @extends Roo.bootstrap.Component
10894  * @children Roo.bootstrap.TableCell
10895  * @parent Roo.bootstrap.TableBody
10896  * Bootstrap TableRow class
10897  * @cfg {String} cls row class
10898  * @cfg {String} align Aligns the content in a table row
10899  * @cfg {String} bgcolor Specifies a background color for a table row
10900  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10901  * @cfg {String} valign Vertical aligns the content in a table row
10902  * 
10903  * @constructor
10904  * Create a new TableRow
10905  * @param {Object} config The config object
10906  */
10907
10908 Roo.bootstrap.TableRow = function(config){
10909     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10910 };
10911
10912 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10913     
10914     cls: false,
10915     align: false,
10916     bgcolor: false,
10917     charoff: false,
10918     valign: false,
10919     
10920     getAutoCreate : function(){
10921         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10922         
10923         cfg = {
10924             tag: 'tr'
10925         };
10926             
10927         if(this.cls){
10928             cfg.cls = this.cls;
10929         }
10930         if(this.align){
10931             cfg.align = this.align;
10932         }
10933         if(this.bgcolor){
10934             cfg.bgcolor = this.bgcolor;
10935         }
10936         if(this.charoff){
10937             cfg.charoff = this.charoff;
10938         }
10939         if(this.valign){
10940             cfg.valign = this.valign;
10941         }
10942         
10943         return cfg;
10944     }
10945    
10946 });
10947
10948  
10949
10950  /*
10951  * - LGPL
10952  *
10953  * table body
10954  * 
10955  */
10956
10957 /**
10958  * @class Roo.bootstrap.TableBody
10959  * @extends Roo.bootstrap.Component
10960  * @children Roo.bootstrap.TableRow
10961  * @parent Roo.bootstrap.Table
10962  * Bootstrap TableBody class
10963  * @cfg {String} cls element class
10964  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10965  * @cfg {String} align Aligns the content inside the element
10966  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10967  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10968  * 
10969  * @constructor
10970  * Create a new TableBody
10971  * @param {Object} config The config object
10972  */
10973
10974 Roo.bootstrap.TableBody = function(config){
10975     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10976 };
10977
10978 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10979     
10980     cls: false,
10981     tag: false,
10982     align: false,
10983     charoff: false,
10984     valign: false,
10985     
10986     getAutoCreate : function(){
10987         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10988         
10989         cfg = {
10990             tag: 'tbody'
10991         };
10992             
10993         if (this.cls) {
10994             cfg.cls=this.cls
10995         }
10996         if(this.tag){
10997             cfg.tag = this.tag;
10998         }
10999         
11000         if(this.align){
11001             cfg.align = this.align;
11002         }
11003         if(this.charoff){
11004             cfg.charoff = this.charoff;
11005         }
11006         if(this.valign){
11007             cfg.valign = this.valign;
11008         }
11009         
11010         return cfg;
11011     }
11012     
11013     
11014 //    initEvents : function()
11015 //    {
11016 //        
11017 //        if(!this.store){
11018 //            return;
11019 //        }
11020 //        
11021 //        this.store = Roo.factory(this.store, Roo.data);
11022 //        this.store.on('load', this.onLoad, this);
11023 //        
11024 //        this.store.load();
11025 //        
11026 //    },
11027 //    
11028 //    onLoad: function () 
11029 //    {   
11030 //        this.fireEvent('load', this);
11031 //    }
11032 //    
11033 //   
11034 });
11035
11036  
11037
11038  /*
11039  * Based on:
11040  * Ext JS Library 1.1.1
11041  * Copyright(c) 2006-2007, Ext JS, LLC.
11042  *
11043  * Originally Released Under LGPL - original licence link has changed is not relivant.
11044  *
11045  * Fork - LGPL
11046  * <script type="text/javascript">
11047  */
11048
11049 // as we use this in bootstrap.
11050 Roo.namespace('Roo.form');
11051  /**
11052  * @class Roo.form.Action
11053  * Internal Class used to handle form actions
11054  * @constructor
11055  * @param {Roo.form.BasicForm} el The form element or its id
11056  * @param {Object} config Configuration options
11057  */
11058
11059  
11060  
11061 // define the action interface
11062 Roo.form.Action = function(form, options){
11063     this.form = form;
11064     this.options = options || {};
11065 };
11066 /**
11067  * Client Validation Failed
11068  * @const 
11069  */
11070 Roo.form.Action.CLIENT_INVALID = 'client';
11071 /**
11072  * Server Validation Failed
11073  * @const 
11074  */
11075 Roo.form.Action.SERVER_INVALID = 'server';
11076  /**
11077  * Connect to Server Failed
11078  * @const 
11079  */
11080 Roo.form.Action.CONNECT_FAILURE = 'connect';
11081 /**
11082  * Reading Data from Server Failed
11083  * @const 
11084  */
11085 Roo.form.Action.LOAD_FAILURE = 'load';
11086
11087 Roo.form.Action.prototype = {
11088     type : 'default',
11089     failureType : undefined,
11090     response : undefined,
11091     result : undefined,
11092
11093     // interface method
11094     run : function(options){
11095
11096     },
11097
11098     // interface method
11099     success : function(response){
11100
11101     },
11102
11103     // interface method
11104     handleResponse : function(response){
11105
11106     },
11107
11108     // default connection failure
11109     failure : function(response){
11110         
11111         this.response = response;
11112         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11113         this.form.afterAction(this, false);
11114     },
11115
11116     processResponse : function(response){
11117         this.response = response;
11118         if(!response.responseText){
11119             return true;
11120         }
11121         this.result = this.handleResponse(response);
11122         return this.result;
11123     },
11124
11125     // utility functions used internally
11126     getUrl : function(appendParams){
11127         var url = this.options.url || this.form.url || this.form.el.dom.action;
11128         if(appendParams){
11129             var p = this.getParams();
11130             if(p){
11131                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11132             }
11133         }
11134         return url;
11135     },
11136
11137     getMethod : function(){
11138         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11139     },
11140
11141     getParams : function(){
11142         var bp = this.form.baseParams;
11143         var p = this.options.params;
11144         if(p){
11145             if(typeof p == "object"){
11146                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11147             }else if(typeof p == 'string' && bp){
11148                 p += '&' + Roo.urlEncode(bp);
11149             }
11150         }else if(bp){
11151             p = Roo.urlEncode(bp);
11152         }
11153         return p;
11154     },
11155
11156     createCallback : function(){
11157         return {
11158             success: this.success,
11159             failure: this.failure,
11160             scope: this,
11161             timeout: (this.form.timeout*1000),
11162             upload: this.form.fileUpload ? this.success : undefined
11163         };
11164     }
11165 };
11166
11167 Roo.form.Action.Submit = function(form, options){
11168     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11169 };
11170
11171 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11172     type : 'submit',
11173
11174     haveProgress : false,
11175     uploadComplete : false,
11176     
11177     // uploadProgress indicator.
11178     uploadProgress : function()
11179     {
11180         if (!this.form.progressUrl) {
11181             return;
11182         }
11183         
11184         if (!this.haveProgress) {
11185             Roo.MessageBox.progress("Uploading", "Uploading");
11186         }
11187         if (this.uploadComplete) {
11188            Roo.MessageBox.hide();
11189            return;
11190         }
11191         
11192         this.haveProgress = true;
11193    
11194         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11195         
11196         var c = new Roo.data.Connection();
11197         c.request({
11198             url : this.form.progressUrl,
11199             params: {
11200                 id : uid
11201             },
11202             method: 'GET',
11203             success : function(req){
11204                //console.log(data);
11205                 var rdata = false;
11206                 var edata;
11207                 try  {
11208                    rdata = Roo.decode(req.responseText)
11209                 } catch (e) {
11210                     Roo.log("Invalid data from server..");
11211                     Roo.log(edata);
11212                     return;
11213                 }
11214                 if (!rdata || !rdata.success) {
11215                     Roo.log(rdata);
11216                     Roo.MessageBox.alert(Roo.encode(rdata));
11217                     return;
11218                 }
11219                 var data = rdata.data;
11220                 
11221                 if (this.uploadComplete) {
11222                    Roo.MessageBox.hide();
11223                    return;
11224                 }
11225                    
11226                 if (data){
11227                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11228                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11229                     );
11230                 }
11231                 this.uploadProgress.defer(2000,this);
11232             },
11233        
11234             failure: function(data) {
11235                 Roo.log('progress url failed ');
11236                 Roo.log(data);
11237             },
11238             scope : this
11239         });
11240            
11241     },
11242     
11243     
11244     run : function()
11245     {
11246         // run get Values on the form, so it syncs any secondary forms.
11247         this.form.getValues();
11248         
11249         var o = this.options;
11250         var method = this.getMethod();
11251         var isPost = method == 'POST';
11252         if(o.clientValidation === false || this.form.isValid()){
11253             
11254             if (this.form.progressUrl) {
11255                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11256                     (new Date() * 1) + '' + Math.random());
11257                     
11258             } 
11259             
11260             
11261             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11262                 form:this.form.el.dom,
11263                 url:this.getUrl(!isPost),
11264                 method: method,
11265                 params:isPost ? this.getParams() : null,
11266                 isUpload: this.form.fileUpload,
11267                 formData : this.form.formData
11268             }));
11269             
11270             this.uploadProgress();
11271
11272         }else if (o.clientValidation !== false){ // client validation failed
11273             this.failureType = Roo.form.Action.CLIENT_INVALID;
11274             this.form.afterAction(this, false);
11275         }
11276     },
11277
11278     success : function(response)
11279     {
11280         this.uploadComplete= true;
11281         if (this.haveProgress) {
11282             Roo.MessageBox.hide();
11283         }
11284         
11285         
11286         var result = this.processResponse(response);
11287         if(result === true || result.success){
11288             this.form.afterAction(this, true);
11289             return;
11290         }
11291         if(result.errors){
11292             this.form.markInvalid(result.errors);
11293             this.failureType = Roo.form.Action.SERVER_INVALID;
11294         }
11295         this.form.afterAction(this, false);
11296     },
11297     failure : function(response)
11298     {
11299         this.uploadComplete= true;
11300         if (this.haveProgress) {
11301             Roo.MessageBox.hide();
11302         }
11303         
11304         this.response = response;
11305         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11306         this.form.afterAction(this, false);
11307     },
11308     
11309     handleResponse : function(response){
11310         if(this.form.errorReader){
11311             var rs = this.form.errorReader.read(response);
11312             var errors = [];
11313             if(rs.records){
11314                 for(var i = 0, len = rs.records.length; i < len; i++) {
11315                     var r = rs.records[i];
11316                     errors[i] = r.data;
11317                 }
11318             }
11319             if(errors.length < 1){
11320                 errors = null;
11321             }
11322             return {
11323                 success : rs.success,
11324                 errors : errors
11325             };
11326         }
11327         var ret = false;
11328         try {
11329             var rt = response.responseText;
11330             if (rt.match(/^\<!--\[CDATA\[/)) {
11331                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11332                 rt = rt.replace(/\]\]--\>$/,'');
11333             }
11334             
11335             ret = Roo.decode(rt);
11336         } catch (e) {
11337             ret = {
11338                 success: false,
11339                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11340                 errors : []
11341             };
11342         }
11343         return ret;
11344         
11345     }
11346 });
11347
11348
11349 Roo.form.Action.Load = function(form, options){
11350     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11351     this.reader = this.form.reader;
11352 };
11353
11354 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11355     type : 'load',
11356
11357     run : function(){
11358         
11359         Roo.Ajax.request(Roo.apply(
11360                 this.createCallback(), {
11361                     method:this.getMethod(),
11362                     url:this.getUrl(false),
11363                     params:this.getParams()
11364         }));
11365     },
11366
11367     success : function(response){
11368         
11369         var result = this.processResponse(response);
11370         if(result === true || !result.success || !result.data){
11371             this.failureType = Roo.form.Action.LOAD_FAILURE;
11372             this.form.afterAction(this, false);
11373             return;
11374         }
11375         this.form.clearInvalid();
11376         this.form.setValues(result.data);
11377         this.form.afterAction(this, true);
11378     },
11379
11380     handleResponse : function(response){
11381         if(this.form.reader){
11382             var rs = this.form.reader.read(response);
11383             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11384             return {
11385                 success : rs.success,
11386                 data : data
11387             };
11388         }
11389         return Roo.decode(response.responseText);
11390     }
11391 });
11392
11393 Roo.form.Action.ACTION_TYPES = {
11394     'load' : Roo.form.Action.Load,
11395     'submit' : Roo.form.Action.Submit
11396 };/*
11397  * - LGPL
11398  *
11399  * form
11400  *
11401  */
11402
11403 /**
11404  * @class Roo.bootstrap.form.Form
11405  * @extends Roo.bootstrap.Component
11406  * @children Roo.bootstrap.Component
11407  * Bootstrap Form class
11408  * @cfg {String} method  GET | POST (default POST)
11409  * @cfg {String} labelAlign top | left (default top)
11410  * @cfg {String} align left  | right - for navbars
11411  * @cfg {Boolean} loadMask load mask when submit (default true)
11412
11413  *
11414  * @constructor
11415  * Create a new Form
11416  * @param {Object} config The config object
11417  */
11418
11419
11420 Roo.bootstrap.form.Form = function(config){
11421     
11422     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11423     
11424     Roo.bootstrap.form.Form.popover.apply();
11425     
11426     this.addEvents({
11427         /**
11428          * @event clientvalidation
11429          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11430          * @param {Form} this
11431          * @param {Boolean} valid true if the form has passed client-side validation
11432          */
11433         clientvalidation: true,
11434         /**
11435          * @event beforeaction
11436          * Fires before any action is performed. Return false to cancel the action.
11437          * @param {Form} this
11438          * @param {Action} action The action to be performed
11439          */
11440         beforeaction: true,
11441         /**
11442          * @event actionfailed
11443          * Fires when an action fails.
11444          * @param {Form} this
11445          * @param {Action} action The action that failed
11446          */
11447         actionfailed : true,
11448         /**
11449          * @event actioncomplete
11450          * Fires when an action is completed.
11451          * @param {Form} this
11452          * @param {Action} action The action that completed
11453          */
11454         actioncomplete : true
11455     });
11456 };
11457
11458 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11459
11460      /**
11461      * @cfg {String} method
11462      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11463      */
11464     method : 'POST',
11465     /**
11466      * @cfg {String} url
11467      * The URL to use for form actions if one isn't supplied in the action options.
11468      */
11469     /**
11470      * @cfg {Boolean} fileUpload
11471      * Set to true if this form is a file upload.
11472      */
11473
11474     /**
11475      * @cfg {Object} baseParams
11476      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11477      */
11478
11479     /**
11480      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11481      */
11482     timeout: 30,
11483     /**
11484      * @cfg {Sting} align (left|right) for navbar forms
11485      */
11486     align : 'left',
11487
11488     // private
11489     activeAction : null,
11490
11491     /**
11492      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11493      * element by passing it or its id or mask the form itself by passing in true.
11494      * @type Mixed
11495      */
11496     waitMsgTarget : false,
11497
11498     loadMask : true,
11499     
11500     /**
11501      * @cfg {Boolean} errorMask (true|false) default false
11502      */
11503     errorMask : false,
11504     
11505     /**
11506      * @cfg {Number} maskOffset Default 100
11507      */
11508     maskOffset : 100,
11509     
11510     /**
11511      * @cfg {Boolean} maskBody
11512      */
11513     maskBody : false,
11514
11515     getAutoCreate : function(){
11516
11517         var cfg = {
11518             tag: 'form',
11519             method : this.method || 'POST',
11520             id : this.id || Roo.id(),
11521             cls : ''
11522         };
11523         if (this.parent().xtype.match(/^Nav/)) {
11524             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11525
11526         }
11527
11528         if (this.labelAlign == 'left' ) {
11529             cfg.cls += ' form-horizontal';
11530         }
11531
11532
11533         return cfg;
11534     },
11535     initEvents : function()
11536     {
11537         this.el.on('submit', this.onSubmit, this);
11538         // this was added as random key presses on the form where triggering form submit.
11539         this.el.on('keypress', function(e) {
11540             if (e.getCharCode() != 13) {
11541                 return true;
11542             }
11543             // we might need to allow it for textareas.. and some other items.
11544             // check e.getTarget().
11545
11546             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11547                 return true;
11548             }
11549
11550             Roo.log("keypress blocked");
11551
11552             e.preventDefault();
11553             return false;
11554         });
11555         
11556     },
11557     // private
11558     onSubmit : function(e){
11559         e.stopEvent();
11560     },
11561
11562      /**
11563      * Returns true if client-side validation on the form is successful.
11564      * @return Boolean
11565      */
11566     isValid : function(){
11567         var items = this.getItems();
11568         var valid = true;
11569         var target = false;
11570         
11571         items.each(function(f){
11572             
11573             if(f.validate()){
11574                 return;
11575             }
11576             
11577             Roo.log('invalid field: ' + f.name);
11578             
11579             valid = false;
11580
11581             if(!target && f.el.isVisible(true)){
11582                 target = f;
11583             }
11584            
11585         });
11586         
11587         if(this.errorMask && !valid){
11588             Roo.bootstrap.form.Form.popover.mask(this, target);
11589         }
11590         
11591         return valid;
11592     },
11593     
11594     /**
11595      * Returns true if any fields in this form have changed since their original load.
11596      * @return Boolean
11597      */
11598     isDirty : function(){
11599         var dirty = false;
11600         var items = this.getItems();
11601         items.each(function(f){
11602            if(f.isDirty()){
11603                dirty = true;
11604                return false;
11605            }
11606            return true;
11607         });
11608         return dirty;
11609     },
11610      /**
11611      * Performs a predefined action (submit or load) or custom actions you define on this form.
11612      * @param {String} actionName The name of the action type
11613      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11614      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11615      * accept other config options):
11616      * <pre>
11617 Property          Type             Description
11618 ----------------  ---------------  ----------------------------------------------------------------------------------
11619 url               String           The url for the action (defaults to the form's url)
11620 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11621 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11622 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11623                                    validate the form on the client (defaults to false)
11624      * </pre>
11625      * @return {BasicForm} this
11626      */
11627     doAction : function(action, options){
11628         if(typeof action == 'string'){
11629             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11630         }
11631         if(this.fireEvent('beforeaction', this, action) !== false){
11632             this.beforeAction(action);
11633             action.run.defer(100, action);
11634         }
11635         return this;
11636     },
11637
11638     // private
11639     beforeAction : function(action){
11640         var o = action.options;
11641         
11642         if(this.loadMask){
11643             
11644             if(this.maskBody){
11645                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11646             } else {
11647                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11648             }
11649         }
11650         // not really supported yet.. ??
11651
11652         //if(this.waitMsgTarget === true){
11653         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11654         //}else if(this.waitMsgTarget){
11655         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11656         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11657         //}else {
11658         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11659        // }
11660
11661     },
11662
11663     // private
11664     afterAction : function(action, success){
11665         this.activeAction = null;
11666         var o = action.options;
11667
11668         if(this.loadMask){
11669             
11670             if(this.maskBody){
11671                 Roo.get(document.body).unmask();
11672             } else {
11673                 this.el.unmask();
11674             }
11675         }
11676         
11677         //if(this.waitMsgTarget === true){
11678 //            this.el.unmask();
11679         //}else if(this.waitMsgTarget){
11680         //    this.waitMsgTarget.unmask();
11681         //}else{
11682         //    Roo.MessageBox.updateProgress(1);
11683         //    Roo.MessageBox.hide();
11684        // }
11685         //
11686         if(success){
11687             if(o.reset){
11688                 this.reset();
11689             }
11690             Roo.callback(o.success, o.scope, [this, action]);
11691             this.fireEvent('actioncomplete', this, action);
11692
11693         }else{
11694
11695             // failure condition..
11696             // we have a scenario where updates need confirming.
11697             // eg. if a locking scenario exists..
11698             // we look for { errors : { needs_confirm : true }} in the response.
11699             if (
11700                 (typeof(action.result) != 'undefined')  &&
11701                 (typeof(action.result.errors) != 'undefined')  &&
11702                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11703            ){
11704                 var _t = this;
11705                 Roo.log("not supported yet");
11706                  /*
11707
11708                 Roo.MessageBox.confirm(
11709                     "Change requires confirmation",
11710                     action.result.errorMsg,
11711                     function(r) {
11712                         if (r != 'yes') {
11713                             return;
11714                         }
11715                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11716                     }
11717
11718                 );
11719                 */
11720
11721
11722                 return;
11723             }
11724
11725             Roo.callback(o.failure, o.scope, [this, action]);
11726             // show an error message if no failed handler is set..
11727             if (!this.hasListener('actionfailed')) {
11728                 Roo.log("need to add dialog support");
11729                 /*
11730                 Roo.MessageBox.alert("Error",
11731                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11732                         action.result.errorMsg :
11733                         "Saving Failed, please check your entries or try again"
11734                 );
11735                 */
11736             }
11737
11738             this.fireEvent('actionfailed', this, action);
11739         }
11740
11741     },
11742     /**
11743      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11744      * @param {String} id The value to search for
11745      * @return Field
11746      */
11747     findField : function(id){
11748         var items = this.getItems();
11749         var field = items.get(id);
11750         if(!field){
11751              items.each(function(f){
11752                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11753                     field = f;
11754                     return false;
11755                 }
11756                 return true;
11757             });
11758         }
11759         return field || null;
11760     },
11761      /**
11762      * Mark fields in this form invalid in bulk.
11763      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11764      * @return {BasicForm} this
11765      */
11766     markInvalid : function(errors){
11767         if(errors instanceof Array){
11768             for(var i = 0, len = errors.length; i < len; i++){
11769                 var fieldError = errors[i];
11770                 var f = this.findField(fieldError.id);
11771                 if(f){
11772                     f.markInvalid(fieldError.msg);
11773                 }
11774             }
11775         }else{
11776             var field, id;
11777             for(id in errors){
11778                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11779                     field.markInvalid(errors[id]);
11780                 }
11781             }
11782         }
11783         //Roo.each(this.childForms || [], function (f) {
11784         //    f.markInvalid(errors);
11785         //});
11786
11787         return this;
11788     },
11789
11790     /**
11791      * Set values for fields in this form in bulk.
11792      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11793      * @return {BasicForm} this
11794      */
11795     setValues : function(values){
11796         if(values instanceof Array){ // array of objects
11797             for(var i = 0, len = values.length; i < len; i++){
11798                 var v = values[i];
11799                 var f = this.findField(v.id);
11800                 if(f){
11801                     f.setValue(v.value);
11802                     if(this.trackResetOnLoad){
11803                         f.originalValue = f.getValue();
11804                     }
11805                 }
11806             }
11807         }else{ // object hash
11808             var field, id;
11809             for(id in values){
11810                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11811
11812                     if (field.setFromData &&
11813                         field.valueField &&
11814                         field.displayField &&
11815                         // combos' with local stores can
11816                         // be queried via setValue()
11817                         // to set their value..
11818                         (field.store && !field.store.isLocal)
11819                         ) {
11820                         // it's a combo
11821                         var sd = { };
11822                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11823                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11824                         field.setFromData(sd);
11825
11826                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11827                         
11828                         field.setFromData(values);
11829                         
11830                     } else {
11831                         field.setValue(values[id]);
11832                     }
11833
11834
11835                     if(this.trackResetOnLoad){
11836                         field.originalValue = field.getValue();
11837                     }
11838                 }
11839             }
11840         }
11841
11842         //Roo.each(this.childForms || [], function (f) {
11843         //    f.setValues(values);
11844         //});
11845
11846         return this;
11847     },
11848
11849     /**
11850      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11851      * they are returned as an array.
11852      * @param {Boolean} asString
11853      * @return {Object}
11854      */
11855     getValues : function(asString){
11856         //if (this.childForms) {
11857             // copy values from the child forms
11858         //    Roo.each(this.childForms, function (f) {
11859         //        this.setValues(f.getValues());
11860         //    }, this);
11861         //}
11862
11863
11864
11865         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11866         if(asString === true){
11867             return fs;
11868         }
11869         return Roo.urlDecode(fs);
11870     },
11871
11872     /**
11873      * Returns the fields in this form as an object with key/value pairs.
11874      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11875      * @return {Object}
11876      */
11877     getFieldValues : function(with_hidden)
11878     {
11879         var items = this.getItems();
11880         var ret = {};
11881         items.each(function(f){
11882             
11883             if (!f.getName()) {
11884                 return;
11885             }
11886             
11887             var v = f.getValue();
11888             
11889             if (f.inputType =='radio') {
11890                 if (typeof(ret[f.getName()]) == 'undefined') {
11891                     ret[f.getName()] = ''; // empty..
11892                 }
11893
11894                 if (!f.el.dom.checked) {
11895                     return;
11896
11897                 }
11898                 v = f.el.dom.value;
11899
11900             }
11901             
11902             if(f.xtype == 'MoneyField'){
11903                 ret[f.currencyName] = f.getCurrency();
11904             }
11905
11906             // not sure if this supported any more..
11907             if ((typeof(v) == 'object') && f.getRawValue) {
11908                 v = f.getRawValue() ; // dates..
11909             }
11910             // combo boxes where name != hiddenName...
11911             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11912                 ret[f.name] = f.getRawValue();
11913             }
11914             ret[f.getName()] = v;
11915         });
11916
11917         return ret;
11918     },
11919
11920     /**
11921      * Clears all invalid messages in this form.
11922      * @return {BasicForm} this
11923      */
11924     clearInvalid : function(){
11925         var items = this.getItems();
11926
11927         items.each(function(f){
11928            f.clearInvalid();
11929         });
11930
11931         return this;
11932     },
11933
11934     /**
11935      * Resets this form.
11936      * @return {BasicForm} this
11937      */
11938     reset : function(){
11939         var items = this.getItems();
11940         items.each(function(f){
11941             f.reset();
11942         });
11943
11944         Roo.each(this.childForms || [], function (f) {
11945             f.reset();
11946         });
11947
11948
11949         return this;
11950     },
11951     
11952     getItems : function()
11953     {
11954         var r=new Roo.util.MixedCollection(false, function(o){
11955             return o.id || (o.id = Roo.id());
11956         });
11957         var iter = function(el) {
11958             if (el.inputEl) {
11959                 r.add(el);
11960             }
11961             if (!el.items) {
11962                 return;
11963             }
11964             Roo.each(el.items,function(e) {
11965                 iter(e);
11966             });
11967         };
11968
11969         iter(this);
11970         return r;
11971     },
11972     
11973     hideFields : function(items)
11974     {
11975         Roo.each(items, function(i){
11976             
11977             var f = this.findField(i);
11978             
11979             if(!f){
11980                 return;
11981             }
11982             
11983             f.hide();
11984             
11985         }, this);
11986     },
11987     
11988     showFields : function(items)
11989     {
11990         Roo.each(items, function(i){
11991             
11992             var f = this.findField(i);
11993             
11994             if(!f){
11995                 return;
11996             }
11997             
11998             f.show();
11999             
12000         }, this);
12001     }
12002
12003 });
12004
12005 Roo.apply(Roo.bootstrap.form.Form, {
12006     
12007     popover : {
12008         
12009         padding : 5,
12010         
12011         isApplied : false,
12012         
12013         isMasked : false,
12014         
12015         form : false,
12016         
12017         target : false,
12018         
12019         toolTip : false,
12020         
12021         intervalID : false,
12022         
12023         maskEl : false,
12024         
12025         apply : function()
12026         {
12027             if(this.isApplied){
12028                 return;
12029             }
12030             
12031             this.maskEl = {
12032                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12033                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12034                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12035                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12036             };
12037             
12038             this.maskEl.top.enableDisplayMode("block");
12039             this.maskEl.left.enableDisplayMode("block");
12040             this.maskEl.bottom.enableDisplayMode("block");
12041             this.maskEl.right.enableDisplayMode("block");
12042             
12043             this.toolTip = new Roo.bootstrap.Tooltip({
12044                 cls : 'roo-form-error-popover',
12045                 alignment : {
12046                     'left' : ['r-l', [-2,0], 'right'],
12047                     'right' : ['l-r', [2,0], 'left'],
12048                     'bottom' : ['tl-bl', [0,2], 'top'],
12049                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12050                 }
12051             });
12052             
12053             this.toolTip.render(Roo.get(document.body));
12054
12055             this.toolTip.el.enableDisplayMode("block");
12056             
12057             Roo.get(document.body).on('click', function(){
12058                 this.unmask();
12059             }, this);
12060             
12061             Roo.get(document.body).on('touchstart', function(){
12062                 this.unmask();
12063             }, this);
12064             
12065             this.isApplied = true
12066         },
12067         
12068         mask : function(form, target)
12069         {
12070             this.form = form;
12071             
12072             this.target = target;
12073             
12074             if(!this.form.errorMask || !target.el){
12075                 return;
12076             }
12077             
12078             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12079             
12080             Roo.log(scrollable);
12081             
12082             var ot = this.target.el.calcOffsetsTo(scrollable);
12083             
12084             var scrollTo = ot[1] - this.form.maskOffset;
12085             
12086             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12087             
12088             scrollable.scrollTo('top', scrollTo);
12089             
12090             var box = this.target.el.getBox();
12091             Roo.log(box);
12092             var zIndex = Roo.bootstrap.Modal.zIndex++;
12093
12094             
12095             this.maskEl.top.setStyle('position', 'absolute');
12096             this.maskEl.top.setStyle('z-index', zIndex);
12097             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12098             this.maskEl.top.setLeft(0);
12099             this.maskEl.top.setTop(0);
12100             this.maskEl.top.show();
12101             
12102             this.maskEl.left.setStyle('position', 'absolute');
12103             this.maskEl.left.setStyle('z-index', zIndex);
12104             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12105             this.maskEl.left.setLeft(0);
12106             this.maskEl.left.setTop(box.y - this.padding);
12107             this.maskEl.left.show();
12108
12109             this.maskEl.bottom.setStyle('position', 'absolute');
12110             this.maskEl.bottom.setStyle('z-index', zIndex);
12111             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12112             this.maskEl.bottom.setLeft(0);
12113             this.maskEl.bottom.setTop(box.bottom + this.padding);
12114             this.maskEl.bottom.show();
12115
12116             this.maskEl.right.setStyle('position', 'absolute');
12117             this.maskEl.right.setStyle('z-index', zIndex);
12118             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12119             this.maskEl.right.setLeft(box.right + this.padding);
12120             this.maskEl.right.setTop(box.y - this.padding);
12121             this.maskEl.right.show();
12122
12123             this.toolTip.bindEl = this.target.el;
12124
12125             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12126
12127             var tip = this.target.blankText;
12128
12129             if(this.target.getValue() !== '' ) {
12130                 
12131                 if (this.target.invalidText.length) {
12132                     tip = this.target.invalidText;
12133                 } else if (this.target.regexText.length){
12134                     tip = this.target.regexText;
12135                 }
12136             }
12137
12138             this.toolTip.show(tip);
12139
12140             this.intervalID = window.setInterval(function() {
12141                 Roo.bootstrap.form.Form.popover.unmask();
12142             }, 10000);
12143
12144             window.onwheel = function(){ return false;};
12145             
12146             (function(){ this.isMasked = true; }).defer(500, this);
12147             
12148         },
12149         
12150         unmask : function()
12151         {
12152             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12153                 return;
12154             }
12155             
12156             this.maskEl.top.setStyle('position', 'absolute');
12157             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12158             this.maskEl.top.hide();
12159
12160             this.maskEl.left.setStyle('position', 'absolute');
12161             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12162             this.maskEl.left.hide();
12163
12164             this.maskEl.bottom.setStyle('position', 'absolute');
12165             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12166             this.maskEl.bottom.hide();
12167
12168             this.maskEl.right.setStyle('position', 'absolute');
12169             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12170             this.maskEl.right.hide();
12171             
12172             this.toolTip.hide();
12173             
12174             this.toolTip.el.hide();
12175             
12176             window.onwheel = function(){ return true;};
12177             
12178             if(this.intervalID){
12179                 window.clearInterval(this.intervalID);
12180                 this.intervalID = false;
12181             }
12182             
12183             this.isMasked = false;
12184             
12185         }
12186         
12187     }
12188     
12189 });
12190
12191 /*
12192  * Based on:
12193  * Ext JS Library 1.1.1
12194  * Copyright(c) 2006-2007, Ext JS, LLC.
12195  *
12196  * Originally Released Under LGPL - original licence link has changed is not relivant.
12197  *
12198  * Fork - LGPL
12199  * <script type="text/javascript">
12200  */
12201 /**
12202  * @class Roo.form.VTypes
12203  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12204  * @static
12205  */
12206 Roo.form.VTypes = function(){
12207     // closure these in so they are only created once.
12208     var alpha = /^[a-zA-Z_]+$/;
12209     var alphanum = /^[a-zA-Z0-9_]+$/;
12210     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12211     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12212
12213     // All these messages and functions are configurable
12214     return {
12215         /**
12216          * The function used to validate email addresses
12217          * @param {String} value The email address
12218          */
12219         email : function(v){
12220             return email.test(v);
12221         },
12222         /**
12223          * The error text to display when the email validation function returns false
12224          * @type String
12225          */
12226         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12227         /**
12228          * The keystroke filter mask to be applied on email input
12229          * @type RegExp
12230          */
12231         emailMask : /[a-z0-9_\.\-@]/i,
12232
12233         /**
12234          * The function used to validate URLs
12235          * @param {String} value The URL
12236          */
12237         url : function(v){
12238             return url.test(v);
12239         },
12240         /**
12241          * The error text to display when the url validation function returns false
12242          * @type String
12243          */
12244         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12245         
12246         /**
12247          * The function used to validate alpha values
12248          * @param {String} value The value
12249          */
12250         alpha : function(v){
12251             return alpha.test(v);
12252         },
12253         /**
12254          * The error text to display when the alpha validation function returns false
12255          * @type String
12256          */
12257         alphaText : 'This field should only contain letters and _',
12258         /**
12259          * The keystroke filter mask to be applied on alpha input
12260          * @type RegExp
12261          */
12262         alphaMask : /[a-z_]/i,
12263
12264         /**
12265          * The function used to validate alphanumeric values
12266          * @param {String} value The value
12267          */
12268         alphanum : function(v){
12269             return alphanum.test(v);
12270         },
12271         /**
12272          * The error text to display when the alphanumeric validation function returns false
12273          * @type String
12274          */
12275         alphanumText : 'This field should only contain letters, numbers and _',
12276         /**
12277          * The keystroke filter mask to be applied on alphanumeric input
12278          * @type RegExp
12279          */
12280         alphanumMask : /[a-z0-9_]/i
12281     };
12282 }();/*
12283  * - LGPL
12284  *
12285  * Input
12286  * 
12287  */
12288
12289 /**
12290  * @class Roo.bootstrap.form.Input
12291  * @extends Roo.bootstrap.Component
12292  * Bootstrap Input class
12293  * @cfg {Boolean} disabled is it disabled
12294  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12295  * @cfg {String} name name of the input
12296  * @cfg {string} fieldLabel - the label associated
12297  * @cfg {string} placeholder - placeholder to put in text.
12298  * @cfg {string} before - input group add on before
12299  * @cfg {string} after - input group add on after
12300  * @cfg {string} size - (lg|sm) or leave empty..
12301  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12302  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12303  * @cfg {Number} md colspan out of 12 for computer-sized screens
12304  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12305  * @cfg {string} value default value of the input
12306  * @cfg {Number} labelWidth set the width of label 
12307  * @cfg {Number} labellg set the width of label (1-12)
12308  * @cfg {Number} labelmd set the width of label (1-12)
12309  * @cfg {Number} labelsm set the width of label (1-12)
12310  * @cfg {Number} labelxs set the width of label (1-12)
12311  * @cfg {String} labelAlign (top|left)
12312  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12313  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12314  * @cfg {String} indicatorpos (left|right) default left
12315  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12316  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12317  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12318  * @cfg {Roo.bootstrap.Button} before Button to show before
12319  * @cfg {Roo.bootstrap.Button} afterButton to show before
12320  * @cfg {String} align (left|center|right) Default left
12321  * @cfg {Boolean} forceFeedback (true|false) Default false
12322  * 
12323  * @constructor
12324  * Create a new Input
12325  * @param {Object} config The config object
12326  */
12327
12328 Roo.bootstrap.form.Input = function(config){
12329     
12330     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12331     
12332     this.addEvents({
12333         /**
12334          * @event focus
12335          * Fires when this field receives input focus.
12336          * @param {Roo.form.Field} this
12337          */
12338         focus : true,
12339         /**
12340          * @event blur
12341          * Fires when this field loses input focus.
12342          * @param {Roo.form.Field} this
12343          */
12344         blur : true,
12345         /**
12346          * @event specialkey
12347          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12348          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12349          * @param {Roo.form.Field} this
12350          * @param {Roo.EventObject} e The event object
12351          */
12352         specialkey : true,
12353         /**
12354          * @event change
12355          * Fires just before the field blurs if the field value has changed.
12356          * @param {Roo.form.Field} this
12357          * @param {Mixed} newValue The new value
12358          * @param {Mixed} oldValue The original value
12359          */
12360         change : true,
12361         /**
12362          * @event invalid
12363          * Fires after the field has been marked as invalid.
12364          * @param {Roo.form.Field} this
12365          * @param {String} msg The validation message
12366          */
12367         invalid : true,
12368         /**
12369          * @event valid
12370          * Fires after the field has been validated with no errors.
12371          * @param {Roo.form.Field} this
12372          */
12373         valid : true,
12374          /**
12375          * @event keyup
12376          * Fires after the key up
12377          * @param {Roo.form.Field} this
12378          * @param {Roo.EventObject}  e The event Object
12379          */
12380         keyup : true,
12381         /**
12382          * @event paste
12383          * Fires after the user pastes into input
12384          * @param {Roo.form.Field} this
12385          * @param {Roo.EventObject}  e The event Object
12386          */
12387         paste : true
12388     });
12389 };
12390
12391 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12392      /**
12393      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12394       automatic validation (defaults to "keyup").
12395      */
12396     validationEvent : "keyup",
12397      /**
12398      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12399      */
12400     validateOnBlur : true,
12401     /**
12402      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12403      */
12404     validationDelay : 250,
12405      /**
12406      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12407      */
12408     focusClass : "x-form-focus",  // not needed???
12409     
12410        
12411     /**
12412      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12413      */
12414     invalidClass : "has-warning",
12415     
12416     /**
12417      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12418      */
12419     validClass : "has-success",
12420     
12421     /**
12422      * @cfg {Boolean} hasFeedback (true|false) default true
12423      */
12424     hasFeedback : true,
12425     
12426     /**
12427      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12428      */
12429     invalidFeedbackClass : "glyphicon-warning-sign",
12430     
12431     /**
12432      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12433      */
12434     validFeedbackClass : "glyphicon-ok",
12435     
12436     /**
12437      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12438      */
12439     selectOnFocus : false,
12440     
12441      /**
12442      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12443      */
12444     maskRe : null,
12445        /**
12446      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12447      */
12448     vtype : null,
12449     
12450       /**
12451      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12452      */
12453     disableKeyFilter : false,
12454     
12455        /**
12456      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12457      */
12458     disabled : false,
12459      /**
12460      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12461      */
12462     allowBlank : true,
12463     /**
12464      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12465      */
12466     blankText : "Please complete this mandatory field",
12467     
12468      /**
12469      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12470      */
12471     minLength : 0,
12472     /**
12473      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12474      */
12475     maxLength : Number.MAX_VALUE,
12476     /**
12477      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12478      */
12479     minLengthText : "The minimum length for this field is {0}",
12480     /**
12481      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12482      */
12483     maxLengthText : "The maximum length for this field is {0}",
12484   
12485     
12486     /**
12487      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12488      * If available, this function will be called only after the basic validators all return true, and will be passed the
12489      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12490      */
12491     validator : null,
12492     /**
12493      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12494      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12495      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12496      */
12497     regex : null,
12498     /**
12499      * @cfg {String} regexText -- Depricated - use Invalid Text
12500      */
12501     regexText : "",
12502     
12503     /**
12504      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12505      */
12506     invalidText : "",
12507     
12508     
12509     
12510     autocomplete: false,
12511     
12512     
12513     fieldLabel : '',
12514     inputType : 'text',
12515     
12516     name : false,
12517     placeholder: false,
12518     before : false,
12519     after : false,
12520     size : false,
12521     hasFocus : false,
12522     preventMark: false,
12523     isFormField : true,
12524     value : '',
12525     labelWidth : 2,
12526     labelAlign : false,
12527     readOnly : false,
12528     align : false,
12529     formatedValue : false,
12530     forceFeedback : false,
12531     
12532     indicatorpos : 'left',
12533     
12534     labellg : 0,
12535     labelmd : 0,
12536     labelsm : 0,
12537     labelxs : 0,
12538     
12539     capture : '',
12540     accept : '',
12541     
12542     parentLabelAlign : function()
12543     {
12544         var parent = this;
12545         while (parent.parent()) {
12546             parent = parent.parent();
12547             if (typeof(parent.labelAlign) !='undefined') {
12548                 return parent.labelAlign;
12549             }
12550         }
12551         return 'left';
12552         
12553     },
12554     
12555     getAutoCreate : function()
12556     {
12557         
12558         var id = Roo.id();
12559         
12560         var cfg = {};
12561         
12562         if(this.inputType != 'hidden'){
12563             cfg.cls = 'form-group' //input-group
12564         }
12565         
12566         var input =  {
12567             tag: 'input',
12568             id : id,
12569             type : this.inputType,
12570             value : this.value,
12571             cls : 'form-control',
12572             placeholder : this.placeholder || '',
12573             autocomplete : this.autocomplete || 'new-password'
12574         };
12575         if (this.inputType == 'file') {
12576             input.style = 'overflow:hidden'; // why not in CSS?
12577         }
12578         
12579         if(this.capture.length){
12580             input.capture = this.capture;
12581         }
12582         
12583         if(this.accept.length){
12584             input.accept = this.accept + "/*";
12585         }
12586         
12587         if(this.align){
12588             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12589         }
12590         
12591         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12592             input.maxLength = this.maxLength;
12593         }
12594         
12595         if (this.disabled) {
12596             input.disabled=true;
12597         }
12598         
12599         if (this.readOnly) {
12600             input.readonly=true;
12601         }
12602         
12603         if (this.name) {
12604             input.name = this.name;
12605         }
12606         
12607         if (this.size) {
12608             input.cls += ' input-' + this.size;
12609         }
12610         
12611         var settings=this;
12612         ['xs','sm','md','lg'].map(function(size){
12613             if (settings[size]) {
12614                 cfg.cls += ' col-' + size + '-' + settings[size];
12615             }
12616         });
12617         
12618         var inputblock = input;
12619         
12620         var feedback = {
12621             tag: 'span',
12622             cls: 'glyphicon form-control-feedback'
12623         };
12624             
12625         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12626             
12627             inputblock = {
12628                 cls : 'has-feedback',
12629                 cn :  [
12630                     input,
12631                     feedback
12632                 ] 
12633             };  
12634         }
12635         
12636         if (this.before || this.after) {
12637             
12638             inputblock = {
12639                 cls : 'input-group',
12640                 cn :  [] 
12641             };
12642             
12643             if (this.before && typeof(this.before) == 'string') {
12644                 
12645                 inputblock.cn.push({
12646                     tag :'span',
12647                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12648                     html : this.before
12649                 });
12650             }
12651             if (this.before && typeof(this.before) == 'object') {
12652                 this.before = Roo.factory(this.before);
12653                 
12654                 inputblock.cn.push({
12655                     tag :'span',
12656                     cls : 'roo-input-before input-group-prepend   input-group-' +
12657                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12658                 });
12659             }
12660             
12661             inputblock.cn.push(input);
12662             
12663             if (this.after && typeof(this.after) == 'string') {
12664                 inputblock.cn.push({
12665                     tag :'span',
12666                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12667                     html : this.after
12668                 });
12669             }
12670             if (this.after && typeof(this.after) == 'object') {
12671                 this.after = Roo.factory(this.after);
12672                 
12673                 inputblock.cn.push({
12674                     tag :'span',
12675                     cls : 'roo-input-after input-group-append  input-group-' +
12676                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12677                 });
12678             }
12679             
12680             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12681                 inputblock.cls += ' has-feedback';
12682                 inputblock.cn.push(feedback);
12683             }
12684         };
12685         
12686         
12687         
12688         cfg = this.getAutoCreateLabel( cfg, inputblock );
12689         
12690        
12691          
12692         
12693         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12694            cfg.cls += ' navbar-form';
12695         }
12696         
12697         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12698             // on BS4 we do this only if not form 
12699             cfg.cls += ' navbar-form';
12700             cfg.tag = 'li';
12701         }
12702         
12703         return cfg;
12704         
12705     },
12706     /**
12707      * autocreate the label - also used by textara... ?? and others?
12708      */
12709     getAutoCreateLabel : function( cfg, inputblock )
12710     {
12711         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12712        
12713         var indicator = {
12714             tag : 'i',
12715             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12716             tooltip : 'This field is required'
12717         };
12718         if (this.allowBlank ) {
12719             indicator.style = this.allowBlank ? ' display:none' : '';
12720         }
12721         if (align ==='left' && this.fieldLabel.length) {
12722             
12723             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12724             
12725             cfg.cn = [
12726                 indicator,
12727                 {
12728                     tag: 'label',
12729                     'for' :  id,
12730                     cls : 'control-label col-form-label',
12731                     html : this.fieldLabel
12732
12733                 },
12734                 {
12735                     cls : "", 
12736                     cn: [
12737                         inputblock
12738                     ]
12739                 }
12740             ];
12741             
12742             var labelCfg = cfg.cn[1];
12743             var contentCfg = cfg.cn[2];
12744             
12745             if(this.indicatorpos == 'right'){
12746                 cfg.cn = [
12747                     {
12748                         tag: 'label',
12749                         'for' :  id,
12750                         cls : 'control-label col-form-label',
12751                         cn : [
12752                             {
12753                                 tag : 'span',
12754                                 html : this.fieldLabel
12755                             },
12756                             indicator
12757                         ]
12758                     },
12759                     {
12760                         cls : "",
12761                         cn: [
12762                             inputblock
12763                         ]
12764                     }
12765
12766                 ];
12767                 
12768                 labelCfg = cfg.cn[0];
12769                 contentCfg = cfg.cn[1];
12770             
12771             }
12772             
12773             if(this.labelWidth > 12){
12774                 labelCfg.style = "width: " + this.labelWidth + 'px';
12775             }
12776             
12777             if(this.labelWidth < 13 && this.labelmd == 0){
12778                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12779             }
12780             
12781             if(this.labellg > 0){
12782                 labelCfg.cls += ' col-lg-' + this.labellg;
12783                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12784             }
12785             
12786             if(this.labelmd > 0){
12787                 labelCfg.cls += ' col-md-' + this.labelmd;
12788                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12789             }
12790             
12791             if(this.labelsm > 0){
12792                 labelCfg.cls += ' col-sm-' + this.labelsm;
12793                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12794             }
12795             
12796             if(this.labelxs > 0){
12797                 labelCfg.cls += ' col-xs-' + this.labelxs;
12798                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12799             }
12800             
12801             
12802         } else if ( this.fieldLabel.length) {
12803                 
12804             
12805             
12806             cfg.cn = [
12807                 {
12808                     tag : 'i',
12809                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12810                     tooltip : 'This field is required',
12811                     style : this.allowBlank ? ' display:none' : '' 
12812                 },
12813                 {
12814                     tag: 'label',
12815                    //cls : 'input-group-addon',
12816                     html : this.fieldLabel
12817
12818                 },
12819
12820                inputblock
12821
12822            ];
12823            
12824            if(this.indicatorpos == 'right'){
12825        
12826                 cfg.cn = [
12827                     {
12828                         tag: 'label',
12829                        //cls : 'input-group-addon',
12830                         html : this.fieldLabel
12831
12832                     },
12833                     {
12834                         tag : 'i',
12835                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12836                         tooltip : 'This field is required',
12837                         style : this.allowBlank ? ' display:none' : '' 
12838                     },
12839
12840                    inputblock
12841
12842                ];
12843
12844             }
12845
12846         } else {
12847             
12848             cfg.cn = [
12849
12850                     inputblock
12851
12852             ];
12853                 
12854                 
12855         };
12856         return cfg;
12857     },
12858     
12859     
12860     /**
12861      * return the real input element.
12862      */
12863     inputEl: function ()
12864     {
12865         return this.el.select('input.form-control',true).first();
12866     },
12867     
12868     tooltipEl : function()
12869     {
12870         return this.inputEl();
12871     },
12872     
12873     indicatorEl : function()
12874     {
12875         if (Roo.bootstrap.version == 4) {
12876             return false; // not enabled in v4 yet.
12877         }
12878         
12879         var indicator = this.el.select('i.roo-required-indicator',true).first();
12880         
12881         if(!indicator){
12882             return false;
12883         }
12884         
12885         return indicator;
12886         
12887     },
12888     
12889     setDisabled : function(v)
12890     {
12891         var i  = this.inputEl().dom;
12892         if (!v) {
12893             i.removeAttribute('disabled');
12894             return;
12895             
12896         }
12897         i.setAttribute('disabled','true');
12898     },
12899     initEvents : function()
12900     {
12901           
12902         this.inputEl().on("keydown" , this.fireKey,  this);
12903         this.inputEl().on("focus", this.onFocus,  this);
12904         this.inputEl().on("blur", this.onBlur,  this);
12905         
12906         this.inputEl().relayEvent('keyup', this);
12907         this.inputEl().relayEvent('paste', this);
12908         
12909         this.indicator = this.indicatorEl();
12910         
12911         if(this.indicator){
12912             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12913         }
12914  
12915         // reference to original value for reset
12916         this.originalValue = this.getValue();
12917         //Roo.form.TextField.superclass.initEvents.call(this);
12918         if(this.validationEvent == 'keyup'){
12919             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12920             this.inputEl().on('keyup', this.filterValidation, this);
12921         }
12922         else if(this.validationEvent !== false){
12923             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12924         }
12925         
12926         if(this.selectOnFocus){
12927             this.on("focus", this.preFocus, this);
12928             
12929         }
12930         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12931             this.inputEl().on("keypress", this.filterKeys, this);
12932         } else {
12933             this.inputEl().relayEvent('keypress', this);
12934         }
12935        /* if(this.grow){
12936             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12937             this.el.on("click", this.autoSize,  this);
12938         }
12939         */
12940         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12941             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12942         }
12943         
12944         if (typeof(this.before) == 'object') {
12945             this.before.render(this.el.select('.roo-input-before',true).first());
12946         }
12947         if (typeof(this.after) == 'object') {
12948             this.after.render(this.el.select('.roo-input-after',true).first());
12949         }
12950         
12951         this.inputEl().on('change', this.onChange, this);
12952         
12953     },
12954     filterValidation : function(e){
12955         if(!e.isNavKeyPress()){
12956             this.validationTask.delay(this.validationDelay);
12957         }
12958     },
12959      /**
12960      * Validates the field value
12961      * @return {Boolean} True if the value is valid, else false
12962      */
12963     validate : function(){
12964         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12965         if(this.disabled || this.validateValue(this.getRawValue())){
12966             this.markValid();
12967             return true;
12968         }
12969         
12970         this.markInvalid();
12971         return false;
12972     },
12973     
12974     
12975     /**
12976      * Validates a value according to the field's validation rules and marks the field as invalid
12977      * if the validation fails
12978      * @param {Mixed} value The value to validate
12979      * @return {Boolean} True if the value is valid, else false
12980      */
12981     validateValue : function(value)
12982     {
12983         if(this.getVisibilityEl().hasClass('hidden')){
12984             return true;
12985         }
12986         
12987         if(value.length < 1)  { // if it's blank
12988             if(this.allowBlank){
12989                 return true;
12990             }
12991             return false;
12992         }
12993         
12994         if(value.length < this.minLength){
12995             return false;
12996         }
12997         if(value.length > this.maxLength){
12998             return false;
12999         }
13000         if(this.vtype){
13001             var vt = Roo.form.VTypes;
13002             if(!vt[this.vtype](value, this)){
13003                 return false;
13004             }
13005         }
13006         if(typeof this.validator == "function"){
13007             var msg = this.validator(value);
13008             if (typeof(msg) == 'string') {
13009                 this.invalidText = msg;
13010             }
13011             if(msg !== true){
13012                 return false;
13013             }
13014         }
13015         
13016         if(this.regex && !this.regex.test(value)){
13017             return false;
13018         }
13019         
13020         return true;
13021     },
13022     
13023      // private
13024     fireKey : function(e){
13025         //Roo.log('field ' + e.getKey());
13026         if(e.isNavKeyPress()){
13027             this.fireEvent("specialkey", this, e);
13028         }
13029     },
13030     focus : function (selectText){
13031         if(this.rendered){
13032             this.inputEl().focus();
13033             if(selectText === true){
13034                 this.inputEl().dom.select();
13035             }
13036         }
13037         return this;
13038     } ,
13039     
13040     onFocus : function(){
13041         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13042            // this.el.addClass(this.focusClass);
13043         }
13044         if(!this.hasFocus){
13045             this.hasFocus = true;
13046             this.startValue = this.getValue();
13047             this.fireEvent("focus", this);
13048         }
13049     },
13050     
13051     beforeBlur : Roo.emptyFn,
13052
13053     
13054     // private
13055     onBlur : function(){
13056         this.beforeBlur();
13057         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13058             //this.el.removeClass(this.focusClass);
13059         }
13060         this.hasFocus = false;
13061         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13062             this.validate();
13063         }
13064         var v = this.getValue();
13065         if(String(v) !== String(this.startValue)){
13066             this.fireEvent('change', this, v, this.startValue);
13067         }
13068         this.fireEvent("blur", this);
13069     },
13070     
13071     onChange : function(e)
13072     {
13073         var v = this.getValue();
13074         if(String(v) !== String(this.startValue)){
13075             this.fireEvent('change', this, v, this.startValue);
13076         }
13077         
13078     },
13079     
13080     /**
13081      * Resets the current field value to the originally loaded value and clears any validation messages
13082      */
13083     reset : function(){
13084         this.setValue(this.originalValue);
13085         this.validate();
13086     },
13087      /**
13088      * Returns the name of the field
13089      * @return {Mixed} name The name field
13090      */
13091     getName: function(){
13092         return this.name;
13093     },
13094      /**
13095      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13096      * @return {Mixed} value The field value
13097      */
13098     getValue : function(){
13099         var v = this.inputEl().getValue();
13100         return v;
13101     },
13102     /**
13103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13104      * @return {Mixed} value The field value
13105      */
13106     getRawValue : function(){
13107         var v = this.inputEl().getValue();
13108         
13109         return v;
13110     },
13111     
13112     /**
13113      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13114      * @param {Mixed} value The value to set
13115      */
13116     setRawValue : function(v){
13117         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13118     },
13119     
13120     selectText : function(start, end){
13121         var v = this.getRawValue();
13122         if(v.length > 0){
13123             start = start === undefined ? 0 : start;
13124             end = end === undefined ? v.length : end;
13125             var d = this.inputEl().dom;
13126             if(d.setSelectionRange){
13127                 d.setSelectionRange(start, end);
13128             }else if(d.createTextRange){
13129                 var range = d.createTextRange();
13130                 range.moveStart("character", start);
13131                 range.moveEnd("character", v.length-end);
13132                 range.select();
13133             }
13134         }
13135     },
13136     
13137     /**
13138      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13139      * @param {Mixed} value The value to set
13140      */
13141     setValue : function(v){
13142         this.value = v;
13143         if(this.rendered){
13144             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13145             this.validate();
13146         }
13147     },
13148     
13149     /*
13150     processValue : function(value){
13151         if(this.stripCharsRe){
13152             var newValue = value.replace(this.stripCharsRe, '');
13153             if(newValue !== value){
13154                 this.setRawValue(newValue);
13155                 return newValue;
13156             }
13157         }
13158         return value;
13159     },
13160   */
13161     preFocus : function(){
13162         
13163         if(this.selectOnFocus){
13164             this.inputEl().dom.select();
13165         }
13166     },
13167     filterKeys : function(e){
13168         var k = e.getKey();
13169         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13170             return;
13171         }
13172         var c = e.getCharCode(), cc = String.fromCharCode(c);
13173         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13174             return;
13175         }
13176         if(!this.maskRe.test(cc)){
13177             e.stopEvent();
13178         }
13179     },
13180      /**
13181      * Clear any invalid styles/messages for this field
13182      */
13183     clearInvalid : function(){
13184         
13185         if(!this.el || this.preventMark){ // not rendered
13186             return;
13187         }
13188         
13189         
13190         this.el.removeClass([this.invalidClass, 'is-invalid']);
13191         
13192         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13193             
13194             var feedback = this.el.select('.form-control-feedback', true).first();
13195             
13196             if(feedback){
13197                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13198             }
13199             
13200         }
13201         
13202         if(this.indicator){
13203             this.indicator.removeClass('visible');
13204             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13205         }
13206         
13207         this.fireEvent('valid', this);
13208     },
13209     
13210      /**
13211      * Mark this field as valid
13212      */
13213     markValid : function()
13214     {
13215         if(!this.el  || this.preventMark){ // not rendered...
13216             return;
13217         }
13218         
13219         this.el.removeClass([this.invalidClass, this.validClass]);
13220         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13221
13222         var feedback = this.el.select('.form-control-feedback', true).first();
13223             
13224         if(feedback){
13225             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226         }
13227         
13228         if(this.indicator){
13229             this.indicator.removeClass('visible');
13230             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13231         }
13232         
13233         if(this.disabled){
13234             return;
13235         }
13236         
13237            
13238         if(this.allowBlank && !this.getRawValue().length){
13239             return;
13240         }
13241         if (Roo.bootstrap.version == 3) {
13242             this.el.addClass(this.validClass);
13243         } else {
13244             this.inputEl().addClass('is-valid');
13245         }
13246
13247         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13248             
13249             var feedback = this.el.select('.form-control-feedback', true).first();
13250             
13251             if(feedback){
13252                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13253                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13254             }
13255             
13256         }
13257         
13258         this.fireEvent('valid', this);
13259     },
13260     
13261      /**
13262      * Mark this field as invalid
13263      * @param {String} msg The validation message
13264      */
13265     markInvalid : function(msg)
13266     {
13267         if(!this.el  || this.preventMark){ // not rendered
13268             return;
13269         }
13270         
13271         this.el.removeClass([this.invalidClass, this.validClass]);
13272         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13273         
13274         var feedback = this.el.select('.form-control-feedback', true).first();
13275             
13276         if(feedback){
13277             this.el.select('.form-control-feedback', true).first().removeClass(
13278                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13279         }
13280
13281         if(this.disabled){
13282             return;
13283         }
13284         
13285         if(this.allowBlank && !this.getRawValue().length){
13286             return;
13287         }
13288         
13289         if(this.indicator){
13290             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13291             this.indicator.addClass('visible');
13292         }
13293         if (Roo.bootstrap.version == 3) {
13294             this.el.addClass(this.invalidClass);
13295         } else {
13296             this.inputEl().addClass('is-invalid');
13297         }
13298         
13299         
13300         
13301         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13302             
13303             var feedback = this.el.select('.form-control-feedback', true).first();
13304             
13305             if(feedback){
13306                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13307                 
13308                 if(this.getValue().length || this.forceFeedback){
13309                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13310                 }
13311                 
13312             }
13313             
13314         }
13315         
13316         this.fireEvent('invalid', this, msg);
13317     },
13318     // private
13319     SafariOnKeyDown : function(event)
13320     {
13321         // this is a workaround for a password hang bug on chrome/ webkit.
13322         if (this.inputEl().dom.type != 'password') {
13323             return;
13324         }
13325         
13326         var isSelectAll = false;
13327         
13328         if(this.inputEl().dom.selectionEnd > 0){
13329             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13330         }
13331         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13332             event.preventDefault();
13333             this.setValue('');
13334             return;
13335         }
13336         
13337         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13338             
13339             event.preventDefault();
13340             // this is very hacky as keydown always get's upper case.
13341             //
13342             var cc = String.fromCharCode(event.getCharCode());
13343             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13344             
13345         }
13346     },
13347     adjustWidth : function(tag, w){
13348         tag = tag.toLowerCase();
13349         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13350             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13351                 if(tag == 'input'){
13352                     return w + 2;
13353                 }
13354                 if(tag == 'textarea'){
13355                     return w-2;
13356                 }
13357             }else if(Roo.isOpera){
13358                 if(tag == 'input'){
13359                     return w + 2;
13360                 }
13361                 if(tag == 'textarea'){
13362                     return w-2;
13363                 }
13364             }
13365         }
13366         return w;
13367     },
13368     
13369     setFieldLabel : function(v)
13370     {
13371         if(!this.rendered){
13372             return;
13373         }
13374         
13375         if(this.indicatorEl()){
13376             var ar = this.el.select('label > span',true);
13377             
13378             if (ar.elements.length) {
13379                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13380                 this.fieldLabel = v;
13381                 return;
13382             }
13383             
13384             var br = this.el.select('label',true);
13385             
13386             if(br.elements.length) {
13387                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13388                 this.fieldLabel = v;
13389                 return;
13390             }
13391             
13392             Roo.log('Cannot Found any of label > span || label in input');
13393             return;
13394         }
13395         
13396         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13397         this.fieldLabel = v;
13398         
13399         
13400     }
13401 });
13402
13403  
13404 /*
13405  * - LGPL
13406  *
13407  * Input
13408  * 
13409  */
13410
13411 /**
13412  * @class Roo.bootstrap.form.TextArea
13413  * @extends Roo.bootstrap.form.Input
13414  * Bootstrap TextArea class
13415  * @cfg {Number} cols Specifies the visible width of a text area
13416  * @cfg {Number} rows Specifies the visible number of lines in a text area
13417  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13418  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13419  * @cfg {string} html text
13420  * 
13421  * @constructor
13422  * Create a new TextArea
13423  * @param {Object} config The config object
13424  */
13425
13426 Roo.bootstrap.form.TextArea = function(config){
13427     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13428    
13429 };
13430
13431 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13432      
13433     cols : false,
13434     rows : 5,
13435     readOnly : false,
13436     warp : 'soft',
13437     resize : false,
13438     value: false,
13439     html: false,
13440     
13441     getAutoCreate : function(){
13442         
13443         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13444         
13445         var id = Roo.id();
13446         
13447         var cfg = {};
13448         
13449         if(this.inputType != 'hidden'){
13450             cfg.cls = 'form-group' //input-group
13451         }
13452         
13453         var input =  {
13454             tag: 'textarea',
13455             id : id,
13456             warp : this.warp,
13457             rows : this.rows,
13458             value : this.value || '',
13459             html: this.html || '',
13460             cls : 'form-control',
13461             placeholder : this.placeholder || '' 
13462             
13463         };
13464         
13465         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13466             input.maxLength = this.maxLength;
13467         }
13468         
13469         if(this.resize){
13470             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13471         }
13472         
13473         if(this.cols){
13474             input.cols = this.cols;
13475         }
13476         
13477         if (this.readOnly) {
13478             input.readonly = true;
13479         }
13480         
13481         if (this.name) {
13482             input.name = this.name;
13483         }
13484         
13485         if (this.size) {
13486             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13487         }
13488         
13489         var settings=this;
13490         ['xs','sm','md','lg'].map(function(size){
13491             if (settings[size]) {
13492                 cfg.cls += ' col-' + size + '-' + settings[size];
13493             }
13494         });
13495         
13496         var inputblock = input;
13497         
13498         if(this.hasFeedback && !this.allowBlank){
13499             
13500             var feedback = {
13501                 tag: 'span',
13502                 cls: 'glyphicon form-control-feedback'
13503             };
13504
13505             inputblock = {
13506                 cls : 'has-feedback',
13507                 cn :  [
13508                     input,
13509                     feedback
13510                 ] 
13511             };  
13512         }
13513         
13514         
13515         if (this.before || this.after) {
13516             
13517             inputblock = {
13518                 cls : 'input-group',
13519                 cn :  [] 
13520             };
13521             if (this.before) {
13522                 inputblock.cn.push({
13523                     tag :'span',
13524                     cls : 'input-group-addon',
13525                     html : this.before
13526                 });
13527             }
13528             
13529             inputblock.cn.push(input);
13530             
13531             if(this.hasFeedback && !this.allowBlank){
13532                 inputblock.cls += ' has-feedback';
13533                 inputblock.cn.push(feedback);
13534             }
13535             
13536             if (this.after) {
13537                 inputblock.cn.push({
13538                     tag :'span',
13539                     cls : 'input-group-addon',
13540                     html : this.after
13541                 });
13542             }
13543             
13544         }
13545         
13546         
13547         cfg = this.getAutoCreateLabel( cfg, inputblock );
13548
13549          
13550         
13551         if (this.disabled) {
13552             input.disabled=true;
13553         }
13554         
13555         return cfg;
13556         
13557     },
13558     /**
13559      * return the real textarea element.
13560      */
13561     inputEl: function ()
13562     {
13563         return this.el.select('textarea.form-control',true).first();
13564     },
13565     
13566     /**
13567      * Clear any invalid styles/messages for this field
13568      */
13569     clearInvalid : function()
13570     {
13571         
13572         if(!this.el || this.preventMark){ // not rendered
13573             return;
13574         }
13575         
13576         var label = this.el.select('label', true).first();
13577         //var icon = this.el.select('i.fa-star', true).first();
13578         
13579         //if(label && icon){
13580         //    icon.remove();
13581         //}
13582         this.el.removeClass( this.validClass);
13583         this.inputEl().removeClass('is-invalid');
13584          
13585         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13586             
13587             var feedback = this.el.select('.form-control-feedback', true).first();
13588             
13589             if(feedback){
13590                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13591             }
13592             
13593         }
13594         
13595         this.fireEvent('valid', this);
13596     },
13597     
13598      /**
13599      * Mark this field as valid
13600      */
13601     markValid : function()
13602     {
13603         if(!this.el  || this.preventMark){ // not rendered
13604             return;
13605         }
13606         
13607         this.el.removeClass([this.invalidClass, this.validClass]);
13608         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13609         
13610         var feedback = this.el.select('.form-control-feedback', true).first();
13611             
13612         if(feedback){
13613             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614         }
13615
13616         if(this.disabled || this.allowBlank){
13617             return;
13618         }
13619         
13620         var label = this.el.select('label', true).first();
13621         var icon = this.el.select('i.fa-star', true).first();
13622         
13623         //if(label && icon){
13624         //    icon.remove();
13625         //}
13626         if (Roo.bootstrap.version == 3) {
13627             this.el.addClass(this.validClass);
13628         } else {
13629             this.inputEl().addClass('is-valid');
13630         }
13631         
13632         
13633         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13634             
13635             var feedback = this.el.select('.form-control-feedback', true).first();
13636             
13637             if(feedback){
13638                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13639                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13640             }
13641             
13642         }
13643         
13644         this.fireEvent('valid', this);
13645     },
13646     
13647      /**
13648      * Mark this field as invalid
13649      * @param {String} msg The validation message
13650      */
13651     markInvalid : function(msg)
13652     {
13653         if(!this.el  || this.preventMark){ // not rendered
13654             return;
13655         }
13656         
13657         this.el.removeClass([this.invalidClass, this.validClass]);
13658         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13659         
13660         var feedback = this.el.select('.form-control-feedback', true).first();
13661             
13662         if(feedback){
13663             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13664         }
13665
13666         if(this.disabled){
13667             return;
13668         }
13669         
13670         var label = this.el.select('label', true).first();
13671         //var icon = this.el.select('i.fa-star', true).first();
13672         
13673         //if(!this.getValue().length && label && !icon){
13674           /*  this.el.createChild({
13675                 tag : 'i',
13676                 cls : 'text-danger fa fa-lg fa-star',
13677                 tooltip : 'This field is required',
13678                 style : 'margin-right:5px;'
13679             }, label, true);
13680             */
13681         //}
13682         
13683         if (Roo.bootstrap.version == 3) {
13684             this.el.addClass(this.invalidClass);
13685         } else {
13686             this.inputEl().addClass('is-invalid');
13687         }
13688         
13689         // fixme ... this may be depricated need to test..
13690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13691             
13692             var feedback = this.el.select('.form-control-feedback', true).first();
13693             
13694             if(feedback){
13695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13696                 
13697                 if(this.getValue().length || this.forceFeedback){
13698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699                 }
13700                 
13701             }
13702             
13703         }
13704         
13705         this.fireEvent('invalid', this, msg);
13706     }
13707 });
13708
13709  
13710 /*
13711  * - LGPL
13712  *
13713  * trigger field - base class for combo..
13714  * 
13715  */
13716  
13717 /**
13718  * @class Roo.bootstrap.form.TriggerField
13719  * @extends Roo.bootstrap.form.Input
13720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723  * for which you can provide a custom implementation.  For example:
13724  * <pre><code>
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13728 </code></pre>
13729  *
13730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735
13736  * @constructor
13737  * Create a new TriggerField.
13738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739  * to the base TextField)
13740  */
13741 Roo.bootstrap.form.TriggerField = function(config){
13742     this.mimicing = false;
13743     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 };
13745
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13747     /**
13748      * @cfg {String} triggerClass A CSS class to apply to the trigger
13749      */
13750      /**
13751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13752      */
13753     hideTrigger:false,
13754
13755     /**
13756      * @cfg {Boolean} removable (true|false) special filter default false
13757      */
13758     removable : false,
13759     
13760     /** @cfg {Boolean} grow @hide */
13761     /** @cfg {Number} growMin @hide */
13762     /** @cfg {Number} growMax @hide */
13763
13764     /**
13765      * @hide 
13766      * @method
13767      */
13768     autoSize: Roo.emptyFn,
13769     // private
13770     monitorTab : true,
13771     // private
13772     deferHeight : true,
13773
13774     
13775     actionMode : 'wrap',
13776     
13777     caret : false,
13778     
13779     
13780     getAutoCreate : function(){
13781        
13782         var align = this.labelAlign || this.parentLabelAlign();
13783         
13784         var id = Roo.id();
13785         
13786         var cfg = {
13787             cls: 'form-group' //input-group
13788         };
13789         
13790         
13791         var input =  {
13792             tag: 'input',
13793             id : id,
13794             type : this.inputType,
13795             cls : 'form-control',
13796             autocomplete: 'new-password',
13797             placeholder : this.placeholder || '' 
13798             
13799         };
13800         if (this.name) {
13801             input.name = this.name;
13802         }
13803         if (this.size) {
13804             input.cls += ' input-' + this.size;
13805         }
13806         
13807         if (this.disabled) {
13808             input.disabled=true;
13809         }
13810         
13811         var inputblock = input;
13812         
13813         if(this.hasFeedback && !this.allowBlank){
13814             
13815             var feedback = {
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             };
13819             
13820             if(this.removable && !this.editable  ){
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         },
13830                         feedback
13831                     ] 
13832                 };
13833             } else {
13834                 inputblock = {
13835                     cls : 'has-feedback',
13836                     cn :  [
13837                         inputblock,
13838                         feedback
13839                     ] 
13840                 };
13841             }
13842
13843         } else {
13844             if(this.removable && !this.editable ){
13845                 inputblock = {
13846                     cls : 'roo-removable',
13847                     cn :  [
13848                         inputblock,
13849                         {
13850                             tag: 'button',
13851                             html : 'x',
13852                             cls : 'roo-combo-removable-btn close'
13853                         }
13854                     ] 
13855                 };
13856             }
13857         }
13858         
13859         if (this.before || this.after) {
13860             
13861             inputblock = {
13862                 cls : 'input-group',
13863                 cn :  [] 
13864             };
13865             if (this.before) {
13866                 inputblock.cn.push({
13867                     tag :'span',
13868                     cls : 'input-group-addon input-group-prepend input-group-text',
13869                     html : this.before
13870                 });
13871             }
13872             
13873             inputblock.cn.push(input);
13874             
13875             if(this.hasFeedback && !this.allowBlank){
13876                 inputblock.cls += ' has-feedback';
13877                 inputblock.cn.push(feedback);
13878             }
13879             
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon input-group-append input-group-text',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890       
13891         
13892         var ibwrap = inputblock;
13893         
13894         if(this.multiple){
13895             ibwrap = {
13896                 tag: 'ul',
13897                 cls: 'roo-select2-choices',
13898                 cn:[
13899                     {
13900                         tag: 'li',
13901                         cls: 'roo-select2-search-field',
13902                         cn: [
13903
13904                             inputblock
13905                         ]
13906                     }
13907                 ]
13908             };
13909                 
13910         }
13911         
13912         var combobox = {
13913             cls: 'roo-select2-container input-group',
13914             cn: [
13915                  {
13916                     tag: 'input',
13917                     type : 'hidden',
13918                     cls: 'form-hidden-field'
13919                 },
13920                 ibwrap
13921             ]
13922         };
13923         
13924         if(!this.multiple && this.showToggleBtn){
13925             
13926             var caret = {
13927                         tag: 'span',
13928                         cls: 'caret'
13929              };
13930             if (this.caret != false) {
13931                 caret = {
13932                      tag: 'i',
13933                      cls: 'fa fa-' + this.caret
13934                 };
13935                 
13936             }
13937             
13938             combobox.cn.push({
13939                 tag :'span',
13940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13941                 cn : [
13942                     Roo.bootstrap.version == 3 ? caret : '',
13943                     {
13944                         tag: 'span',
13945                         cls: 'combobox-clear',
13946                         cn  : [
13947                             {
13948                                 tag : 'i',
13949                                 cls: 'icon-remove'
13950                             }
13951                         ]
13952                     }
13953                 ]
13954
13955             })
13956         }
13957         
13958         if(this.multiple){
13959             combobox.cls += ' roo-select2-container-multi';
13960         }
13961          var indicator = {
13962             tag : 'i',
13963             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964             tooltip : 'This field is required'
13965         };
13966       
13967         if (this.allowBlank) {
13968             indicator = {
13969                 tag : 'i',
13970                 style : 'display:none'
13971             };
13972         }
13973          
13974         
13975         
13976         if (align ==='left' && this.fieldLabel.length) {
13977             
13978             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13979
13980             cfg.cn = [
13981                 indicator,
13982                 {
13983                     tag: 'label',
13984                     'for' :  id,
13985                     cls : 'control-label',
13986                     html : this.fieldLabel
13987
13988                 },
13989                 {
13990                     cls : "", 
13991                     cn: [
13992                         combobox
13993                     ]
13994                 }
13995
13996             ];
13997             
13998             var labelCfg = cfg.cn[1];
13999             var contentCfg = cfg.cn[2];
14000             
14001             if(this.indicatorpos == 'right'){
14002                 cfg.cn = [
14003                     {
14004                         tag: 'label',
14005                         'for' :  id,
14006                         cls : 'control-label',
14007                         cn : [
14008                             {
14009                                 tag : 'span',
14010                                 html : this.fieldLabel
14011                             },
14012                             indicator
14013                         ]
14014                     },
14015                     {
14016                         cls : "", 
14017                         cn: [
14018                             combobox
14019                         ]
14020                     }
14021
14022                 ];
14023                 
14024                 labelCfg = cfg.cn[0];
14025                 contentCfg = cfg.cn[1];
14026             }
14027             
14028             if(this.labelWidth > 12){
14029                 labelCfg.style = "width: " + this.labelWidth + 'px';
14030             }
14031             
14032             if(this.labelWidth < 13 && this.labelmd == 0){
14033                 this.labelmd = this.labelWidth;
14034             }
14035             
14036             if(this.labellg > 0){
14037                 labelCfg.cls += ' col-lg-' + this.labellg;
14038                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14039             }
14040             
14041             if(this.labelmd > 0){
14042                 labelCfg.cls += ' col-md-' + this.labelmd;
14043                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14044             }
14045             
14046             if(this.labelsm > 0){
14047                 labelCfg.cls += ' col-sm-' + this.labelsm;
14048                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14049             }
14050             
14051             if(this.labelxs > 0){
14052                 labelCfg.cls += ' col-xs-' + this.labelxs;
14053                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14054             }
14055             
14056         } else if ( this.fieldLabel.length) {
14057 //                Roo.log(" label");
14058             cfg.cn = [
14059                 indicator,
14060                {
14061                    tag: 'label',
14062                    //cls : 'input-group-addon',
14063                    html : this.fieldLabel
14064
14065                },
14066
14067                combobox
14068
14069             ];
14070             
14071             if(this.indicatorpos == 'right'){
14072                 
14073                 cfg.cn = [
14074                     {
14075                        tag: 'label',
14076                        cn : [
14077                            {
14078                                tag : 'span',
14079                                html : this.fieldLabel
14080                            },
14081                            indicator
14082                        ]
14083
14084                     },
14085                     combobox
14086
14087                 ];
14088
14089             }
14090
14091         } else {
14092             
14093 //                Roo.log(" no label && no align");
14094                 cfg = combobox
14095                      
14096                 
14097         }
14098         
14099         var settings=this;
14100         ['xs','sm','md','lg'].map(function(size){
14101             if (settings[size]) {
14102                 cfg.cls += ' col-' + size + '-' + settings[size];
14103             }
14104         });
14105         
14106         return cfg;
14107         
14108     },
14109     
14110     
14111     
14112     // private
14113     onResize : function(w, h){
14114 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14115 //        if(typeof w == 'number'){
14116 //            var x = w - this.trigger.getWidth();
14117 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14118 //            this.trigger.setStyle('left', x+'px');
14119 //        }
14120     },
14121
14122     // private
14123     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14124
14125     // private
14126     getResizeEl : function(){
14127         return this.inputEl();
14128     },
14129
14130     // private
14131     getPositionEl : function(){
14132         return this.inputEl();
14133     },
14134
14135     // private
14136     alignErrorIcon : function(){
14137         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14138     },
14139
14140     // private
14141     initEvents : function(){
14142         
14143         this.createList();
14144         
14145         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14146         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14147         if(!this.multiple && this.showToggleBtn){
14148             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14149             if(this.hideTrigger){
14150                 this.trigger.setDisplayed(false);
14151             }
14152             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14153         }
14154         
14155         if(this.multiple){
14156             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14157         }
14158         
14159         if(this.removable && !this.editable && !this.tickable){
14160             var close = this.closeTriggerEl();
14161             
14162             if(close){
14163                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14164                 close.on('click', this.removeBtnClick, this, close);
14165             }
14166         }
14167         
14168         //this.trigger.addClassOnOver('x-form-trigger-over');
14169         //this.trigger.addClassOnClick('x-form-trigger-click');
14170         
14171         //if(!this.width){
14172         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14173         //}
14174     },
14175     
14176     closeTriggerEl : function()
14177     {
14178         var close = this.el.select('.roo-combo-removable-btn', true).first();
14179         return close ? close : false;
14180     },
14181     
14182     removeBtnClick : function(e, h, el)
14183     {
14184         e.preventDefault();
14185         
14186         if(this.fireEvent("remove", this) !== false){
14187             this.reset();
14188             this.fireEvent("afterremove", this)
14189         }
14190     },
14191     
14192     createList : function()
14193     {
14194         this.list = Roo.get(document.body).createChild({
14195             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14196             cls: 'typeahead typeahead-long dropdown-menu shadow',
14197             style: 'display:none'
14198         });
14199         
14200         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14201         
14202     },
14203
14204     // private
14205     initTrigger : function(){
14206        
14207     },
14208
14209     // private
14210     onDestroy : function(){
14211         if(this.trigger){
14212             this.trigger.removeAllListeners();
14213           //  this.trigger.remove();
14214         }
14215         //if(this.wrap){
14216         //    this.wrap.remove();
14217         //}
14218         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14219     },
14220
14221     // private
14222     onFocus : function(){
14223         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14224         /*
14225         if(!this.mimicing){
14226             this.wrap.addClass('x-trigger-wrap-focus');
14227             this.mimicing = true;
14228             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14229             if(this.monitorTab){
14230                 this.el.on("keydown", this.checkTab, this);
14231             }
14232         }
14233         */
14234     },
14235
14236     // private
14237     checkTab : function(e){
14238         if(e.getKey() == e.TAB){
14239             this.triggerBlur();
14240         }
14241     },
14242
14243     // private
14244     onBlur : function(){
14245         // do nothing
14246     },
14247
14248     // private
14249     mimicBlur : function(e, t){
14250         /*
14251         if(!this.wrap.contains(t) && this.validateBlur()){
14252             this.triggerBlur();
14253         }
14254         */
14255     },
14256
14257     // private
14258     triggerBlur : function(){
14259         this.mimicing = false;
14260         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14261         if(this.monitorTab){
14262             this.el.un("keydown", this.checkTab, this);
14263         }
14264         //this.wrap.removeClass('x-trigger-wrap-focus');
14265         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14266     },
14267
14268     // private
14269     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14270     validateBlur : function(e, t){
14271         return true;
14272     },
14273
14274     // private
14275     onDisable : function(){
14276         this.inputEl().dom.disabled = true;
14277         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14278         //if(this.wrap){
14279         //    this.wrap.addClass('x-item-disabled');
14280         //}
14281     },
14282
14283     // private
14284     onEnable : function(){
14285         this.inputEl().dom.disabled = false;
14286         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14287         //if(this.wrap){
14288         //    this.el.removeClass('x-item-disabled');
14289         //}
14290     },
14291
14292     // private
14293     onShow : function(){
14294         var ae = this.getActionEl();
14295         
14296         if(ae){
14297             ae.dom.style.display = '';
14298             ae.dom.style.visibility = 'visible';
14299         }
14300     },
14301
14302     // private
14303     
14304     onHide : function(){
14305         var ae = this.getActionEl();
14306         ae.dom.style.display = 'none';
14307     },
14308
14309     /**
14310      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14311      * by an implementing function.
14312      * @method
14313      * @param {EventObject} e
14314      */
14315     onTriggerClick : Roo.emptyFn
14316 });
14317  
14318 /*
14319 * Licence: LGPL
14320 */
14321
14322 /**
14323  * @class Roo.bootstrap.form.CardUploader
14324  * @extends Roo.bootstrap.Button
14325  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14326  * @cfg {Number} errorTimeout default 3000
14327  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14328  * @cfg {Array}  html The button text.
14329
14330  *
14331  * @constructor
14332  * Create a new CardUploader
14333  * @param {Object} config The config object
14334  */
14335
14336 Roo.bootstrap.form.CardUploader = function(config){
14337     
14338  
14339     
14340     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14341     
14342     
14343     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14344         return r.data.id
14345      });
14346     
14347      this.addEvents({
14348          // raw events
14349         /**
14350          * @event preview
14351          * When a image is clicked on - and needs to display a slideshow or similar..
14352          * @param {Roo.bootstrap.Card} this
14353          * @param {Object} The image information data 
14354          *
14355          */
14356         'preview' : true,
14357          /**
14358          * @event download
14359          * When a the download link is clicked
14360          * @param {Roo.bootstrap.Card} this
14361          * @param {Object} The image information data  contains 
14362          */
14363         'download' : true
14364         
14365     });
14366 };
14367  
14368 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14369     
14370      
14371     errorTimeout : 3000,
14372      
14373     images : false,
14374    
14375     fileCollection : false,
14376     allowBlank : true,
14377     
14378     getAutoCreate : function()
14379     {
14380         
14381         var cfg =  {
14382             cls :'form-group' ,
14383             cn : [
14384                
14385                 {
14386                     tag: 'label',
14387                    //cls : 'input-group-addon',
14388                     html : this.fieldLabel
14389
14390                 },
14391
14392                 {
14393                     tag: 'input',
14394                     type : 'hidden',
14395                     name : this.name,
14396                     value : this.value,
14397                     cls : 'd-none  form-control'
14398                 },
14399                 
14400                 {
14401                     tag: 'input',
14402                     multiple : 'multiple',
14403                     type : 'file',
14404                     cls : 'd-none  roo-card-upload-selector'
14405                 },
14406                 
14407                 {
14408                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14409                 },
14410                 {
14411                     cls : 'card-columns roo-card-uploader-container'
14412                 }
14413
14414             ]
14415         };
14416            
14417          
14418         return cfg;
14419     },
14420     
14421     getChildContainer : function() /// what children are added to.
14422     {
14423         return this.containerEl;
14424     },
14425    
14426     getButtonContainer : function() /// what children are added to.
14427     {
14428         return this.el.select(".roo-card-uploader-button-container").first();
14429     },
14430    
14431     initEvents : function()
14432     {
14433         
14434         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14435         
14436         var t = this;
14437         this.addxtype({
14438             xns: Roo.bootstrap,
14439
14440             xtype : 'Button',
14441             container_method : 'getButtonContainer' ,            
14442             html :  this.html, // fix changable?
14443             cls : 'w-100 ',
14444             listeners : {
14445                 'click' : function(btn, e) {
14446                     t.onClick(e);
14447                 }
14448             }
14449         });
14450         
14451         
14452         
14453         
14454         this.urlAPI = (window.createObjectURL && window) || 
14455                                 (window.URL && URL.revokeObjectURL && URL) || 
14456                                 (window.webkitURL && webkitURL);
14457                         
14458          
14459          
14460          
14461         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14462         
14463         this.selectorEl.on('change', this.onFileSelected, this);
14464         if (this.images) {
14465             var t = this;
14466             this.images.forEach(function(img) {
14467                 t.addCard(img)
14468             });
14469             this.images = false;
14470         }
14471         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14472          
14473        
14474     },
14475     
14476    
14477     onClick : function(e)
14478     {
14479         e.preventDefault();
14480          
14481         this.selectorEl.dom.click();
14482          
14483     },
14484     
14485     onFileSelected : function(e)
14486     {
14487         e.preventDefault();
14488         
14489         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14490             return;
14491         }
14492         
14493         Roo.each(this.selectorEl.dom.files, function(file){    
14494             this.addFile(file);
14495         }, this);
14496          
14497     },
14498     
14499       
14500     
14501       
14502     
14503     addFile : function(file)
14504     {
14505            
14506         if(typeof(file) === 'string'){
14507             throw "Add file by name?"; // should not happen
14508             return;
14509         }
14510         
14511         if(!file || !this.urlAPI){
14512             return;
14513         }
14514         
14515         // file;
14516         // file.type;
14517         
14518         var _this = this;
14519         
14520         
14521         var url = _this.urlAPI.createObjectURL( file);
14522            
14523         this.addCard({
14524             id : Roo.bootstrap.form.CardUploader.ID--,
14525             is_uploaded : false,
14526             src : url,
14527             srcfile : file,
14528             title : file.name,
14529             mimetype : file.type,
14530             preview : false,
14531             is_deleted : 0
14532         });
14533         
14534     },
14535     
14536     /**
14537      * addCard - add an Attachment to the uploader
14538      * @param data - the data about the image to upload
14539      *
14540      * {
14541           id : 123
14542           title : "Title of file",
14543           is_uploaded : false,
14544           src : "http://.....",
14545           srcfile : { the File upload object },
14546           mimetype : file.type,
14547           preview : false,
14548           is_deleted : 0
14549           .. any other data...
14550         }
14551      *
14552      * 
14553     */
14554     
14555     addCard : function (data)
14556     {
14557         // hidden input element?
14558         // if the file is not an image...
14559         //then we need to use something other that and header_image
14560         var t = this;
14561         //   remove.....
14562         var footer = [
14563             {
14564                 xns : Roo.bootstrap,
14565                 xtype : 'CardFooter',
14566                  items: [
14567                     {
14568                         xns : Roo.bootstrap,
14569                         xtype : 'Element',
14570                         cls : 'd-flex',
14571                         items : [
14572                             
14573                             {
14574                                 xns : Roo.bootstrap,
14575                                 xtype : 'Button',
14576                                 html : String.format("<small>{0}</small>", data.title),
14577                                 cls : 'col-10 text-left',
14578                                 size: 'sm',
14579                                 weight: 'link',
14580                                 fa : 'download',
14581                                 listeners : {
14582                                     click : function() {
14583                                      
14584                                         t.fireEvent( "download", t, data );
14585                                     }
14586                                 }
14587                             },
14588                           
14589                             {
14590                                 xns : Roo.bootstrap,
14591                                 xtype : 'Button',
14592                                 style: 'max-height: 28px; ',
14593                                 size : 'sm',
14594                                 weight: 'danger',
14595                                 cls : 'col-2',
14596                                 fa : 'times',
14597                                 listeners : {
14598                                     click : function() {
14599                                         t.removeCard(data.id)
14600                                     }
14601                                 }
14602                             }
14603                         ]
14604                     }
14605                     
14606                 ] 
14607             }
14608             
14609         ];
14610         
14611         var cn = this.addxtype(
14612             {
14613                  
14614                 xns : Roo.bootstrap,
14615                 xtype : 'Card',
14616                 closeable : true,
14617                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14618                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14619                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14620                 data : data,
14621                 html : false,
14622                  
14623                 items : footer,
14624                 initEvents : function() {
14625                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14626                     var card = this;
14627                     this.imgEl = this.el.select('.card-img-top').first();
14628                     if (this.imgEl) {
14629                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14630                         this.imgEl.set({ 'pointer' : 'cursor' });
14631                                   
14632                     }
14633                     this.getCardFooter().addClass('p-1');
14634                     
14635                   
14636                 }
14637                 
14638             }
14639         );
14640         // dont' really need ot update items.
14641         // this.items.push(cn);
14642         this.fileCollection.add(cn);
14643         
14644         if (!data.srcfile) {
14645             this.updateInput();
14646             return;
14647         }
14648             
14649         var _t = this;
14650         var reader = new FileReader();
14651         reader.addEventListener("load", function() {  
14652             data.srcdata =  reader.result;
14653             _t.updateInput();
14654         });
14655         reader.readAsDataURL(data.srcfile);
14656         
14657         
14658         
14659     },
14660     removeCard : function(id)
14661     {
14662         
14663         var card  = this.fileCollection.get(id);
14664         card.data.is_deleted = 1;
14665         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14666         //this.fileCollection.remove(card);
14667         //this.items = this.items.filter(function(e) { return e != card });
14668         // dont' really need ot update items.
14669         card.el.dom.parentNode.removeChild(card.el.dom);
14670         this.updateInput();
14671
14672         
14673     },
14674     reset: function()
14675     {
14676         this.fileCollection.each(function(card) {
14677             if (card.el.dom && card.el.dom.parentNode) {
14678                 card.el.dom.parentNode.removeChild(card.el.dom);
14679             }
14680         });
14681         this.fileCollection.clear();
14682         this.updateInput();
14683     },
14684     
14685     updateInput : function()
14686     {
14687          var data = [];
14688         this.fileCollection.each(function(e) {
14689             data.push(e.data);
14690             
14691         });
14692         this.inputEl().dom.value = JSON.stringify(data);
14693         
14694         
14695         
14696     }
14697     
14698     
14699 });
14700
14701
14702 Roo.bootstrap.form.CardUploader.ID = -1;/*
14703  * Based on:
14704  * Ext JS Library 1.1.1
14705  * Copyright(c) 2006-2007, Ext JS, LLC.
14706  *
14707  * Originally Released Under LGPL - original licence link has changed is not relivant.
14708  *
14709  * Fork - LGPL
14710  * <script type="text/javascript">
14711  */
14712
14713
14714 /**
14715  * @class Roo.data.SortTypes
14716  * @static
14717  * Defines the default sorting (casting?) comparison functions used when sorting data.
14718  */
14719 Roo.data.SortTypes = {
14720     /**
14721      * Default sort that does nothing
14722      * @param {Mixed} s The value being converted
14723      * @return {Mixed} The comparison value
14724      */
14725     none : function(s){
14726         return s;
14727     },
14728     
14729     /**
14730      * The regular expression used to strip tags
14731      * @type {RegExp}
14732      * @property
14733      */
14734     stripTagsRE : /<\/?[^>]+>/gi,
14735     
14736     /**
14737      * Strips all HTML tags to sort on text only
14738      * @param {Mixed} s The value being converted
14739      * @return {String} The comparison value
14740      */
14741     asText : function(s){
14742         return String(s).replace(this.stripTagsRE, "");
14743     },
14744     
14745     /**
14746      * Strips all HTML tags to sort on text only - Case insensitive
14747      * @param {Mixed} s The value being converted
14748      * @return {String} The comparison value
14749      */
14750     asUCText : function(s){
14751         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14752     },
14753     
14754     /**
14755      * Case insensitive string
14756      * @param {Mixed} s The value being converted
14757      * @return {String} The comparison value
14758      */
14759     asUCString : function(s) {
14760         return String(s).toUpperCase();
14761     },
14762     
14763     /**
14764      * Date sorting
14765      * @param {Mixed} s The value being converted
14766      * @return {Number} The comparison value
14767      */
14768     asDate : function(s) {
14769         if(!s){
14770             return 0;
14771         }
14772         if(s instanceof Date){
14773             return s.getTime();
14774         }
14775         return Date.parse(String(s));
14776     },
14777     
14778     /**
14779      * Float sorting
14780      * @param {Mixed} s The value being converted
14781      * @return {Float} The comparison value
14782      */
14783     asFloat : function(s) {
14784         var val = parseFloat(String(s).replace(/,/g, ""));
14785         if(isNaN(val)) {
14786             val = 0;
14787         }
14788         return val;
14789     },
14790     
14791     /**
14792      * Integer sorting
14793      * @param {Mixed} s The value being converted
14794      * @return {Number} The comparison value
14795      */
14796     asInt : function(s) {
14797         var val = parseInt(String(s).replace(/,/g, ""));
14798         if(isNaN(val)) {
14799             val = 0;
14800         }
14801         return val;
14802     }
14803 };/*
14804  * Based on:
14805  * Ext JS Library 1.1.1
14806  * Copyright(c) 2006-2007, Ext JS, LLC.
14807  *
14808  * Originally Released Under LGPL - original licence link has changed is not relivant.
14809  *
14810  * Fork - LGPL
14811  * <script type="text/javascript">
14812  */
14813
14814 /**
14815 * @class Roo.data.Record
14816  * Instances of this class encapsulate both record <em>definition</em> information, and record
14817  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14818  * to access Records cached in an {@link Roo.data.Store} object.<br>
14819  * <p>
14820  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14821  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14822  * objects.<br>
14823  * <p>
14824  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14825  * @constructor
14826  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14827  * {@link #create}. The parameters are the same.
14828  * @param {Array} data An associative Array of data values keyed by the field name.
14829  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14830  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14831  * not specified an integer id is generated.
14832  */
14833 Roo.data.Record = function(data, id){
14834     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14835     this.data = data;
14836 };
14837
14838 /**
14839  * Generate a constructor for a specific record layout.
14840  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14841  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14842  * Each field definition object may contain the following properties: <ul>
14843  * <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,
14844  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14845  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14846  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14847  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14848  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14849  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14850  * this may be omitted.</p></li>
14851  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14852  * <ul><li>auto (Default, implies no conversion)</li>
14853  * <li>string</li>
14854  * <li>int</li>
14855  * <li>float</li>
14856  * <li>boolean</li>
14857  * <li>date</li></ul></p></li>
14858  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14859  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14860  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14861  * by the Reader into an object that will be stored in the Record. It is passed the
14862  * following parameters:<ul>
14863  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14864  * </ul></p></li>
14865  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14866  * </ul>
14867  * <br>usage:<br><pre><code>
14868 var TopicRecord = Roo.data.Record.create(
14869     {name: 'title', mapping: 'topic_title'},
14870     {name: 'author', mapping: 'username'},
14871     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14872     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14873     {name: 'lastPoster', mapping: 'user2'},
14874     {name: 'excerpt', mapping: 'post_text'}
14875 );
14876
14877 var myNewRecord = new TopicRecord({
14878     title: 'Do my job please',
14879     author: 'noobie',
14880     totalPosts: 1,
14881     lastPost: new Date(),
14882     lastPoster: 'Animal',
14883     excerpt: 'No way dude!'
14884 });
14885 myStore.add(myNewRecord);
14886 </code></pre>
14887  * @method create
14888  * @static
14889  */
14890 Roo.data.Record.create = function(o){
14891     var f = function(){
14892         f.superclass.constructor.apply(this, arguments);
14893     };
14894     Roo.extend(f, Roo.data.Record);
14895     var p = f.prototype;
14896     p.fields = new Roo.util.MixedCollection(false, function(field){
14897         return field.name;
14898     });
14899     for(var i = 0, len = o.length; i < len; i++){
14900         p.fields.add(new Roo.data.Field(o[i]));
14901     }
14902     f.getField = function(name){
14903         return p.fields.get(name);  
14904     };
14905     return f;
14906 };
14907
14908 Roo.data.Record.AUTO_ID = 1000;
14909 Roo.data.Record.EDIT = 'edit';
14910 Roo.data.Record.REJECT = 'reject';
14911 Roo.data.Record.COMMIT = 'commit';
14912
14913 Roo.data.Record.prototype = {
14914     /**
14915      * Readonly flag - true if this record has been modified.
14916      * @type Boolean
14917      */
14918     dirty : false,
14919     editing : false,
14920     error: null,
14921     modified: null,
14922
14923     // private
14924     join : function(store){
14925         this.store = store;
14926     },
14927
14928     /**
14929      * Set the named field to the specified value.
14930      * @param {String} name The name of the field to set.
14931      * @param {Object} value The value to set the field to.
14932      */
14933     set : function(name, value){
14934         if(this.data[name] == value){
14935             return;
14936         }
14937         this.dirty = true;
14938         if(!this.modified){
14939             this.modified = {};
14940         }
14941         if(typeof this.modified[name] == 'undefined'){
14942             this.modified[name] = this.data[name];
14943         }
14944         this.data[name] = value;
14945         if(!this.editing && this.store){
14946             this.store.afterEdit(this);
14947         }       
14948     },
14949
14950     /**
14951      * Get the value of the named field.
14952      * @param {String} name The name of the field to get the value of.
14953      * @return {Object} The value of the field.
14954      */
14955     get : function(name){
14956         return this.data[name]; 
14957     },
14958
14959     // private
14960     beginEdit : function(){
14961         this.editing = true;
14962         this.modified = {}; 
14963     },
14964
14965     // private
14966     cancelEdit : function(){
14967         this.editing = false;
14968         delete this.modified;
14969     },
14970
14971     // private
14972     endEdit : function(){
14973         this.editing = false;
14974         if(this.dirty && this.store){
14975             this.store.afterEdit(this);
14976         }
14977     },
14978
14979     /**
14980      * Usually called by the {@link Roo.data.Store} which owns the Record.
14981      * Rejects all changes made to the Record since either creation, or the last commit operation.
14982      * Modified fields are reverted to their original values.
14983      * <p>
14984      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14985      * of reject operations.
14986      */
14987     reject : function(){
14988         var m = this.modified;
14989         for(var n in m){
14990             if(typeof m[n] != "function"){
14991                 this.data[n] = m[n];
14992             }
14993         }
14994         this.dirty = false;
14995         delete this.modified;
14996         this.editing = false;
14997         if(this.store){
14998             this.store.afterReject(this);
14999         }
15000     },
15001
15002     /**
15003      * Usually called by the {@link Roo.data.Store} which owns the Record.
15004      * Commits all changes made to the Record since either creation, or the last commit operation.
15005      * <p>
15006      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15007      * of commit operations.
15008      */
15009     commit : function(){
15010         this.dirty = false;
15011         delete this.modified;
15012         this.editing = false;
15013         if(this.store){
15014             this.store.afterCommit(this);
15015         }
15016     },
15017
15018     // private
15019     hasError : function(){
15020         return this.error != null;
15021     },
15022
15023     // private
15024     clearError : function(){
15025         this.error = null;
15026     },
15027
15028     /**
15029      * Creates a copy of this record.
15030      * @param {String} id (optional) A new record id if you don't want to use this record's id
15031      * @return {Record}
15032      */
15033     copy : function(newId) {
15034         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15035     }
15036 };/*
15037  * Based on:
15038  * Ext JS Library 1.1.1
15039  * Copyright(c) 2006-2007, Ext JS, LLC.
15040  *
15041  * Originally Released Under LGPL - original licence link has changed is not relivant.
15042  *
15043  * Fork - LGPL
15044  * <script type="text/javascript">
15045  */
15046
15047
15048
15049 /**
15050  * @class Roo.data.Store
15051  * @extends Roo.util.Observable
15052  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15053  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15054  * <p>
15055  * 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
15056  * has no knowledge of the format of the data returned by the Proxy.<br>
15057  * <p>
15058  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15059  * instances from the data object. These records are cached and made available through accessor functions.
15060  * @constructor
15061  * Creates a new Store.
15062  * @param {Object} config A config object containing the objects needed for the Store to access data,
15063  * and read the data into Records.
15064  */
15065 Roo.data.Store = function(config){
15066     this.data = new Roo.util.MixedCollection(false);
15067     this.data.getKey = function(o){
15068         return o.id;
15069     };
15070     this.baseParams = {};
15071     // private
15072     this.paramNames = {
15073         "start" : "start",
15074         "limit" : "limit",
15075         "sort" : "sort",
15076         "dir" : "dir",
15077         "multisort" : "_multisort"
15078     };
15079
15080     if(config && config.data){
15081         this.inlineData = config.data;
15082         delete config.data;
15083     }
15084
15085     Roo.apply(this, config);
15086     
15087     if(this.reader){ // reader passed
15088         this.reader = Roo.factory(this.reader, Roo.data);
15089         this.reader.xmodule = this.xmodule || false;
15090         if(!this.recordType){
15091             this.recordType = this.reader.recordType;
15092         }
15093         if(this.reader.onMetaChange){
15094             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15095         }
15096     }
15097
15098     if(this.recordType){
15099         this.fields = this.recordType.prototype.fields;
15100     }
15101     this.modified = [];
15102
15103     this.addEvents({
15104         /**
15105          * @event datachanged
15106          * Fires when the data cache has changed, and a widget which is using this Store
15107          * as a Record cache should refresh its view.
15108          * @param {Store} this
15109          */
15110         datachanged : true,
15111         /**
15112          * @event metachange
15113          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15114          * @param {Store} this
15115          * @param {Object} meta The JSON metadata
15116          */
15117         metachange : true,
15118         /**
15119          * @event add
15120          * Fires when Records have been added to the Store
15121          * @param {Store} this
15122          * @param {Roo.data.Record[]} records The array of Records added
15123          * @param {Number} index The index at which the record(s) were added
15124          */
15125         add : true,
15126         /**
15127          * @event remove
15128          * Fires when a Record has been removed from the Store
15129          * @param {Store} this
15130          * @param {Roo.data.Record} record The Record that was removed
15131          * @param {Number} index The index at which the record was removed
15132          */
15133         remove : true,
15134         /**
15135          * @event update
15136          * Fires when a Record has been updated
15137          * @param {Store} this
15138          * @param {Roo.data.Record} record The Record that was updated
15139          * @param {String} operation The update operation being performed.  Value may be one of:
15140          * <pre><code>
15141  Roo.data.Record.EDIT
15142  Roo.data.Record.REJECT
15143  Roo.data.Record.COMMIT
15144          * </code></pre>
15145          */
15146         update : true,
15147         /**
15148          * @event clear
15149          * Fires when the data cache has been cleared.
15150          * @param {Store} this
15151          */
15152         clear : true,
15153         /**
15154          * @event beforeload
15155          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15156          * the load action will be canceled.
15157          * @param {Store} this
15158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15159          */
15160         beforeload : true,
15161         /**
15162          * @event beforeloadadd
15163          * Fires after a new set of Records has been loaded.
15164          * @param {Store} this
15165          * @param {Roo.data.Record[]} records The Records that were loaded
15166          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15167          */
15168         beforeloadadd : true,
15169         /**
15170          * @event load
15171          * Fires after a new set of Records has been loaded, before they are added to the store.
15172          * @param {Store} this
15173          * @param {Roo.data.Record[]} records The Records that were loaded
15174          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15175          * @params {Object} return from reader
15176          */
15177         load : true,
15178         /**
15179          * @event loadexception
15180          * Fires if an exception occurs in the Proxy during loading.
15181          * Called with the signature of the Proxy's "loadexception" event.
15182          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15183          * 
15184          * @param {Proxy} 
15185          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15186          * @param {Object} opts - load Options
15187          * @param {Object} jsonData from your request (normally this contains the Exception)
15188          */
15189         loadexception : true
15190     });
15191     
15192     if(this.proxy){
15193         this.proxy = Roo.factory(this.proxy, Roo.data);
15194         this.proxy.xmodule = this.xmodule || false;
15195         this.relayEvents(this.proxy,  ["loadexception"]);
15196     }
15197     this.sortToggle = {};
15198     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15199
15200     Roo.data.Store.superclass.constructor.call(this);
15201
15202     if(this.inlineData){
15203         this.loadData(this.inlineData);
15204         delete this.inlineData;
15205     }
15206 };
15207
15208 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15209      /**
15210     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15211     * without a remote query - used by combo/forms at present.
15212     */
15213     
15214     /**
15215     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15216     */
15217     /**
15218     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15219     */
15220     /**
15221     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15222     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15223     */
15224     /**
15225     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15226     * on any HTTP request
15227     */
15228     /**
15229     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15230     */
15231     /**
15232     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15233     */
15234     multiSort: false,
15235     /**
15236     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15237     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15238     */
15239     remoteSort : false,
15240
15241     /**
15242     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15243      * loaded or when a record is removed. (defaults to false).
15244     */
15245     pruneModifiedRecords : false,
15246
15247     // private
15248     lastOptions : null,
15249
15250     /**
15251      * Add Records to the Store and fires the add event.
15252      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15253      */
15254     add : function(records){
15255         records = [].concat(records);
15256         for(var i = 0, len = records.length; i < len; i++){
15257             records[i].join(this);
15258         }
15259         var index = this.data.length;
15260         this.data.addAll(records);
15261         this.fireEvent("add", this, records, index);
15262     },
15263
15264     /**
15265      * Remove a Record from the Store and fires the remove event.
15266      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15267      */
15268     remove : function(record){
15269         var index = this.data.indexOf(record);
15270         this.data.removeAt(index);
15271  
15272         if(this.pruneModifiedRecords){
15273             this.modified.remove(record);
15274         }
15275         this.fireEvent("remove", this, record, index);
15276     },
15277
15278     /**
15279      * Remove all Records from the Store and fires the clear event.
15280      */
15281     removeAll : function(){
15282         this.data.clear();
15283         if(this.pruneModifiedRecords){
15284             this.modified = [];
15285         }
15286         this.fireEvent("clear", this);
15287     },
15288
15289     /**
15290      * Inserts Records to the Store at the given index and fires the add event.
15291      * @param {Number} index The start index at which to insert the passed Records.
15292      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15293      */
15294     insert : function(index, records){
15295         records = [].concat(records);
15296         for(var i = 0, len = records.length; i < len; i++){
15297             this.data.insert(index, records[i]);
15298             records[i].join(this);
15299         }
15300         this.fireEvent("add", this, records, index);
15301     },
15302
15303     /**
15304      * Get the index within the cache of the passed Record.
15305      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15306      * @return {Number} The index of the passed Record. Returns -1 if not found.
15307      */
15308     indexOf : function(record){
15309         return this.data.indexOf(record);
15310     },
15311
15312     /**
15313      * Get the index within the cache of the Record with the passed id.
15314      * @param {String} id The id of the Record to find.
15315      * @return {Number} The index of the Record. Returns -1 if not found.
15316      */
15317     indexOfId : function(id){
15318         return this.data.indexOfKey(id);
15319     },
15320
15321     /**
15322      * Get the Record with the specified id.
15323      * @param {String} id The id of the Record to find.
15324      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15325      */
15326     getById : function(id){
15327         return this.data.key(id);
15328     },
15329
15330     /**
15331      * Get the Record at the specified index.
15332      * @param {Number} index The index of the Record to find.
15333      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15334      */
15335     getAt : function(index){
15336         return this.data.itemAt(index);
15337     },
15338
15339     /**
15340      * Returns a range of Records between specified indices.
15341      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15342      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15343      * @return {Roo.data.Record[]} An array of Records
15344      */
15345     getRange : function(start, end){
15346         return this.data.getRange(start, end);
15347     },
15348
15349     // private
15350     storeOptions : function(o){
15351         o = Roo.apply({}, o);
15352         delete o.callback;
15353         delete o.scope;
15354         this.lastOptions = o;
15355     },
15356
15357     /**
15358      * Loads the Record cache from the configured Proxy using the configured Reader.
15359      * <p>
15360      * If using remote paging, then the first load call must specify the <em>start</em>
15361      * and <em>limit</em> properties in the options.params property to establish the initial
15362      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15363      * <p>
15364      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15365      * and this call will return before the new data has been loaded. Perform any post-processing
15366      * in a callback function, or in a "load" event handler.</strong>
15367      * <p>
15368      * @param {Object} options An object containing properties which control loading options:<ul>
15369      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15370      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15371      * <pre>
15372                 {
15373                     data : data,  // array of key=>value data like JsonReader
15374                     total : data.length,
15375                     success : true
15376                     
15377                 }
15378         </pre>
15379             }.</li>
15380      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15381      * passed the following arguments:<ul>
15382      * <li>r : Roo.data.Record[]</li>
15383      * <li>options: Options object from the load call</li>
15384      * <li>success: Boolean success indicator</li></ul></li>
15385      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15386      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15387      * </ul>
15388      */
15389     load : function(options){
15390         options = options || {};
15391         if(this.fireEvent("beforeload", this, options) !== false){
15392             this.storeOptions(options);
15393             var p = Roo.apply(options.params || {}, this.baseParams);
15394             // if meta was not loaded from remote source.. try requesting it.
15395             if (!this.reader.metaFromRemote) {
15396                 p._requestMeta = 1;
15397             }
15398             if(this.sortInfo && this.remoteSort){
15399                 var pn = this.paramNames;
15400                 p[pn["sort"]] = this.sortInfo.field;
15401                 p[pn["dir"]] = this.sortInfo.direction;
15402             }
15403             if (this.multiSort) {
15404                 var pn = this.paramNames;
15405                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15406             }
15407             
15408             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15409         }
15410     },
15411
15412     /**
15413      * Reloads the Record cache from the configured Proxy using the configured Reader and
15414      * the options from the last load operation performed.
15415      * @param {Object} options (optional) An object containing properties which may override the options
15416      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15417      * the most recently used options are reused).
15418      */
15419     reload : function(options){
15420         this.load(Roo.applyIf(options||{}, this.lastOptions));
15421     },
15422
15423     // private
15424     // Called as a callback by the Reader during a load operation.
15425     loadRecords : function(o, options, success){
15426          
15427         if(!o){
15428             if(success !== false){
15429                 this.fireEvent("load", this, [], options, o);
15430             }
15431             if(options.callback){
15432                 options.callback.call(options.scope || this, [], options, false);
15433             }
15434             return;
15435         }
15436         // if data returned failure - throw an exception.
15437         if (o.success === false) {
15438             // show a message if no listener is registered.
15439             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15440                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15441             }
15442             // loadmask wil be hooked into this..
15443             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15444             return;
15445         }
15446         var r = o.records, t = o.totalRecords || r.length;
15447         
15448         this.fireEvent("beforeloadadd", this, r, options, o);
15449         
15450         if(!options || options.add !== true){
15451             if(this.pruneModifiedRecords){
15452                 this.modified = [];
15453             }
15454             for(var i = 0, len = r.length; i < len; i++){
15455                 r[i].join(this);
15456             }
15457             if(this.snapshot){
15458                 this.data = this.snapshot;
15459                 delete this.snapshot;
15460             }
15461             this.data.clear();
15462             this.data.addAll(r);
15463             this.totalLength = t;
15464             this.applySort();
15465             this.fireEvent("datachanged", this);
15466         }else{
15467             this.totalLength = Math.max(t, this.data.length+r.length);
15468             this.add(r);
15469         }
15470         
15471         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15472                 
15473             var e = new Roo.data.Record({});
15474
15475             e.set(this.parent.displayField, this.parent.emptyTitle);
15476             e.set(this.parent.valueField, '');
15477
15478             this.insert(0, e);
15479         }
15480             
15481         this.fireEvent("load", this, r, options, o);
15482         if(options.callback){
15483             options.callback.call(options.scope || this, r, options, true);
15484         }
15485     },
15486
15487
15488     /**
15489      * Loads data from a passed data block. A Reader which understands the format of the data
15490      * must have been configured in the constructor.
15491      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15492      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15493      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15494      */
15495     loadData : function(o, append){
15496         var r = this.reader.readRecords(o);
15497         this.loadRecords(r, {add: append}, true);
15498     },
15499     
15500      /**
15501      * using 'cn' the nested child reader read the child array into it's child stores.
15502      * @param {Object} rec The record with a 'children array
15503      */
15504     loadDataFromChildren : function(rec)
15505     {
15506         this.loadData(this.reader.toLoadData(rec));
15507     },
15508     
15509
15510     /**
15511      * Gets the number of cached records.
15512      * <p>
15513      * <em>If using paging, this may not be the total size of the dataset. If the data object
15514      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15515      * the data set size</em>
15516      */
15517     getCount : function(){
15518         return this.data.length || 0;
15519     },
15520
15521     /**
15522      * Gets the total number of records in the dataset as returned by the server.
15523      * <p>
15524      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15525      * the dataset size</em>
15526      */
15527     getTotalCount : function(){
15528         return this.totalLength || 0;
15529     },
15530
15531     /**
15532      * Returns the sort state of the Store as an object with two properties:
15533      * <pre><code>
15534  field {String} The name of the field by which the Records are sorted
15535  direction {String} The sort order, "ASC" or "DESC"
15536      * </code></pre>
15537      */
15538     getSortState : function(){
15539         return this.sortInfo;
15540     },
15541
15542     // private
15543     applySort : function(){
15544         if(this.sortInfo && !this.remoteSort){
15545             var s = this.sortInfo, f = s.field;
15546             var st = this.fields.get(f).sortType;
15547             var fn = function(r1, r2){
15548                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15549                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15550             };
15551             this.data.sort(s.direction, fn);
15552             if(this.snapshot && this.snapshot != this.data){
15553                 this.snapshot.sort(s.direction, fn);
15554             }
15555         }
15556     },
15557
15558     /**
15559      * Sets the default sort column and order to be used by the next load operation.
15560      * @param {String} fieldName The name of the field to sort by.
15561      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15562      */
15563     setDefaultSort : function(field, dir){
15564         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15565     },
15566
15567     /**
15568      * Sort the Records.
15569      * If remote sorting is used, the sort is performed on the server, and the cache is
15570      * reloaded. If local sorting is used, the cache is sorted internally.
15571      * @param {String} fieldName The name of the field to sort by.
15572      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15573      */
15574     sort : function(fieldName, dir){
15575         var f = this.fields.get(fieldName);
15576         if(!dir){
15577             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15578             
15579             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15580                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15581             }else{
15582                 dir = f.sortDir;
15583             }
15584         }
15585         this.sortToggle[f.name] = dir;
15586         this.sortInfo = {field: f.name, direction: dir};
15587         if(!this.remoteSort){
15588             this.applySort();
15589             this.fireEvent("datachanged", this);
15590         }else{
15591             this.load(this.lastOptions);
15592         }
15593     },
15594
15595     /**
15596      * Calls the specified function for each of the Records in the cache.
15597      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15598      * Returning <em>false</em> aborts and exits the iteration.
15599      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15600      */
15601     each : function(fn, scope){
15602         this.data.each(fn, scope);
15603     },
15604
15605     /**
15606      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15607      * (e.g., during paging).
15608      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15609      */
15610     getModifiedRecords : function(){
15611         return this.modified;
15612     },
15613
15614     // private
15615     createFilterFn : function(property, value, anyMatch){
15616         if(!value.exec){ // not a regex
15617             value = String(value);
15618             if(value.length == 0){
15619                 return false;
15620             }
15621             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15622         }
15623         return function(r){
15624             return value.test(r.data[property]);
15625         };
15626     },
15627
15628     /**
15629      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15630      * @param {String} property A field on your records
15631      * @param {Number} start The record index to start at (defaults to 0)
15632      * @param {Number} end The last record index to include (defaults to length - 1)
15633      * @return {Number} The sum
15634      */
15635     sum : function(property, start, end){
15636         var rs = this.data.items, v = 0;
15637         start = start || 0;
15638         end = (end || end === 0) ? end : rs.length-1;
15639
15640         for(var i = start; i <= end; i++){
15641             v += (rs[i].data[property] || 0);
15642         }
15643         return v;
15644     },
15645
15646     /**
15647      * Filter the records by a specified property.
15648      * @param {String} field A field on your records
15649      * @param {String/RegExp} value Either a string that the field
15650      * should start with or a RegExp to test against the field
15651      * @param {Boolean} anyMatch True to match any part not just the beginning
15652      */
15653     filter : function(property, value, anyMatch){
15654         var fn = this.createFilterFn(property, value, anyMatch);
15655         return fn ? this.filterBy(fn) : this.clearFilter();
15656     },
15657
15658     /**
15659      * Filter by a function. The specified function will be called with each
15660      * record in this data source. If the function returns true the record is included,
15661      * otherwise it is filtered.
15662      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15663      * @param {Object} scope (optional) The scope of the function (defaults to this)
15664      */
15665     filterBy : function(fn, scope){
15666         this.snapshot = this.snapshot || this.data;
15667         this.data = this.queryBy(fn, scope||this);
15668         this.fireEvent("datachanged", this);
15669     },
15670
15671     /**
15672      * Query the records by a specified property.
15673      * @param {String} field A field on your records
15674      * @param {String/RegExp} value Either a string that the field
15675      * should start with or a RegExp to test against the field
15676      * @param {Boolean} anyMatch True to match any part not just the beginning
15677      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15678      */
15679     query : function(property, value, anyMatch){
15680         var fn = this.createFilterFn(property, value, anyMatch);
15681         return fn ? this.queryBy(fn) : this.data.clone();
15682     },
15683
15684     /**
15685      * Query by a function. The specified function will be called with each
15686      * record in this data source. If the function returns true the record is included
15687      * in the results.
15688      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15689      * @param {Object} scope (optional) The scope of the function (defaults to this)
15690       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15691      **/
15692     queryBy : function(fn, scope){
15693         var data = this.snapshot || this.data;
15694         return data.filterBy(fn, scope||this);
15695     },
15696
15697     /**
15698      * Collects unique values for a particular dataIndex from this store.
15699      * @param {String} dataIndex The property to collect
15700      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15701      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15702      * @return {Array} An array of the unique values
15703      **/
15704     collect : function(dataIndex, allowNull, bypassFilter){
15705         var d = (bypassFilter === true && this.snapshot) ?
15706                 this.snapshot.items : this.data.items;
15707         var v, sv, r = [], l = {};
15708         for(var i = 0, len = d.length; i < len; i++){
15709             v = d[i].data[dataIndex];
15710             sv = String(v);
15711             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15712                 l[sv] = true;
15713                 r[r.length] = v;
15714             }
15715         }
15716         return r;
15717     },
15718
15719     /**
15720      * Revert to a view of the Record cache with no filtering applied.
15721      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15722      */
15723     clearFilter : function(suppressEvent){
15724         if(this.snapshot && this.snapshot != this.data){
15725             this.data = this.snapshot;
15726             delete this.snapshot;
15727             if(suppressEvent !== true){
15728                 this.fireEvent("datachanged", this);
15729             }
15730         }
15731     },
15732
15733     // private
15734     afterEdit : function(record){
15735         if(this.modified.indexOf(record) == -1){
15736             this.modified.push(record);
15737         }
15738         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15739     },
15740     
15741     // private
15742     afterReject : function(record){
15743         this.modified.remove(record);
15744         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15745     },
15746
15747     // private
15748     afterCommit : function(record){
15749         this.modified.remove(record);
15750         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15751     },
15752
15753     /**
15754      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15755      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15756      */
15757     commitChanges : function(){
15758         var m = this.modified.slice(0);
15759         this.modified = [];
15760         for(var i = 0, len = m.length; i < len; i++){
15761             m[i].commit();
15762         }
15763     },
15764
15765     /**
15766      * Cancel outstanding changes on all changed records.
15767      */
15768     rejectChanges : function(){
15769         var m = this.modified.slice(0);
15770         this.modified = [];
15771         for(var i = 0, len = m.length; i < len; i++){
15772             m[i].reject();
15773         }
15774     },
15775
15776     onMetaChange : function(meta, rtype, o){
15777         this.recordType = rtype;
15778         this.fields = rtype.prototype.fields;
15779         delete this.snapshot;
15780         this.sortInfo = meta.sortInfo || this.sortInfo;
15781         this.modified = [];
15782         this.fireEvent('metachange', this, this.reader.meta);
15783     },
15784     
15785     moveIndex : function(data, type)
15786     {
15787         var index = this.indexOf(data);
15788         
15789         var newIndex = index + type;
15790         
15791         this.remove(data);
15792         
15793         this.insert(newIndex, data);
15794         
15795     }
15796 });/*
15797  * Based on:
15798  * Ext JS Library 1.1.1
15799  * Copyright(c) 2006-2007, Ext JS, LLC.
15800  *
15801  * Originally Released Under LGPL - original licence link has changed is not relivant.
15802  *
15803  * Fork - LGPL
15804  * <script type="text/javascript">
15805  */
15806
15807 /**
15808  * @class Roo.data.SimpleStore
15809  * @extends Roo.data.Store
15810  * Small helper class to make creating Stores from Array data easier.
15811  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15812  * @cfg {Array} fields An array of field definition objects, or field name strings.
15813  * @cfg {Object} an existing reader (eg. copied from another store)
15814  * @cfg {Array} data The multi-dimensional array of data
15815  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15816  * @cfg {Roo.data.Reader} reader  [not-required] 
15817  * @constructor
15818  * @param {Object} config
15819  */
15820 Roo.data.SimpleStore = function(config)
15821 {
15822     Roo.data.SimpleStore.superclass.constructor.call(this, {
15823         isLocal : true,
15824         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15825                 id: config.id
15826             },
15827             Roo.data.Record.create(config.fields)
15828         ),
15829         proxy : new Roo.data.MemoryProxy(config.data)
15830     });
15831     this.load();
15832 };
15833 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15834  * Based on:
15835  * Ext JS Library 1.1.1
15836  * Copyright(c) 2006-2007, Ext JS, LLC.
15837  *
15838  * Originally Released Under LGPL - original licence link has changed is not relivant.
15839  *
15840  * Fork - LGPL
15841  * <script type="text/javascript">
15842  */
15843
15844 /**
15845 /**
15846  * @extends Roo.data.Store
15847  * @class Roo.data.JsonStore
15848  * Small helper class to make creating Stores for JSON data easier. <br/>
15849 <pre><code>
15850 var store = new Roo.data.JsonStore({
15851     url: 'get-images.php',
15852     root: 'images',
15853     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15854 });
15855 </code></pre>
15856  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15857  * JsonReader and HttpProxy (unless inline data is provided).</b>
15858  * @cfg {Array} fields An array of field definition objects, or field name strings.
15859  * @constructor
15860  * @param {Object} config
15861  */
15862 Roo.data.JsonStore = function(c){
15863     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15864         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15865         reader: new Roo.data.JsonReader(c, c.fields)
15866     }));
15867 };
15868 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15869  * Based on:
15870  * Ext JS Library 1.1.1
15871  * Copyright(c) 2006-2007, Ext JS, LLC.
15872  *
15873  * Originally Released Under LGPL - original licence link has changed is not relivant.
15874  *
15875  * Fork - LGPL
15876  * <script type="text/javascript">
15877  */
15878
15879  
15880 Roo.data.Field = function(config){
15881     if(typeof config == "string"){
15882         config = {name: config};
15883     }
15884     Roo.apply(this, config);
15885     
15886     if(!this.type){
15887         this.type = "auto";
15888     }
15889     
15890     var st = Roo.data.SortTypes;
15891     // named sortTypes are supported, here we look them up
15892     if(typeof this.sortType == "string"){
15893         this.sortType = st[this.sortType];
15894     }
15895     
15896     // set default sortType for strings and dates
15897     if(!this.sortType){
15898         switch(this.type){
15899             case "string":
15900                 this.sortType = st.asUCString;
15901                 break;
15902             case "date":
15903                 this.sortType = st.asDate;
15904                 break;
15905             default:
15906                 this.sortType = st.none;
15907         }
15908     }
15909
15910     // define once
15911     var stripRe = /[\$,%]/g;
15912
15913     // prebuilt conversion function for this field, instead of
15914     // switching every time we're reading a value
15915     if(!this.convert){
15916         var cv, dateFormat = this.dateFormat;
15917         switch(this.type){
15918             case "":
15919             case "auto":
15920             case undefined:
15921                 cv = function(v){ return v; };
15922                 break;
15923             case "string":
15924                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15925                 break;
15926             case "int":
15927                 cv = function(v){
15928                     return v !== undefined && v !== null && v !== '' ?
15929                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15930                     };
15931                 break;
15932             case "float":
15933                 cv = function(v){
15934                     return v !== undefined && v !== null && v !== '' ?
15935                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15936                     };
15937                 break;
15938             case "bool":
15939             case "boolean":
15940                 cv = function(v){ return v === true || v === "true" || v == 1; };
15941                 break;
15942             case "date":
15943                 cv = function(v){
15944                     if(!v){
15945                         return '';
15946                     }
15947                     if(v instanceof Date){
15948                         return v;
15949                     }
15950                     if(dateFormat){
15951                         if(dateFormat == "timestamp"){
15952                             return new Date(v*1000);
15953                         }
15954                         return Date.parseDate(v, dateFormat);
15955                     }
15956                     var parsed = Date.parse(v);
15957                     return parsed ? new Date(parsed) : null;
15958                 };
15959              break;
15960             
15961         }
15962         this.convert = cv;
15963     }
15964 };
15965
15966 Roo.data.Field.prototype = {
15967     dateFormat: null,
15968     defaultValue: "",
15969     mapping: null,
15970     sortType : null,
15971     sortDir : "ASC"
15972 };/*
15973  * Based on:
15974  * Ext JS Library 1.1.1
15975  * Copyright(c) 2006-2007, Ext JS, LLC.
15976  *
15977  * Originally Released Under LGPL - original licence link has changed is not relivant.
15978  *
15979  * Fork - LGPL
15980  * <script type="text/javascript">
15981  */
15982  
15983 // Base class for reading structured data from a data source.  This class is intended to be
15984 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15985
15986 /**
15987  * @class Roo.data.DataReader
15988  * @abstract
15989  * Base class for reading structured data from a data source.  This class is intended to be
15990  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15991  */
15992
15993 Roo.data.DataReader = function(meta, recordType){
15994     
15995     this.meta = meta;
15996     
15997     this.recordType = recordType instanceof Array ? 
15998         Roo.data.Record.create(recordType) : recordType;
15999 };
16000
16001 Roo.data.DataReader.prototype = {
16002     
16003     
16004     readerType : 'Data',
16005      /**
16006      * Create an empty record
16007      * @param {Object} data (optional) - overlay some values
16008      * @return {Roo.data.Record} record created.
16009      */
16010     newRow :  function(d) {
16011         var da =  {};
16012         this.recordType.prototype.fields.each(function(c) {
16013             switch( c.type) {
16014                 case 'int' : da[c.name] = 0; break;
16015                 case 'date' : da[c.name] = new Date(); break;
16016                 case 'float' : da[c.name] = 0.0; break;
16017                 case 'boolean' : da[c.name] = false; break;
16018                 default : da[c.name] = ""; break;
16019             }
16020             
16021         });
16022         return new this.recordType(Roo.apply(da, d));
16023     }
16024     
16025     
16026 };/*
16027  * Based on:
16028  * Ext JS Library 1.1.1
16029  * Copyright(c) 2006-2007, Ext JS, LLC.
16030  *
16031  * Originally Released Under LGPL - original licence link has changed is not relivant.
16032  *
16033  * Fork - LGPL
16034  * <script type="text/javascript">
16035  */
16036
16037 /**
16038  * @class Roo.data.DataProxy
16039  * @extends Roo.util.Observable
16040  * @abstract
16041  * This class is an abstract base class for implementations which provide retrieval of
16042  * unformatted data objects.<br>
16043  * <p>
16044  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16045  * (of the appropriate type which knows how to parse the data object) to provide a block of
16046  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16047  * <p>
16048  * Custom implementations must implement the load method as described in
16049  * {@link Roo.data.HttpProxy#load}.
16050  */
16051 Roo.data.DataProxy = function(){
16052     this.addEvents({
16053         /**
16054          * @event beforeload
16055          * Fires before a network request is made to retrieve a data object.
16056          * @param {Object} This DataProxy object.
16057          * @param {Object} params The params parameter to the load function.
16058          */
16059         beforeload : true,
16060         /**
16061          * @event load
16062          * Fires before the load method's callback is called.
16063          * @param {Object} This DataProxy object.
16064          * @param {Object} o The data object.
16065          * @param {Object} arg The callback argument object passed to the load function.
16066          */
16067         load : true,
16068         /**
16069          * @event loadexception
16070          * Fires if an Exception occurs during data retrieval.
16071          * @param {Object} This DataProxy object.
16072          * @param {Object} o The data object.
16073          * @param {Object} arg The callback argument object passed to the load function.
16074          * @param {Object} e The Exception.
16075          */
16076         loadexception : true
16077     });
16078     Roo.data.DataProxy.superclass.constructor.call(this);
16079 };
16080
16081 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16082
16083     /**
16084      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16085      */
16086 /*
16087  * Based on:
16088  * Ext JS Library 1.1.1
16089  * Copyright(c) 2006-2007, Ext JS, LLC.
16090  *
16091  * Originally Released Under LGPL - original licence link has changed is not relivant.
16092  *
16093  * Fork - LGPL
16094  * <script type="text/javascript">
16095  */
16096 /**
16097  * @class Roo.data.MemoryProxy
16098  * @extends Roo.data.DataProxy
16099  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16100  * to the Reader when its load method is called.
16101  * @constructor
16102  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16103  */
16104 Roo.data.MemoryProxy = function(config){
16105     var data = config;
16106     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16107         data = config.data;
16108     }
16109     Roo.data.MemoryProxy.superclass.constructor.call(this);
16110     this.data = data;
16111 };
16112
16113 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16114     
16115     /**
16116      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16117      */
16118     /**
16119      * Load data from the requested source (in this case an in-memory
16120      * data object passed to the constructor), read the data object into
16121      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16122      * process that block using the passed callback.
16123      * @param {Object} params This parameter is not used by the MemoryProxy class.
16124      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16125      * object into a block of Roo.data.Records.
16126      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16127      * The function must be passed <ul>
16128      * <li>The Record block object</li>
16129      * <li>The "arg" argument from the load function</li>
16130      * <li>A boolean success indicator</li>
16131      * </ul>
16132      * @param {Object} scope The scope in which to call the callback
16133      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16134      */
16135     load : function(params, reader, callback, scope, arg){
16136         params = params || {};
16137         var result;
16138         try {
16139             result = reader.readRecords(params.data ? params.data :this.data);
16140         }catch(e){
16141             this.fireEvent("loadexception", this, arg, null, e);
16142             callback.call(scope, null, arg, false);
16143             return;
16144         }
16145         callback.call(scope, result, arg, true);
16146     },
16147     
16148     // private
16149     update : function(params, records){
16150         
16151     }
16152 });/*
16153  * Based on:
16154  * Ext JS Library 1.1.1
16155  * Copyright(c) 2006-2007, Ext JS, LLC.
16156  *
16157  * Originally Released Under LGPL - original licence link has changed is not relivant.
16158  *
16159  * Fork - LGPL
16160  * <script type="text/javascript">
16161  */
16162 /**
16163  * @class Roo.data.HttpProxy
16164  * @extends Roo.data.DataProxy
16165  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16166  * configured to reference a certain URL.<br><br>
16167  * <p>
16168  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16169  * from which the running page was served.<br><br>
16170  * <p>
16171  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16172  * <p>
16173  * Be aware that to enable the browser to parse an XML document, the server must set
16174  * the Content-Type header in the HTTP response to "text/xml".
16175  * @constructor
16176  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16177  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16178  * will be used to make the request.
16179  */
16180 Roo.data.HttpProxy = function(conn){
16181     Roo.data.HttpProxy.superclass.constructor.call(this);
16182     // is conn a conn config or a real conn?
16183     this.conn = conn;
16184     this.useAjax = !conn || !conn.events;
16185   
16186 };
16187
16188 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16189     // thse are take from connection...
16190     
16191     /**
16192      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16193      */
16194     /**
16195      * @cfg {Object} extraParams  An object containing properties which are used as
16196      * extra parameters to each request made by this object. (defaults to undefined)
16197      */
16198     /**
16199      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16200      *  to each request made by this object. (defaults to undefined)
16201      */
16202     /**
16203      * @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)
16204      */
16205     /**
16206      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16207      */
16208      /**
16209      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16210      * @type Boolean
16211      */
16212   
16213
16214     /**
16215      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16216      * @type Boolean
16217      */
16218     /**
16219      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16220      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16221      * a finer-grained basis than the DataProxy events.
16222      */
16223     getConnection : function(){
16224         return this.useAjax ? Roo.Ajax : this.conn;
16225     },
16226
16227     /**
16228      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16229      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16230      * process that block using the passed callback.
16231      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16232      * for the request to the remote server.
16233      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16234      * object into a block of Roo.data.Records.
16235      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16236      * The function must be passed <ul>
16237      * <li>The Record block object</li>
16238      * <li>The "arg" argument from the load function</li>
16239      * <li>A boolean success indicator</li>
16240      * </ul>
16241      * @param {Object} scope The scope in which to call the callback
16242      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16243      */
16244     load : function(params, reader, callback, scope, arg){
16245         if(this.fireEvent("beforeload", this, params) !== false){
16246             var  o = {
16247                 params : params || {},
16248                 request: {
16249                     callback : callback,
16250                     scope : scope,
16251                     arg : arg
16252                 },
16253                 reader: reader,
16254                 callback : this.loadResponse,
16255                 scope: this
16256             };
16257             if(this.useAjax){
16258                 Roo.applyIf(o, this.conn);
16259                 if(this.activeRequest){
16260                     Roo.Ajax.abort(this.activeRequest);
16261                 }
16262                 this.activeRequest = Roo.Ajax.request(o);
16263             }else{
16264                 this.conn.request(o);
16265             }
16266         }else{
16267             callback.call(scope||this, null, arg, false);
16268         }
16269     },
16270
16271     // private
16272     loadResponse : function(o, success, response){
16273         delete this.activeRequest;
16274         if(!success){
16275             this.fireEvent("loadexception", this, o, response);
16276             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16277             return;
16278         }
16279         var result;
16280         try {
16281             result = o.reader.read(response);
16282         }catch(e){
16283             o.success = false;
16284             o.raw = { errorMsg : response.responseText };
16285             this.fireEvent("loadexception", this, o, response, e);
16286             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16287             return;
16288         }
16289         
16290         this.fireEvent("load", this, o, o.request.arg);
16291         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16292     },
16293
16294     // private
16295     update : function(dataSet){
16296
16297     },
16298
16299     // private
16300     updateResponse : function(dataSet){
16301
16302     }
16303 });/*
16304  * Based on:
16305  * Ext JS Library 1.1.1
16306  * Copyright(c) 2006-2007, Ext JS, LLC.
16307  *
16308  * Originally Released Under LGPL - original licence link has changed is not relivant.
16309  *
16310  * Fork - LGPL
16311  * <script type="text/javascript">
16312  */
16313
16314 /**
16315  * @class Roo.data.ScriptTagProxy
16316  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16317  * other than the originating domain of the running page.<br><br>
16318  * <p>
16319  * <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
16320  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16321  * <p>
16322  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16323  * source code that is used as the source inside a &lt;script> tag.<br><br>
16324  * <p>
16325  * In order for the browser to process the returned data, the server must wrap the data object
16326  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16327  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16328  * depending on whether the callback name was passed:
16329  * <p>
16330  * <pre><code>
16331 boolean scriptTag = false;
16332 String cb = request.getParameter("callback");
16333 if (cb != null) {
16334     scriptTag = true;
16335     response.setContentType("text/javascript");
16336 } else {
16337     response.setContentType("application/x-json");
16338 }
16339 Writer out = response.getWriter();
16340 if (scriptTag) {
16341     out.write(cb + "(");
16342 }
16343 out.print(dataBlock.toJsonString());
16344 if (scriptTag) {
16345     out.write(");");
16346 }
16347 </pre></code>
16348  *
16349  * @constructor
16350  * @param {Object} config A configuration object.
16351  */
16352 Roo.data.ScriptTagProxy = function(config){
16353     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16354     Roo.apply(this, config);
16355     this.head = document.getElementsByTagName("head")[0];
16356 };
16357
16358 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16359
16360 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16361     /**
16362      * @cfg {String} url The URL from which to request the data object.
16363      */
16364     /**
16365      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16366      */
16367     timeout : 30000,
16368     /**
16369      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16370      * the server the name of the callback function set up by the load call to process the returned data object.
16371      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16372      * javascript output which calls this named function passing the data object as its only parameter.
16373      */
16374     callbackParam : "callback",
16375     /**
16376      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16377      * name to the request.
16378      */
16379     nocache : true,
16380
16381     /**
16382      * Load data from the configured URL, read the data object into
16383      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16384      * process that block using the passed callback.
16385      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16386      * for the request to the remote server.
16387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16388      * object into a block of Roo.data.Records.
16389      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16390      * The function must be passed <ul>
16391      * <li>The Record block object</li>
16392      * <li>The "arg" argument from the load function</li>
16393      * <li>A boolean success indicator</li>
16394      * </ul>
16395      * @param {Object} scope The scope in which to call the callback
16396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16397      */
16398     load : function(params, reader, callback, scope, arg){
16399         if(this.fireEvent("beforeload", this, params) !== false){
16400
16401             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16402
16403             var url = this.url;
16404             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16405             if(this.nocache){
16406                 url += "&_dc=" + (new Date().getTime());
16407             }
16408             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16409             var trans = {
16410                 id : transId,
16411                 cb : "stcCallback"+transId,
16412                 scriptId : "stcScript"+transId,
16413                 params : params,
16414                 arg : arg,
16415                 url : url,
16416                 callback : callback,
16417                 scope : scope,
16418                 reader : reader
16419             };
16420             var conn = this;
16421
16422             window[trans.cb] = function(o){
16423                 conn.handleResponse(o, trans);
16424             };
16425
16426             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16427
16428             if(this.autoAbort !== false){
16429                 this.abort();
16430             }
16431
16432             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16433
16434             var script = document.createElement("script");
16435             script.setAttribute("src", url);
16436             script.setAttribute("type", "text/javascript");
16437             script.setAttribute("id", trans.scriptId);
16438             this.head.appendChild(script);
16439
16440             this.trans = trans;
16441         }else{
16442             callback.call(scope||this, null, arg, false);
16443         }
16444     },
16445
16446     // private
16447     isLoading : function(){
16448         return this.trans ? true : false;
16449     },
16450
16451     /**
16452      * Abort the current server request.
16453      */
16454     abort : function(){
16455         if(this.isLoading()){
16456             this.destroyTrans(this.trans);
16457         }
16458     },
16459
16460     // private
16461     destroyTrans : function(trans, isLoaded){
16462         this.head.removeChild(document.getElementById(trans.scriptId));
16463         clearTimeout(trans.timeoutId);
16464         if(isLoaded){
16465             window[trans.cb] = undefined;
16466             try{
16467                 delete window[trans.cb];
16468             }catch(e){}
16469         }else{
16470             // if hasn't been loaded, wait for load to remove it to prevent script error
16471             window[trans.cb] = function(){
16472                 window[trans.cb] = undefined;
16473                 try{
16474                     delete window[trans.cb];
16475                 }catch(e){}
16476             };
16477         }
16478     },
16479
16480     // private
16481     handleResponse : function(o, trans){
16482         this.trans = false;
16483         this.destroyTrans(trans, true);
16484         var result;
16485         try {
16486             result = trans.reader.readRecords(o);
16487         }catch(e){
16488             this.fireEvent("loadexception", this, o, trans.arg, e);
16489             trans.callback.call(trans.scope||window, null, trans.arg, false);
16490             return;
16491         }
16492         this.fireEvent("load", this, o, trans.arg);
16493         trans.callback.call(trans.scope||window, result, trans.arg, true);
16494     },
16495
16496     // private
16497     handleFailure : function(trans){
16498         this.trans = false;
16499         this.destroyTrans(trans, false);
16500         this.fireEvent("loadexception", this, null, trans.arg);
16501         trans.callback.call(trans.scope||window, null, trans.arg, false);
16502     }
16503 });/*
16504  * Based on:
16505  * Ext JS Library 1.1.1
16506  * Copyright(c) 2006-2007, Ext JS, LLC.
16507  *
16508  * Originally Released Under LGPL - original licence link has changed is not relivant.
16509  *
16510  * Fork - LGPL
16511  * <script type="text/javascript">
16512  */
16513
16514 /**
16515  * @class Roo.data.JsonReader
16516  * @extends Roo.data.DataReader
16517  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16518  * based on mappings in a provided Roo.data.Record constructor.
16519  * 
16520  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16521  * in the reply previously. 
16522  * 
16523  * <p>
16524  * Example code:
16525  * <pre><code>
16526 var RecordDef = Roo.data.Record.create([
16527     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16528     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16529 ]);
16530 var myReader = new Roo.data.JsonReader({
16531     totalProperty: "results",    // The property which contains the total dataset size (optional)
16532     root: "rows",                // The property which contains an Array of row objects
16533     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16534 }, RecordDef);
16535 </code></pre>
16536  * <p>
16537  * This would consume a JSON file like this:
16538  * <pre><code>
16539 { 'results': 2, 'rows': [
16540     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16541     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16542 }
16543 </code></pre>
16544  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16545  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16546  * paged from the remote server.
16547  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16548  * @cfg {String} root name of the property which contains the Array of row objects.
16549  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16550  * @cfg {Array} fields Array of field definition objects
16551  * @constructor
16552  * Create a new JsonReader
16553  * @param {Object} meta Metadata configuration options
16554  * @param {Object} recordType Either an Array of field definition objects,
16555  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16556  */
16557 Roo.data.JsonReader = function(meta, recordType){
16558     
16559     meta = meta || {};
16560     // set some defaults:
16561     Roo.applyIf(meta, {
16562         totalProperty: 'total',
16563         successProperty : 'success',
16564         root : 'data',
16565         id : 'id'
16566     });
16567     
16568     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16569 };
16570 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16571     
16572     readerType : 'Json',
16573     
16574     /**
16575      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16576      * Used by Store query builder to append _requestMeta to params.
16577      * 
16578      */
16579     metaFromRemote : false,
16580     /**
16581      * This method is only used by a DataProxy which has retrieved data from a remote server.
16582      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16583      * @return {Object} data A data block which is used by an Roo.data.Store object as
16584      * a cache of Roo.data.Records.
16585      */
16586     read : function(response){
16587         var json = response.responseText;
16588        
16589         var o = /* eval:var:o */ eval("("+json+")");
16590         if(!o) {
16591             throw {message: "JsonReader.read: Json object not found"};
16592         }
16593         
16594         if(o.metaData){
16595             
16596             delete this.ef;
16597             this.metaFromRemote = true;
16598             this.meta = o.metaData;
16599             this.recordType = Roo.data.Record.create(o.metaData.fields);
16600             this.onMetaChange(this.meta, this.recordType, o);
16601         }
16602         return this.readRecords(o);
16603     },
16604
16605     // private function a store will implement
16606     onMetaChange : function(meta, recordType, o){
16607
16608     },
16609
16610     /**
16611          * @ignore
16612          */
16613     simpleAccess: function(obj, subsc) {
16614         return obj[subsc];
16615     },
16616
16617         /**
16618          * @ignore
16619          */
16620     getJsonAccessor: function(){
16621         var re = /[\[\.]/;
16622         return function(expr) {
16623             try {
16624                 return(re.test(expr))
16625                     ? new Function("obj", "return obj." + expr)
16626                     : function(obj){
16627                         return obj[expr];
16628                     };
16629             } catch(e){}
16630             return Roo.emptyFn;
16631         };
16632     }(),
16633
16634     /**
16635      * Create a data block containing Roo.data.Records from an XML document.
16636      * @param {Object} o An object which contains an Array of row objects in the property specified
16637      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16638      * which contains the total size of the dataset.
16639      * @return {Object} data A data block which is used by an Roo.data.Store object as
16640      * a cache of Roo.data.Records.
16641      */
16642     readRecords : function(o){
16643         /**
16644          * After any data loads, the raw JSON data is available for further custom processing.
16645          * @type Object
16646          */
16647         this.o = o;
16648         var s = this.meta, Record = this.recordType,
16649             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16650
16651 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16652         if (!this.ef) {
16653             if(s.totalProperty) {
16654                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16655                 }
16656                 if(s.successProperty) {
16657                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16658                 }
16659                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16660                 if (s.id) {
16661                         var g = this.getJsonAccessor(s.id);
16662                         this.getId = function(rec) {
16663                                 var r = g(rec);  
16664                                 return (r === undefined || r === "") ? null : r;
16665                         };
16666                 } else {
16667                         this.getId = function(){return null;};
16668                 }
16669             this.ef = [];
16670             for(var jj = 0; jj < fl; jj++){
16671                 f = fi[jj];
16672                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16673                 this.ef[jj] = this.getJsonAccessor(map);
16674             }
16675         }
16676
16677         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16678         if(s.totalProperty){
16679             var vt = parseInt(this.getTotal(o), 10);
16680             if(!isNaN(vt)){
16681                 totalRecords = vt;
16682             }
16683         }
16684         if(s.successProperty){
16685             var vs = this.getSuccess(o);
16686             if(vs === false || vs === 'false'){
16687                 success = false;
16688             }
16689         }
16690         var records = [];
16691         for(var i = 0; i < c; i++){
16692             var n = root[i];
16693             var values = {};
16694             var id = this.getId(n);
16695             for(var j = 0; j < fl; j++){
16696                 f = fi[j];
16697                                 var v = this.ef[j](n);
16698                                 if (!f.convert) {
16699                                         Roo.log('missing convert for ' + f.name);
16700                                         Roo.log(f);
16701                                         continue;
16702                                 }
16703                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16704             }
16705                         if (!Record) {
16706                                 return {
16707                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16708                                         success : false,
16709                                         records : [],
16710                                         totalRecords : 0
16711                                 };
16712                         }
16713             var record = new Record(values, id);
16714             record.json = n;
16715             records[i] = record;
16716         }
16717         return {
16718             raw : o,
16719             success : success,
16720             records : records,
16721             totalRecords : totalRecords
16722         };
16723     },
16724     // used when loading children.. @see loadDataFromChildren
16725     toLoadData: function(rec)
16726     {
16727         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16728         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16729         return { data : data, total : data.length };
16730         
16731     }
16732 });/*
16733  * Based on:
16734  * Ext JS Library 1.1.1
16735  * Copyright(c) 2006-2007, Ext JS, LLC.
16736  *
16737  * Originally Released Under LGPL - original licence link has changed is not relivant.
16738  *
16739  * Fork - LGPL
16740  * <script type="text/javascript">
16741  */
16742
16743 /**
16744  * @class Roo.data.ArrayReader
16745  * @extends Roo.data.DataReader
16746  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16747  * Each element of that Array represents a row of data fields. The
16748  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16749  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16750  * <p>
16751  * Example code:.
16752  * <pre><code>
16753 var RecordDef = Roo.data.Record.create([
16754     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16755     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16756 ]);
16757 var myReader = new Roo.data.ArrayReader({
16758     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16759 }, RecordDef);
16760 </code></pre>
16761  * <p>
16762  * This would consume an Array like this:
16763  * <pre><code>
16764 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16765   </code></pre>
16766  
16767  * @constructor
16768  * Create a new JsonReader
16769  * @param {Object} meta Metadata configuration options.
16770  * @param {Object|Array} recordType Either an Array of field definition objects
16771  * 
16772  * @cfg {Array} fields Array of field definition objects
16773  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16774  * as specified to {@link Roo.data.Record#create},
16775  * or an {@link Roo.data.Record} object
16776  *
16777  * 
16778  * created using {@link Roo.data.Record#create}.
16779  */
16780 Roo.data.ArrayReader = function(meta, recordType)
16781 {    
16782     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16783 };
16784
16785 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16786     
16787       /**
16788      * Create a data block containing Roo.data.Records from an XML document.
16789      * @param {Object} o An Array of row objects which represents the dataset.
16790      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16791      * a cache of Roo.data.Records.
16792      */
16793     readRecords : function(o)
16794     {
16795         var sid = this.meta ? this.meta.id : null;
16796         var recordType = this.recordType, fields = recordType.prototype.fields;
16797         var records = [];
16798         var root = o;
16799         for(var i = 0; i < root.length; i++){
16800             var n = root[i];
16801             var values = {};
16802             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16803             for(var j = 0, jlen = fields.length; j < jlen; j++){
16804                 var f = fields.items[j];
16805                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16806                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16807                 v = f.convert(v);
16808                 values[f.name] = v;
16809             }
16810             var record = new recordType(values, id);
16811             record.json = n;
16812             records[records.length] = record;
16813         }
16814         return {
16815             records : records,
16816             totalRecords : records.length
16817         };
16818     },
16819     // used when loading children.. @see loadDataFromChildren
16820     toLoadData: function(rec)
16821     {
16822         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16823         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16824         
16825     }
16826     
16827     
16828 });/*
16829  * - LGPL
16830  * * 
16831  */
16832
16833 /**
16834  * @class Roo.bootstrap.form.ComboBox
16835  * @extends Roo.bootstrap.form.TriggerField
16836  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16837  * @cfg {Boolean} append (true|false) default false
16838  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16839  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16840  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16841  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16842  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16843  * @cfg {Boolean} animate default true
16844  * @cfg {Boolean} emptyResultText only for touch device
16845  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16846  * @cfg {String} emptyTitle default ''
16847  * @cfg {Number} width fixed with? experimental
16848  * @constructor
16849  * Create a new ComboBox.
16850  * @param {Object} config Configuration options
16851  */
16852 Roo.bootstrap.form.ComboBox = function(config){
16853     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16854     this.addEvents({
16855         /**
16856          * @event expand
16857          * Fires when the dropdown list is expanded
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         */
16860         'expand' : true,
16861         /**
16862          * @event collapse
16863          * Fires when the dropdown list is collapsed
16864         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865         */
16866         'collapse' : true,
16867         /**
16868          * @event beforeselect
16869          * Fires before a list item is selected. Return false to cancel the selection.
16870         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871         * @param {Roo.data.Record} record The data record returned from the underlying store
16872         * @param {Number} index The index of the selected item in the dropdown list
16873         */
16874         'beforeselect' : true,
16875         /**
16876          * @event select
16877          * Fires when a list item is selected
16878         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16880         * @param {Number} index The index of the selected item in the dropdown list
16881         */
16882         'select' : true,
16883         /**
16884          * @event beforequery
16885          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16886          * The event object passed has these properties:
16887         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888         * @param {String} query The query
16889         * @param {Boolean} forceAll true to force "all" query
16890         * @param {Boolean} cancel true to cancel the query
16891         * @param {Object} e The query event object
16892         */
16893         'beforequery': true,
16894          /**
16895          * @event add
16896          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16897         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16898         */
16899         'add' : true,
16900         /**
16901          * @event edit
16902          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16903         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16905         */
16906         'edit' : true,
16907         /**
16908          * @event remove
16909          * Fires when the remove value from the combobox array
16910         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16911         */
16912         'remove' : true,
16913         /**
16914          * @event afterremove
16915          * Fires when the remove value from the combobox array
16916         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16917         */
16918         'afterremove' : true,
16919         /**
16920          * @event specialfilter
16921          * Fires when specialfilter
16922             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16923             */
16924         'specialfilter' : true,
16925         /**
16926          * @event tick
16927          * Fires when tick the element
16928             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16929             */
16930         'tick' : true,
16931         /**
16932          * @event touchviewdisplay
16933          * Fires when touch view require special display (default is using displayField)
16934             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935             * @param {Object} cfg set html .
16936             */
16937         'touchviewdisplay' : true
16938         
16939     });
16940     
16941     this.item = [];
16942     this.tickItems = [];
16943     
16944     this.selectedIndex = -1;
16945     if(this.mode == 'local'){
16946         if(config.queryDelay === undefined){
16947             this.queryDelay = 10;
16948         }
16949         if(config.minChars === undefined){
16950             this.minChars = 0;
16951         }
16952     }
16953 };
16954
16955 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16956      
16957     /**
16958      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16959      * rendering into an Roo.Editor, defaults to false)
16960      */
16961     /**
16962      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16963      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16964      */
16965     /**
16966      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16967      */
16968     /**
16969      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16970      * the dropdown list (defaults to undefined, with no header element)
16971      */
16972
16973      /**
16974      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16975      */
16976      
16977      /**
16978      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16979      */
16980     listWidth: undefined,
16981     /**
16982      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16983      * mode = 'remote' or 'text' if mode = 'local')
16984      */
16985     displayField: undefined,
16986     
16987     /**
16988      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16989      * mode = 'remote' or 'value' if mode = 'local'). 
16990      * Note: use of a valueField requires the user make a selection
16991      * in order for a value to be mapped.
16992      */
16993     valueField: undefined,
16994     /**
16995      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16996      */
16997     modalTitle : '',
16998     
16999     /**
17000      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17001      * field's data value (defaults to the underlying DOM element's name)
17002      */
17003     hiddenName: undefined,
17004     /**
17005      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17006      */
17007     listClass: '',
17008     /**
17009      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17010      */
17011     selectedClass: 'active',
17012     
17013     /**
17014      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17015      */
17016     shadow:'sides',
17017     /**
17018      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17019      * anchor positions (defaults to 'tl-bl')
17020      */
17021     listAlign: 'tl-bl?',
17022     /**
17023      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17024      */
17025     maxHeight: 300,
17026     /**
17027      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17028      * query specified by the allQuery config option (defaults to 'query')
17029      */
17030     triggerAction: 'query',
17031     /**
17032      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17033      * (defaults to 4, does not apply if editable = false)
17034      */
17035     minChars : 4,
17036     /**
17037      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17038      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17039      */
17040     typeAhead: false,
17041     /**
17042      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17043      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17044      */
17045     queryDelay: 500,
17046     /**
17047      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17048      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17049      */
17050     pageSize: 0,
17051     /**
17052      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17053      * when editable = true (defaults to false)
17054      */
17055     selectOnFocus:false,
17056     /**
17057      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17058      */
17059     queryParam: 'query',
17060     /**
17061      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17062      * when mode = 'remote' (defaults to 'Loading...')
17063      */
17064     loadingText: 'Loading...',
17065     /**
17066      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17067      */
17068     resizable: false,
17069     /**
17070      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17071      */
17072     handleHeight : 8,
17073     /**
17074      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17075      * traditional select (defaults to true)
17076      */
17077     editable: true,
17078     /**
17079      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17080      */
17081     allQuery: '',
17082     /**
17083      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17084      */
17085     mode: 'remote',
17086     /**
17087      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17088      * listWidth has a higher value)
17089      */
17090     minListWidth : 70,
17091     /**
17092      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17093      * allow the user to set arbitrary text into the field (defaults to false)
17094      */
17095     forceSelection:false,
17096     /**
17097      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17098      * if typeAhead = true (defaults to 250)
17099      */
17100     typeAheadDelay : 250,
17101     /**
17102      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17103      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17104      */
17105     valueNotFoundText : undefined,
17106     /**
17107      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17108      */
17109     blockFocus : false,
17110     
17111     /**
17112      * @cfg {Boolean} disableClear Disable showing of clear button.
17113      */
17114     disableClear : false,
17115     /**
17116      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17117      */
17118     alwaysQuery : false,
17119     
17120     /**
17121      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17122      */
17123     multiple : false,
17124     
17125     /**
17126      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17127      */
17128     invalidClass : "has-warning",
17129     
17130     /**
17131      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17132      */
17133     validClass : "has-success",
17134     
17135     /**
17136      * @cfg {Boolean} specialFilter (true|false) special filter default false
17137      */
17138     specialFilter : false,
17139     
17140     /**
17141      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17142      */
17143     mobileTouchView : true,
17144     
17145     /**
17146      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17147      */
17148     useNativeIOS : false,
17149     
17150     /**
17151      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17152      */
17153     mobile_restrict_height : false,
17154     
17155     ios_options : false,
17156     
17157     //private
17158     addicon : false,
17159     editicon: false,
17160     
17161     page: 0,
17162     hasQuery: false,
17163     append: false,
17164     loadNext: false,
17165     autoFocus : true,
17166     tickable : false,
17167     btnPosition : 'right',
17168     triggerList : true,
17169     showToggleBtn : true,
17170     animate : true,
17171     emptyResultText: 'Empty',
17172     triggerText : 'Select',
17173     emptyTitle : '',
17174     width : false,
17175     
17176     // element that contains real text value.. (when hidden is used..)
17177     
17178     getAutoCreate : function()
17179     {   
17180         var cfg = false;
17181         //render
17182         /*
17183          * Render classic select for iso
17184          */
17185         
17186         if(Roo.isIOS && this.useNativeIOS){
17187             cfg = this.getAutoCreateNativeIOS();
17188             return cfg;
17189         }
17190         
17191         /*
17192          * Touch Devices
17193          */
17194         
17195         if(Roo.isTouch && this.mobileTouchView){
17196             cfg = this.getAutoCreateTouchView();
17197             return cfg;;
17198         }
17199         
17200         /*
17201          *  Normal ComboBox
17202          */
17203         if(!this.tickable){
17204             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17205             return cfg;
17206         }
17207         
17208         /*
17209          *  ComboBox with tickable selections
17210          */
17211              
17212         var align = this.labelAlign || this.parentLabelAlign();
17213         
17214         cfg = {
17215             cls : 'form-group roo-combobox-tickable' //input-group
17216         };
17217         
17218         var btn_text_select = '';
17219         var btn_text_done = '';
17220         var btn_text_cancel = '';
17221         
17222         if (this.btn_text_show) {
17223             btn_text_select = 'Select';
17224             btn_text_done = 'Done';
17225             btn_text_cancel = 'Cancel'; 
17226         }
17227         
17228         var buttons = {
17229             tag : 'div',
17230             cls : 'tickable-buttons',
17231             cn : [
17232                 {
17233                     tag : 'button',
17234                     type : 'button',
17235                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17236                     //html : this.triggerText
17237                     html: btn_text_select
17238                 },
17239                 {
17240                     tag : 'button',
17241                     type : 'button',
17242                     name : 'ok',
17243                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17244                     //html : 'Done'
17245                     html: btn_text_done
17246                 },
17247                 {
17248                     tag : 'button',
17249                     type : 'button',
17250                     name : 'cancel',
17251                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17252                     //html : 'Cancel'
17253                     html: btn_text_cancel
17254                 }
17255             ]
17256         };
17257         
17258         if(this.editable){
17259             buttons.cn.unshift({
17260                 tag: 'input',
17261                 cls: 'roo-select2-search-field-input'
17262             });
17263         }
17264         
17265         var _this = this;
17266         
17267         Roo.each(buttons.cn, function(c){
17268             if (_this.size) {
17269                 c.cls += ' btn-' + _this.size;
17270             }
17271
17272             if (_this.disabled) {
17273                 c.disabled = true;
17274             }
17275         });
17276         
17277         var box = {
17278             tag: 'div',
17279             style : 'display: contents',
17280             cn: [
17281                 {
17282                     tag: 'input',
17283                     type : 'hidden',
17284                     cls: 'form-hidden-field'
17285                 },
17286                 {
17287                     tag: 'ul',
17288                     cls: 'roo-select2-choices',
17289                     cn:[
17290                         {
17291                             tag: 'li',
17292                             cls: 'roo-select2-search-field',
17293                             cn: [
17294                                 buttons
17295                             ]
17296                         }
17297                     ]
17298                 }
17299             ]
17300         };
17301         
17302         var combobox = {
17303             cls: 'roo-select2-container input-group roo-select2-container-multi',
17304             cn: [
17305                 
17306                 box
17307 //                {
17308 //                    tag: 'ul',
17309 //                    cls: 'typeahead typeahead-long dropdown-menu',
17310 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17311 //                }
17312             ]
17313         };
17314         
17315         if(this.hasFeedback && !this.allowBlank){
17316             
17317             var feedback = {
17318                 tag: 'span',
17319                 cls: 'glyphicon form-control-feedback'
17320             };
17321
17322             combobox.cn.push(feedback);
17323         }
17324         
17325         
17326         
17327         var indicator = {
17328             tag : 'i',
17329             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17330             tooltip : 'This field is required'
17331         };
17332          
17333         if (this.allowBlank) {
17334             indicator = {
17335                 tag : 'i',
17336                 style : 'display:none'
17337             };
17338         } 
17339         if (align ==='left' && this.fieldLabel.length) {
17340             
17341             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17342             
17343             cfg.cn = [
17344                 indicator,
17345                 {
17346                     tag: 'label',
17347                     'for' :  id,
17348                     cls : 'control-label col-form-label',
17349                     html : this.fieldLabel
17350
17351                 },
17352                 {
17353                     cls : "", 
17354                     cn: [
17355                         combobox
17356                     ]
17357                 }
17358
17359             ];
17360             
17361             var labelCfg = cfg.cn[1];
17362             var contentCfg = cfg.cn[2];
17363             
17364
17365             if(this.indicatorpos == 'right'){
17366                 
17367                 cfg.cn = [
17368                     {
17369                         tag: 'label',
17370                         'for' :  id,
17371                         cls : 'control-label col-form-label',
17372                         cn : [
17373                             {
17374                                 tag : 'span',
17375                                 html : this.fieldLabel
17376                             },
17377                             indicator
17378                         ]
17379                     },
17380                     {
17381                         cls : "",
17382                         cn: [
17383                             combobox
17384                         ]
17385                     }
17386
17387                 ];
17388                 
17389                 
17390                 
17391                 labelCfg = cfg.cn[0];
17392                 contentCfg = cfg.cn[1];
17393             
17394             }
17395             
17396             if(this.labelWidth > 12){
17397                 labelCfg.style = "width: " + this.labelWidth + 'px';
17398             }
17399             if(this.width * 1 > 0){
17400                 contentCfg.style = "width: " + this.width + 'px';
17401             }
17402             if(this.labelWidth < 13 && this.labelmd == 0){
17403                 this.labelmd = this.labelWidth;
17404             }
17405             
17406             if(this.labellg > 0){
17407                 labelCfg.cls += ' col-lg-' + this.labellg;
17408                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17409             }
17410             
17411             if(this.labelmd > 0){
17412                 labelCfg.cls += ' col-md-' + this.labelmd;
17413                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17414             }
17415             
17416             if(this.labelsm > 0){
17417                 labelCfg.cls += ' col-sm-' + this.labelsm;
17418                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17419             }
17420             
17421             if(this.labelxs > 0){
17422                 labelCfg.cls += ' col-xs-' + this.labelxs;
17423                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17424             }
17425                 
17426                 
17427         } else if ( this.fieldLabel.length) {
17428 //                Roo.log(" label");
17429                  cfg.cn = [
17430                    indicator,
17431                     {
17432                         tag: 'label',
17433                         //cls : 'input-group-addon',
17434                         html : this.fieldLabel
17435                     },
17436                     combobox
17437                 ];
17438                 
17439                 if(this.indicatorpos == 'right'){
17440                     cfg.cn = [
17441                         {
17442                             tag: 'label',
17443                             //cls : 'input-group-addon',
17444                             html : this.fieldLabel
17445                         },
17446                         indicator,
17447                         combobox
17448                     ];
17449                     
17450                 }
17451
17452         } else {
17453             
17454 //                Roo.log(" no label && no align");
17455                 cfg = combobox
17456                      
17457                 
17458         }
17459          
17460         var settings=this;
17461         ['xs','sm','md','lg'].map(function(size){
17462             if (settings[size]) {
17463                 cfg.cls += ' col-' + size + '-' + settings[size];
17464             }
17465         });
17466         
17467         return cfg;
17468         
17469     },
17470     
17471     _initEventsCalled : false,
17472     
17473     // private
17474     initEvents: function()
17475     {   
17476         if (this._initEventsCalled) { // as we call render... prevent looping...
17477             return;
17478         }
17479         this._initEventsCalled = true;
17480         
17481         if (!this.store) {
17482             throw "can not find store for combo";
17483         }
17484         
17485         this.indicator = this.indicatorEl();
17486         
17487         this.store = Roo.factory(this.store, Roo.data);
17488         this.store.parent = this;
17489         
17490         // if we are building from html. then this element is so complex, that we can not really
17491         // use the rendered HTML.
17492         // so we have to trash and replace the previous code.
17493         if (Roo.XComponent.build_from_html) {
17494             // remove this element....
17495             var e = this.el.dom, k=0;
17496             while (e ) { e = e.previousSibling;  ++k;}
17497
17498             this.el.remove();
17499             
17500             this.el=false;
17501             this.rendered = false;
17502             
17503             this.render(this.parent().getChildContainer(true), k);
17504         }
17505         
17506         if(Roo.isIOS && this.useNativeIOS){
17507             this.initIOSView();
17508             return;
17509         }
17510         
17511         /*
17512          * Touch Devices
17513          */
17514         
17515         if(Roo.isTouch && this.mobileTouchView){
17516             this.initTouchView();
17517             return;
17518         }
17519         
17520         if(this.tickable){
17521             this.initTickableEvents();
17522             return;
17523         }
17524         
17525         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17526         
17527         if(this.hiddenName){
17528             
17529             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17530             
17531             this.hiddenField.dom.value =
17532                 this.hiddenValue !== undefined ? this.hiddenValue :
17533                 this.value !== undefined ? this.value : '';
17534
17535             // prevent input submission
17536             this.el.dom.removeAttribute('name');
17537             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17538              
17539              
17540         }
17541         //if(Roo.isGecko){
17542         //    this.el.dom.setAttribute('autocomplete', 'off');
17543         //}
17544         
17545         var cls = 'x-combo-list';
17546         
17547         //this.list = new Roo.Layer({
17548         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17549         //});
17550         
17551         var _this = this;
17552         
17553         (function(){
17554             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17555             _this.list.setWidth(lw);
17556         }).defer(100);
17557         
17558         this.list.on('mouseover', this.onViewOver, this);
17559         this.list.on('mousemove', this.onViewMove, this);
17560         this.list.on('scroll', this.onViewScroll, this);
17561         
17562         /*
17563         this.list.swallowEvent('mousewheel');
17564         this.assetHeight = 0;
17565
17566         if(this.title){
17567             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17568             this.assetHeight += this.header.getHeight();
17569         }
17570
17571         this.innerList = this.list.createChild({cls:cls+'-inner'});
17572         this.innerList.on('mouseover', this.onViewOver, this);
17573         this.innerList.on('mousemove', this.onViewMove, this);
17574         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17575         
17576         if(this.allowBlank && !this.pageSize && !this.disableClear){
17577             this.footer = this.list.createChild({cls:cls+'-ft'});
17578             this.pageTb = new Roo.Toolbar(this.footer);
17579            
17580         }
17581         if(this.pageSize){
17582             this.footer = this.list.createChild({cls:cls+'-ft'});
17583             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17584                     {pageSize: this.pageSize});
17585             
17586         }
17587         
17588         if (this.pageTb && this.allowBlank && !this.disableClear) {
17589             var _this = this;
17590             this.pageTb.add(new Roo.Toolbar.Fill(), {
17591                 cls: 'x-btn-icon x-btn-clear',
17592                 text: '&#160;',
17593                 handler: function()
17594                 {
17595                     _this.collapse();
17596                     _this.clearValue();
17597                     _this.onSelect(false, -1);
17598                 }
17599             });
17600         }
17601         if (this.footer) {
17602             this.assetHeight += this.footer.getHeight();
17603         }
17604         */
17605             
17606         if(!this.tpl){
17607             this.tpl = Roo.bootstrap.version == 4 ?
17608                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17609                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17610         }
17611
17612         this.view = new Roo.View(this.list, this.tpl, {
17613             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17614         });
17615         //this.view.wrapEl.setDisplayed(false);
17616         this.view.on('click', this.onViewClick, this);
17617         
17618         
17619         this.store.on('beforeload', this.onBeforeLoad, this);
17620         this.store.on('load', this.onLoad, this);
17621         this.store.on('loadexception', this.onLoadException, this);
17622         /*
17623         if(this.resizable){
17624             this.resizer = new Roo.Resizable(this.list,  {
17625                pinned:true, handles:'se'
17626             });
17627             this.resizer.on('resize', function(r, w, h){
17628                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17629                 this.listWidth = w;
17630                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17631                 this.restrictHeight();
17632             }, this);
17633             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17634         }
17635         */
17636         if(!this.editable){
17637             this.editable = true;
17638             this.setEditable(false);
17639         }
17640         
17641         /*
17642         
17643         if (typeof(this.events.add.listeners) != 'undefined') {
17644             
17645             this.addicon = this.wrap.createChild(
17646                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17647        
17648             this.addicon.on('click', function(e) {
17649                 this.fireEvent('add', this);
17650             }, this);
17651         }
17652         if (typeof(this.events.edit.listeners) != 'undefined') {
17653             
17654             this.editicon = this.wrap.createChild(
17655                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17656             if (this.addicon) {
17657                 this.editicon.setStyle('margin-left', '40px');
17658             }
17659             this.editicon.on('click', function(e) {
17660                 
17661                 // we fire even  if inothing is selected..
17662                 this.fireEvent('edit', this, this.lastData );
17663                 
17664             }, this);
17665         }
17666         */
17667         
17668         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17669             "up" : function(e){
17670                 this.inKeyMode = true;
17671                 this.selectPrev();
17672             },
17673
17674             "down" : function(e){
17675                 if(!this.isExpanded()){
17676                     this.onTriggerClick();
17677                 }else{
17678                     this.inKeyMode = true;
17679                     this.selectNext();
17680                 }
17681             },
17682
17683             "enter" : function(e){
17684 //                this.onViewClick();
17685                 //return true;
17686                 this.collapse();
17687                 
17688                 if(this.fireEvent("specialkey", this, e)){
17689                     this.onViewClick(false);
17690                 }
17691                 
17692                 return true;
17693             },
17694
17695             "esc" : function(e){
17696                 this.collapse();
17697             },
17698
17699             "tab" : function(e){
17700                 this.collapse();
17701                 
17702                 if(this.fireEvent("specialkey", this, e)){
17703                     this.onViewClick(false);
17704                 }
17705                 
17706                 return true;
17707             },
17708
17709             scope : this,
17710
17711             doRelay : function(foo, bar, hname){
17712                 if(hname == 'down' || this.scope.isExpanded()){
17713                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17714                 }
17715                 return true;
17716             },
17717
17718             forceKeyDown: true
17719         });
17720         
17721         
17722         this.queryDelay = Math.max(this.queryDelay || 10,
17723                 this.mode == 'local' ? 10 : 250);
17724         
17725         
17726         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17727         
17728         if(this.typeAhead){
17729             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17730         }
17731         if(this.editable !== false){
17732             this.inputEl().on("keyup", this.onKeyUp, this);
17733         }
17734         if(this.forceSelection){
17735             this.inputEl().on('blur', this.doForce, this);
17736         }
17737         
17738         if(this.multiple){
17739             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17740             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17741         }
17742     },
17743     
17744     initTickableEvents: function()
17745     {   
17746         this.createList();
17747         
17748         if(this.hiddenName){
17749             
17750             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17751             
17752             this.hiddenField.dom.value =
17753                 this.hiddenValue !== undefined ? this.hiddenValue :
17754                 this.value !== undefined ? this.value : '';
17755
17756             // prevent input submission
17757             this.el.dom.removeAttribute('name');
17758             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17759              
17760              
17761         }
17762         
17763 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17764         
17765         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17766         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17767         if(this.triggerList){
17768             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17769         }
17770          
17771         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17772         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17773         
17774         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17775         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17776         
17777         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17778         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17779         
17780         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17781         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17782         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17783         
17784         this.okBtn.hide();
17785         this.cancelBtn.hide();
17786         
17787         var _this = this;
17788         
17789         (function(){
17790             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17791             _this.list.setWidth(lw);
17792         }).defer(100);
17793         
17794         this.list.on('mouseover', this.onViewOver, this);
17795         this.list.on('mousemove', this.onViewMove, this);
17796         
17797         this.list.on('scroll', this.onViewScroll, this);
17798         
17799         if(!this.tpl){
17800             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17801                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17802         }
17803
17804         this.view = new Roo.View(this.list, this.tpl, {
17805             singleSelect:true,
17806             tickable:true,
17807             parent:this,
17808             store: this.store,
17809             selectedClass: this.selectedClass
17810         });
17811         
17812         //this.view.wrapEl.setDisplayed(false);
17813         this.view.on('click', this.onViewClick, this);
17814         
17815         
17816         
17817         this.store.on('beforeload', this.onBeforeLoad, this);
17818         this.store.on('load', this.onLoad, this);
17819         this.store.on('loadexception', this.onLoadException, this);
17820         
17821         if(this.editable){
17822             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17823                 "up" : function(e){
17824                     this.inKeyMode = true;
17825                     this.selectPrev();
17826                 },
17827
17828                 "down" : function(e){
17829                     this.inKeyMode = true;
17830                     this.selectNext();
17831                 },
17832
17833                 "enter" : function(e){
17834                     if(this.fireEvent("specialkey", this, e)){
17835                         this.onViewClick(false);
17836                     }
17837                     
17838                     return true;
17839                 },
17840
17841                 "esc" : function(e){
17842                     this.onTickableFooterButtonClick(e, false, false);
17843                 },
17844
17845                 "tab" : function(e){
17846                     this.fireEvent("specialkey", this, e);
17847                     
17848                     this.onTickableFooterButtonClick(e, false, false);
17849                     
17850                     return true;
17851                 },
17852
17853                 scope : this,
17854
17855                 doRelay : function(e, fn, key){
17856                     if(this.scope.isExpanded()){
17857                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17858                     }
17859                     return true;
17860                 },
17861
17862                 forceKeyDown: true
17863             });
17864         }
17865         
17866         this.queryDelay = Math.max(this.queryDelay || 10,
17867                 this.mode == 'local' ? 10 : 250);
17868         
17869         
17870         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17871         
17872         if(this.typeAhead){
17873             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17874         }
17875         
17876         if(this.editable !== false){
17877             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17878         }
17879         
17880         this.indicator = this.indicatorEl();
17881         
17882         if(this.indicator){
17883             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17884             this.indicator.hide();
17885         }
17886         
17887     },
17888
17889     onDestroy : function(){
17890         if(this.view){
17891             this.view.setStore(null);
17892             this.view.el.removeAllListeners();
17893             this.view.el.remove();
17894             this.view.purgeListeners();
17895         }
17896         if(this.list){
17897             this.list.dom.innerHTML  = '';
17898         }
17899         
17900         if(this.store){
17901             this.store.un('beforeload', this.onBeforeLoad, this);
17902             this.store.un('load', this.onLoad, this);
17903             this.store.un('loadexception', this.onLoadException, this);
17904         }
17905         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17906     },
17907
17908     // private
17909     fireKey : function(e){
17910         if(e.isNavKeyPress() && !this.list.isVisible()){
17911             this.fireEvent("specialkey", this, e);
17912         }
17913     },
17914
17915     // private
17916     onResize: function(w, h)
17917     {
17918         
17919         
17920 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17921 //        
17922 //        if(typeof w != 'number'){
17923 //            // we do not handle it!?!?
17924 //            return;
17925 //        }
17926 //        var tw = this.trigger.getWidth();
17927 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17928 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17929 //        var x = w - tw;
17930 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17931 //            
17932 //        //this.trigger.setStyle('left', x+'px');
17933 //        
17934 //        if(this.list && this.listWidth === undefined){
17935 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17936 //            this.list.setWidth(lw);
17937 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17938 //        }
17939         
17940     
17941         
17942     },
17943
17944     /**
17945      * Allow or prevent the user from directly editing the field text.  If false is passed,
17946      * the user will only be able to select from the items defined in the dropdown list.  This method
17947      * is the runtime equivalent of setting the 'editable' config option at config time.
17948      * @param {Boolean} value True to allow the user to directly edit the field text
17949      */
17950     setEditable : function(value){
17951         if(value == this.editable){
17952             return;
17953         }
17954         this.editable = value;
17955         if(!value){
17956             this.inputEl().dom.setAttribute('readOnly', true);
17957             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17958             this.inputEl().addClass('x-combo-noedit');
17959         }else{
17960             this.inputEl().dom.removeAttribute('readOnly');
17961             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17962             this.inputEl().removeClass('x-combo-noedit');
17963         }
17964     },
17965
17966     // private
17967     
17968     onBeforeLoad : function(combo,opts){
17969         if(!this.hasFocus){
17970             return;
17971         }
17972          if (!opts.add) {
17973             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17974          }
17975         this.restrictHeight();
17976         this.selectedIndex = -1;
17977     },
17978
17979     // private
17980     onLoad : function(){
17981         
17982         this.hasQuery = false;
17983         
17984         if(!this.hasFocus){
17985             return;
17986         }
17987         
17988         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17989             this.loading.hide();
17990         }
17991         
17992         if(this.store.getCount() > 0){
17993             
17994             this.expand();
17995             this.restrictHeight();
17996             if(this.lastQuery == this.allQuery){
17997                 if(this.editable && !this.tickable){
17998                     this.inputEl().dom.select();
17999                 }
18000                 
18001                 if(
18002                     !this.selectByValue(this.value, true) &&
18003                     this.autoFocus && 
18004                     (
18005                         !this.store.lastOptions ||
18006                         typeof(this.store.lastOptions.add) == 'undefined' || 
18007                         this.store.lastOptions.add != true
18008                     )
18009                 ){
18010                     this.select(0, true);
18011                 }
18012             }else{
18013                 if(this.autoFocus){
18014                     this.selectNext();
18015                 }
18016                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18017                     this.taTask.delay(this.typeAheadDelay);
18018                 }
18019             }
18020         }else{
18021             this.onEmptyResults();
18022         }
18023         
18024         //this.el.focus();
18025     },
18026     // private
18027     onLoadException : function()
18028     {
18029         this.hasQuery = false;
18030         
18031         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18032             this.loading.hide();
18033         }
18034         
18035         if(this.tickable && this.editable){
18036             return;
18037         }
18038         
18039         this.collapse();
18040         // only causes errors at present
18041         //Roo.log(this.store.reader.jsonData);
18042         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18043             // fixme
18044             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18045         //}
18046         
18047         
18048     },
18049     // private
18050     onTypeAhead : function(){
18051         if(this.store.getCount() > 0){
18052             var r = this.store.getAt(0);
18053             var newValue = r.data[this.displayField];
18054             var len = newValue.length;
18055             var selStart = this.getRawValue().length;
18056             
18057             if(selStart != len){
18058                 this.setRawValue(newValue);
18059                 this.selectText(selStart, newValue.length);
18060             }
18061         }
18062     },
18063
18064     // private
18065     onSelect : function(record, index){
18066         
18067         if(this.fireEvent('beforeselect', this, record, index) !== false){
18068         
18069             this.setFromData(index > -1 ? record.data : false);
18070             
18071             this.collapse();
18072             this.fireEvent('select', this, record, index);
18073         }
18074     },
18075
18076     /**
18077      * Returns the currently selected field value or empty string if no value is set.
18078      * @return {String} value The selected value
18079      */
18080     getValue : function()
18081     {
18082         if(Roo.isIOS && this.useNativeIOS){
18083             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18084         }
18085         
18086         if(this.multiple){
18087             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18088         }
18089         
18090         if(this.valueField){
18091             return typeof this.value != 'undefined' ? this.value : '';
18092         }else{
18093             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18094         }
18095     },
18096     
18097     getRawValue : function()
18098     {
18099         if(Roo.isIOS && this.useNativeIOS){
18100             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18101         }
18102         
18103         var v = this.inputEl().getValue();
18104         
18105         return v;
18106     },
18107
18108     /**
18109      * Clears any text/value currently set in the field
18110      */
18111     clearValue : function(){
18112         
18113         if(this.hiddenField){
18114             this.hiddenField.dom.value = '';
18115         }
18116         this.value = '';
18117         this.setRawValue('');
18118         this.lastSelectionText = '';
18119         this.lastData = false;
18120         
18121         var close = this.closeTriggerEl();
18122         
18123         if(close){
18124             close.hide();
18125         }
18126         
18127         this.validate();
18128         
18129     },
18130
18131     /**
18132      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18133      * will be displayed in the field.  If the value does not match the data value of an existing item,
18134      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18135      * Otherwise the field will be blank (although the value will still be set).
18136      * @param {String} value The value to match
18137      */
18138     setValue : function(v)
18139     {
18140         if(Roo.isIOS && this.useNativeIOS){
18141             this.setIOSValue(v);
18142             return;
18143         }
18144         
18145         if(this.multiple){
18146             this.syncValue();
18147             return;
18148         }
18149         
18150         var text = v;
18151         if(this.valueField){
18152             var r = this.findRecord(this.valueField, v);
18153             if(r){
18154                 text = r.data[this.displayField];
18155             }else if(this.valueNotFoundText !== undefined){
18156                 text = this.valueNotFoundText;
18157             }
18158         }
18159         this.lastSelectionText = text;
18160         if(this.hiddenField){
18161             this.hiddenField.dom.value = v;
18162         }
18163         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18164         this.value = v;
18165         
18166         var close = this.closeTriggerEl();
18167         
18168         if(close){
18169             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18170         }
18171         
18172         this.validate();
18173     },
18174     /**
18175      * @property {Object} the last set data for the element
18176      */
18177     
18178     lastData : false,
18179     /**
18180      * Sets the value of the field based on a object which is related to the record format for the store.
18181      * @param {Object} value the value to set as. or false on reset?
18182      */
18183     setFromData : function(o){
18184         
18185         if(this.multiple){
18186             this.addItem(o);
18187             return;
18188         }
18189             
18190         var dv = ''; // display value
18191         var vv = ''; // value value..
18192         this.lastData = o;
18193         if (this.displayField) {
18194             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18195         } else {
18196             // this is an error condition!!!
18197             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18198         }
18199         
18200         if(this.valueField){
18201             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18202         }
18203         
18204         var close = this.closeTriggerEl();
18205         
18206         if(close){
18207             if(dv.length || vv * 1 > 0){
18208                 close.show() ;
18209                 this.blockFocus=true;
18210             } else {
18211                 close.hide();
18212             }             
18213         }
18214         
18215         if(this.hiddenField){
18216             this.hiddenField.dom.value = vv;
18217             
18218             this.lastSelectionText = dv;
18219             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18220             this.value = vv;
18221             return;
18222         }
18223         // no hidden field.. - we store the value in 'value', but still display
18224         // display field!!!!
18225         this.lastSelectionText = dv;
18226         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18227         this.value = vv;
18228         
18229         
18230         
18231     },
18232     // private
18233     reset : function(){
18234         // overridden so that last data is reset..
18235         
18236         if(this.multiple){
18237             this.clearItem();
18238             return;
18239         }
18240         
18241         this.setValue(this.originalValue);
18242         //this.clearInvalid();
18243         this.lastData = false;
18244         if (this.view) {
18245             this.view.clearSelections();
18246         }
18247         
18248         this.validate();
18249     },
18250     // private
18251     findRecord : function(prop, value){
18252         var record;
18253         if(this.store.getCount() > 0){
18254             this.store.each(function(r){
18255                 if(r.data[prop] == value){
18256                     record = r;
18257                     return false;
18258                 }
18259                 return true;
18260             });
18261         }
18262         return record;
18263     },
18264     
18265     getName: function()
18266     {
18267         // returns hidden if it's set..
18268         if (!this.rendered) {return ''};
18269         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18270         
18271     },
18272     // private
18273     onViewMove : function(e, t){
18274         this.inKeyMode = false;
18275     },
18276
18277     // private
18278     onViewOver : function(e, t){
18279         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18280             return;
18281         }
18282         var item = this.view.findItemFromChild(t);
18283         
18284         if(item){
18285             var index = this.view.indexOf(item);
18286             this.select(index, false);
18287         }
18288     },
18289
18290     // private
18291     onViewClick : function(view, doFocus, el, e)
18292     {
18293         var index = this.view.getSelectedIndexes()[0];
18294         
18295         var r = this.store.getAt(index);
18296         
18297         if(this.tickable){
18298             
18299             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18300                 return;
18301             }
18302             
18303             var rm = false;
18304             var _this = this;
18305             
18306             Roo.each(this.tickItems, function(v,k){
18307                 
18308                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18309                     Roo.log(v);
18310                     _this.tickItems.splice(k, 1);
18311                     
18312                     if(typeof(e) == 'undefined' && view == false){
18313                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18314                     }
18315                     
18316                     rm = true;
18317                     return;
18318                 }
18319             });
18320             
18321             if(rm){
18322                 return;
18323             }
18324             
18325             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18326                 this.tickItems.push(r.data);
18327             }
18328             
18329             if(typeof(e) == 'undefined' && view == false){
18330                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18331             }
18332                     
18333             return;
18334         }
18335         
18336         if(r){
18337             this.onSelect(r, index);
18338         }
18339         if(doFocus !== false && !this.blockFocus){
18340             this.inputEl().focus();
18341         }
18342     },
18343
18344     // private
18345     restrictHeight : function(){
18346         //this.innerList.dom.style.height = '';
18347         //var inner = this.innerList.dom;
18348         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18349         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18350         //this.list.beginUpdate();
18351         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18352         this.list.alignTo(this.inputEl(), this.listAlign);
18353         this.list.alignTo(this.inputEl(), this.listAlign);
18354         //this.list.endUpdate();
18355     },
18356
18357     // private
18358     onEmptyResults : function(){
18359         
18360         if(this.tickable && this.editable){
18361             this.hasFocus = false;
18362             this.restrictHeight();
18363             return;
18364         }
18365         
18366         this.collapse();
18367     },
18368
18369     /**
18370      * Returns true if the dropdown list is expanded, else false.
18371      */
18372     isExpanded : function(){
18373         return this.list.isVisible();
18374     },
18375
18376     /**
18377      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18378      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18379      * @param {String} value The data value of the item to select
18380      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18381      * selected item if it is not currently in view (defaults to true)
18382      * @return {Boolean} True if the value matched an item in the list, else false
18383      */
18384     selectByValue : function(v, scrollIntoView){
18385         if(v !== undefined && v !== null){
18386             var r = this.findRecord(this.valueField || this.displayField, v);
18387             if(r){
18388                 this.select(this.store.indexOf(r), scrollIntoView);
18389                 return true;
18390             }
18391         }
18392         return false;
18393     },
18394
18395     /**
18396      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18397      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18398      * @param {Number} index The zero-based index of the list item to select
18399      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18400      * selected item if it is not currently in view (defaults to true)
18401      */
18402     select : function(index, scrollIntoView){
18403         this.selectedIndex = index;
18404         this.view.select(index);
18405         if(scrollIntoView !== false){
18406             var el = this.view.getNode(index);
18407             /*
18408              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18409              */
18410             if(el){
18411                 this.list.scrollChildIntoView(el, false);
18412             }
18413         }
18414     },
18415
18416     // private
18417     selectNext : function(){
18418         var ct = this.store.getCount();
18419         if(ct > 0){
18420             if(this.selectedIndex == -1){
18421                 this.select(0);
18422             }else if(this.selectedIndex < ct-1){
18423                 this.select(this.selectedIndex+1);
18424             }
18425         }
18426     },
18427
18428     // private
18429     selectPrev : function(){
18430         var ct = this.store.getCount();
18431         if(ct > 0){
18432             if(this.selectedIndex == -1){
18433                 this.select(0);
18434             }else if(this.selectedIndex != 0){
18435                 this.select(this.selectedIndex-1);
18436             }
18437         }
18438     },
18439
18440     // private
18441     onKeyUp : function(e){
18442         if(this.editable !== false && !e.isSpecialKey()){
18443             this.lastKey = e.getKey();
18444             this.dqTask.delay(this.queryDelay);
18445         }
18446     },
18447
18448     // private
18449     validateBlur : function(){
18450         return !this.list || !this.list.isVisible();   
18451     },
18452
18453     // private
18454     initQuery : function(){
18455         
18456         var v = this.getRawValue();
18457         
18458         if(this.tickable && this.editable){
18459             v = this.tickableInputEl().getValue();
18460         }
18461         
18462         this.doQuery(v);
18463     },
18464
18465     // private
18466     doForce : function(){
18467         if(this.inputEl().dom.value.length > 0){
18468             this.inputEl().dom.value =
18469                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18470              
18471         }
18472     },
18473
18474     /**
18475      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18476      * query allowing the query action to be canceled if needed.
18477      * @param {String} query The SQL query to execute
18478      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18479      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18480      * saved in the current store (defaults to false)
18481      */
18482     doQuery : function(q, forceAll){
18483         
18484         if(q === undefined || q === null){
18485             q = '';
18486         }
18487         var qe = {
18488             query: q,
18489             forceAll: forceAll,
18490             combo: this,
18491             cancel:false
18492         };
18493         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18494             return false;
18495         }
18496         q = qe.query;
18497         
18498         forceAll = qe.forceAll;
18499         if(forceAll === true || (q.length >= this.minChars)){
18500             
18501             this.hasQuery = true;
18502             
18503             if(this.lastQuery != q || this.alwaysQuery){
18504                 this.lastQuery = q;
18505                 if(this.mode == 'local'){
18506                     this.selectedIndex = -1;
18507                     if(forceAll){
18508                         this.store.clearFilter();
18509                     }else{
18510                         
18511                         if(this.specialFilter){
18512                             this.fireEvent('specialfilter', this);
18513                             this.onLoad();
18514                             return;
18515                         }
18516                         
18517                         this.store.filter(this.displayField, q);
18518                     }
18519                     
18520                     this.store.fireEvent("datachanged", this.store);
18521                     
18522                     this.onLoad();
18523                     
18524                     
18525                 }else{
18526                     
18527                     this.store.baseParams[this.queryParam] = q;
18528                     
18529                     var options = {params : this.getParams(q)};
18530                     
18531                     if(this.loadNext){
18532                         options.add = true;
18533                         options.params.start = this.page * this.pageSize;
18534                     }
18535                     
18536                     this.store.load(options);
18537                     
18538                     /*
18539                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18540                      *  we should expand the list on onLoad
18541                      *  so command out it
18542                      */
18543 //                    this.expand();
18544                 }
18545             }else{
18546                 this.selectedIndex = -1;
18547                 this.onLoad();   
18548             }
18549         }
18550         
18551         this.loadNext = false;
18552     },
18553     
18554     // private
18555     getParams : function(q){
18556         var p = {};
18557         //p[this.queryParam] = q;
18558         
18559         if(this.pageSize){
18560             p.start = 0;
18561             p.limit = this.pageSize;
18562         }
18563         return p;
18564     },
18565
18566     /**
18567      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18568      */
18569     collapse : function(){
18570         if(!this.isExpanded()){
18571             return;
18572         }
18573         
18574         this.list.hide();
18575         
18576         this.hasFocus = false;
18577         
18578         if(this.tickable){
18579             this.okBtn.hide();
18580             this.cancelBtn.hide();
18581             this.trigger.show();
18582             
18583             if(this.editable){
18584                 this.tickableInputEl().dom.value = '';
18585                 this.tickableInputEl().blur();
18586             }
18587             
18588         }
18589         
18590         Roo.get(document).un('mousedown', this.collapseIf, this);
18591         Roo.get(document).un('mousewheel', this.collapseIf, this);
18592         if (!this.editable) {
18593             Roo.get(document).un('keydown', this.listKeyPress, this);
18594         }
18595         this.fireEvent('collapse', this);
18596         
18597         this.validate();
18598     },
18599
18600     // private
18601     collapseIf : function(e){
18602         var in_combo  = e.within(this.el);
18603         var in_list =  e.within(this.list);
18604         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18605         
18606         if (in_combo || in_list || is_list) {
18607             //e.stopPropagation();
18608             return;
18609         }
18610         
18611         if(this.tickable){
18612             this.onTickableFooterButtonClick(e, false, false);
18613         }
18614
18615         this.collapse();
18616         
18617     },
18618
18619     /**
18620      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18621      */
18622     expand : function(){
18623        
18624         if(this.isExpanded() || !this.hasFocus){
18625             return;
18626         }
18627         
18628         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18629         this.list.setWidth(lw);
18630         
18631         Roo.log('expand');
18632         
18633         this.list.show();
18634         
18635         this.restrictHeight();
18636         
18637         if(this.tickable){
18638             
18639             this.tickItems = Roo.apply([], this.item);
18640             
18641             this.okBtn.show();
18642             this.cancelBtn.show();
18643             this.trigger.hide();
18644             
18645             if(this.editable){
18646                 this.tickableInputEl().focus();
18647             }
18648             
18649         }
18650         
18651         Roo.get(document).on('mousedown', this.collapseIf, this);
18652         Roo.get(document).on('mousewheel', this.collapseIf, this);
18653         if (!this.editable) {
18654             Roo.get(document).on('keydown', this.listKeyPress, this);
18655         }
18656         
18657         this.fireEvent('expand', this);
18658     },
18659
18660     // private
18661     // Implements the default empty TriggerField.onTriggerClick function
18662     onTriggerClick : function(e)
18663     {
18664         Roo.log('trigger click');
18665         
18666         if(this.disabled || !this.triggerList){
18667             return;
18668         }
18669         
18670         this.page = 0;
18671         this.loadNext = false;
18672         
18673         if(this.isExpanded()){
18674             this.collapse();
18675             if (!this.blockFocus) {
18676                 this.inputEl().focus();
18677             }
18678             
18679         }else {
18680             this.hasFocus = true;
18681             if(this.triggerAction == 'all') {
18682                 this.doQuery(this.allQuery, true);
18683             } else {
18684                 this.doQuery(this.getRawValue());
18685             }
18686             if (!this.blockFocus) {
18687                 this.inputEl().focus();
18688             }
18689         }
18690     },
18691     
18692     onTickableTriggerClick : function(e)
18693     {
18694         if(this.disabled){
18695             return;
18696         }
18697         
18698         this.page = 0;
18699         this.loadNext = false;
18700         this.hasFocus = true;
18701         
18702         if(this.triggerAction == 'all') {
18703             this.doQuery(this.allQuery, true);
18704         } else {
18705             this.doQuery(this.getRawValue());
18706         }
18707     },
18708     
18709     onSearchFieldClick : function(e)
18710     {
18711         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18712             this.onTickableFooterButtonClick(e, false, false);
18713             return;
18714         }
18715         
18716         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18717             return;
18718         }
18719         
18720         this.page = 0;
18721         this.loadNext = false;
18722         this.hasFocus = true;
18723         
18724         if(this.triggerAction == 'all') {
18725             this.doQuery(this.allQuery, true);
18726         } else {
18727             this.doQuery(this.getRawValue());
18728         }
18729     },
18730     
18731     listKeyPress : function(e)
18732     {
18733         //Roo.log('listkeypress');
18734         // scroll to first matching element based on key pres..
18735         if (e.isSpecialKey()) {
18736             return false;
18737         }
18738         var k = String.fromCharCode(e.getKey()).toUpperCase();
18739         //Roo.log(k);
18740         var match  = false;
18741         var csel = this.view.getSelectedNodes();
18742         var cselitem = false;
18743         if (csel.length) {
18744             var ix = this.view.indexOf(csel[0]);
18745             cselitem  = this.store.getAt(ix);
18746             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18747                 cselitem = false;
18748             }
18749             
18750         }
18751         
18752         this.store.each(function(v) { 
18753             if (cselitem) {
18754                 // start at existing selection.
18755                 if (cselitem.id == v.id) {
18756                     cselitem = false;
18757                 }
18758                 return true;
18759             }
18760                 
18761             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18762                 match = this.store.indexOf(v);
18763                 return false;
18764             }
18765             return true;
18766         }, this);
18767         
18768         if (match === false) {
18769             return true; // no more action?
18770         }
18771         // scroll to?
18772         this.view.select(match);
18773         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18774         sn.scrollIntoView(sn.dom.parentNode, false);
18775     },
18776     
18777     onViewScroll : function(e, t){
18778         
18779         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){
18780             return;
18781         }
18782         
18783         this.hasQuery = true;
18784         
18785         this.loading = this.list.select('.loading', true).first();
18786         
18787         if(this.loading === null){
18788             this.list.createChild({
18789                 tag: 'div',
18790                 cls: 'loading roo-select2-more-results roo-select2-active',
18791                 html: 'Loading more results...'
18792             });
18793             
18794             this.loading = this.list.select('.loading', true).first();
18795             
18796             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18797             
18798             this.loading.hide();
18799         }
18800         
18801         this.loading.show();
18802         
18803         var _combo = this;
18804         
18805         this.page++;
18806         this.loadNext = true;
18807         
18808         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18809         
18810         return;
18811     },
18812     
18813     addItem : function(o)
18814     {   
18815         var dv = ''; // display value
18816         
18817         if (this.displayField) {
18818             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18819         } else {
18820             // this is an error condition!!!
18821             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18822         }
18823         
18824         if(!dv.length){
18825             return;
18826         }
18827         
18828         var choice = this.choices.createChild({
18829             tag: 'li',
18830             cls: 'roo-select2-search-choice',
18831             cn: [
18832                 {
18833                     tag: 'div',
18834                     html: dv
18835                 },
18836                 {
18837                     tag: 'a',
18838                     href: '#',
18839                     cls: 'roo-select2-search-choice-close fa fa-times',
18840                     tabindex: '-1'
18841                 }
18842             ]
18843             
18844         }, this.searchField);
18845         
18846         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18847         
18848         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18849         
18850         this.item.push(o);
18851         
18852         this.lastData = o;
18853         
18854         this.syncValue();
18855         
18856         this.inputEl().dom.value = '';
18857         
18858         this.validate();
18859     },
18860     
18861     onRemoveItem : function(e, _self, o)
18862     {
18863         e.preventDefault();
18864         
18865         this.lastItem = Roo.apply([], this.item);
18866         
18867         var index = this.item.indexOf(o.data) * 1;
18868         
18869         if( index < 0){
18870             Roo.log('not this item?!');
18871             return;
18872         }
18873         
18874         this.item.splice(index, 1);
18875         o.item.remove();
18876         
18877         this.syncValue();
18878         
18879         this.fireEvent('remove', this, e);
18880         
18881         this.validate();
18882         
18883     },
18884     
18885     syncValue : function()
18886     {
18887         if(!this.item.length){
18888             this.clearValue();
18889             return;
18890         }
18891             
18892         var value = [];
18893         var _this = this;
18894         Roo.each(this.item, function(i){
18895             if(_this.valueField){
18896                 value.push(i[_this.valueField]);
18897                 return;
18898             }
18899
18900             value.push(i);
18901         });
18902
18903         this.value = value.join(',');
18904
18905         if(this.hiddenField){
18906             this.hiddenField.dom.value = this.value;
18907         }
18908         
18909         this.store.fireEvent("datachanged", this.store);
18910         
18911         this.validate();
18912     },
18913     
18914     clearItem : function()
18915     {
18916         if(!this.multiple){
18917             return;
18918         }
18919         
18920         this.item = [];
18921         
18922         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18923            c.remove();
18924         });
18925         
18926         this.syncValue();
18927         
18928         this.validate();
18929         
18930         if(this.tickable && !Roo.isTouch){
18931             this.view.refresh();
18932         }
18933     },
18934     
18935     inputEl: function ()
18936     {
18937         if(Roo.isIOS && this.useNativeIOS){
18938             return this.el.select('select.roo-ios-select', true).first();
18939         }
18940         
18941         if(Roo.isTouch && this.mobileTouchView){
18942             return this.el.select('input.form-control',true).first();
18943         }
18944         
18945         if(this.tickable){
18946             return this.searchField;
18947         }
18948         
18949         return this.el.select('input.form-control',true).first();
18950     },
18951     
18952     onTickableFooterButtonClick : function(e, btn, el)
18953     {
18954         e.preventDefault();
18955         
18956         this.lastItem = Roo.apply([], this.item);
18957         
18958         if(btn && btn.name == 'cancel'){
18959             this.tickItems = Roo.apply([], this.item);
18960             this.collapse();
18961             return;
18962         }
18963         
18964         this.clearItem();
18965         
18966         var _this = this;
18967         
18968         Roo.each(this.tickItems, function(o){
18969             _this.addItem(o);
18970         });
18971         
18972         this.collapse();
18973         
18974     },
18975     
18976     validate : function()
18977     {
18978         if(this.getVisibilityEl().hasClass('hidden')){
18979             return true;
18980         }
18981         
18982         var v = this.getRawValue();
18983         
18984         if(this.multiple){
18985             v = this.getValue();
18986         }
18987         
18988         if(this.disabled || this.allowBlank || v.length){
18989             this.markValid();
18990             return true;
18991         }
18992         
18993         this.markInvalid();
18994         return false;
18995     },
18996     
18997     tickableInputEl : function()
18998     {
18999         if(!this.tickable || !this.editable){
19000             return this.inputEl();
19001         }
19002         
19003         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19004     },
19005     
19006     
19007     getAutoCreateTouchView : function()
19008     {
19009         var id = Roo.id();
19010         
19011         var cfg = {
19012             cls: 'form-group' //input-group
19013         };
19014         
19015         var input =  {
19016             tag: 'input',
19017             id : id,
19018             type : this.inputType,
19019             cls : 'form-control x-combo-noedit',
19020             autocomplete: 'new-password',
19021             placeholder : this.placeholder || '',
19022             readonly : true
19023         };
19024         
19025         if (this.name) {
19026             input.name = this.name;
19027         }
19028         
19029         if (this.size) {
19030             input.cls += ' input-' + this.size;
19031         }
19032         
19033         if (this.disabled) {
19034             input.disabled = true;
19035         }
19036         
19037         var inputblock = {
19038             cls : 'roo-combobox-wrap',
19039             cn : [
19040                 input
19041             ]
19042         };
19043         
19044         if(this.before){
19045             inputblock.cls += ' input-group';
19046             
19047             inputblock.cn.unshift({
19048                 tag :'span',
19049                 cls : 'input-group-addon input-group-prepend input-group-text',
19050                 html : this.before
19051             });
19052         }
19053         
19054         if(this.removable && !this.multiple){
19055             inputblock.cls += ' roo-removable';
19056             
19057             inputblock.cn.push({
19058                 tag: 'button',
19059                 html : 'x',
19060                 cls : 'roo-combo-removable-btn close'
19061             });
19062         }
19063
19064         if(this.hasFeedback && !this.allowBlank){
19065             
19066             inputblock.cls += ' has-feedback';
19067             
19068             inputblock.cn.push({
19069                 tag: 'span',
19070                 cls: 'glyphicon form-control-feedback'
19071             });
19072             
19073         }
19074         
19075         if (this.after) {
19076             
19077             inputblock.cls += (this.before) ? '' : ' input-group';
19078             
19079             inputblock.cn.push({
19080                 tag :'span',
19081                 cls : 'input-group-addon input-group-append input-group-text',
19082                 html : this.after
19083             });
19084         }
19085
19086         
19087         var ibwrap = inputblock;
19088         
19089         if(this.multiple){
19090             ibwrap = {
19091                 tag: 'ul',
19092                 cls: 'roo-select2-choices',
19093                 cn:[
19094                     {
19095                         tag: 'li',
19096                         cls: 'roo-select2-search-field',
19097                         cn: [
19098
19099                             inputblock
19100                         ]
19101                     }
19102                 ]
19103             };
19104         
19105             
19106         }
19107         
19108         var combobox = {
19109             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19110             cn: [
19111                 {
19112                     tag: 'input',
19113                     type : 'hidden',
19114                     cls: 'form-hidden-field'
19115                 },
19116                 ibwrap
19117             ]
19118         };
19119         
19120         if(!this.multiple && this.showToggleBtn){
19121             
19122             var caret = {
19123                 cls: 'caret'
19124             };
19125             
19126             if (this.caret != false) {
19127                 caret = {
19128                      tag: 'i',
19129                      cls: 'fa fa-' + this.caret
19130                 };
19131                 
19132             }
19133             
19134             combobox.cn.push({
19135                 tag :'span',
19136                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19137                 cn : [
19138                     Roo.bootstrap.version == 3 ? caret : '',
19139                     {
19140                         tag: 'span',
19141                         cls: 'combobox-clear',
19142                         cn  : [
19143                             {
19144                                 tag : 'i',
19145                                 cls: 'icon-remove'
19146                             }
19147                         ]
19148                     }
19149                 ]
19150
19151             })
19152         }
19153         
19154         if(this.multiple){
19155             combobox.cls += ' roo-select2-container-multi';
19156         }
19157         
19158         var required =  this.allowBlank ?  {
19159                     tag : 'i',
19160                     style: 'display: none'
19161                 } : {
19162                    tag : 'i',
19163                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19164                    tooltip : 'This field is required'
19165                 };
19166         
19167         var align = this.labelAlign || this.parentLabelAlign();
19168         
19169         if (align ==='left' && this.fieldLabel.length) {
19170
19171             cfg.cn = [
19172                 required,
19173                 {
19174                     tag: 'label',
19175                     cls : 'control-label col-form-label',
19176                     html : this.fieldLabel
19177
19178                 },
19179                 {
19180                     cls : 'roo-combobox-wrap ', 
19181                     cn: [
19182                         combobox
19183                     ]
19184                 }
19185             ];
19186             
19187             var labelCfg = cfg.cn[1];
19188             var contentCfg = cfg.cn[2];
19189             
19190
19191             if(this.indicatorpos == 'right'){
19192                 cfg.cn = [
19193                     {
19194                         tag: 'label',
19195                         'for' :  id,
19196                         cls : 'control-label col-form-label',
19197                         cn : [
19198                             {
19199                                 tag : 'span',
19200                                 html : this.fieldLabel
19201                             },
19202                             required
19203                         ]
19204                     },
19205                     {
19206                         cls : "roo-combobox-wrap ",
19207                         cn: [
19208                             combobox
19209                         ]
19210                     }
19211
19212                 ];
19213                 
19214                 labelCfg = cfg.cn[0];
19215                 contentCfg = cfg.cn[1];
19216             }
19217             
19218            
19219             
19220             if(this.labelWidth > 12){
19221                 labelCfg.style = "width: " + this.labelWidth + 'px';
19222             }
19223            
19224             if(this.labelWidth < 13 && this.labelmd == 0){
19225                 this.labelmd = this.labelWidth;
19226             }
19227             
19228             if(this.labellg > 0){
19229                 labelCfg.cls += ' col-lg-' + this.labellg;
19230                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19231             }
19232             
19233             if(this.labelmd > 0){
19234                 labelCfg.cls += ' col-md-' + this.labelmd;
19235                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19236             }
19237             
19238             if(this.labelsm > 0){
19239                 labelCfg.cls += ' col-sm-' + this.labelsm;
19240                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19241             }
19242             
19243             if(this.labelxs > 0){
19244                 labelCfg.cls += ' col-xs-' + this.labelxs;
19245                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19246             }
19247                 
19248                 
19249         } else if ( this.fieldLabel.length) {
19250             cfg.cn = [
19251                required,
19252                 {
19253                     tag: 'label',
19254                     cls : 'control-label',
19255                     html : this.fieldLabel
19256
19257                 },
19258                 {
19259                     cls : '', 
19260                     cn: [
19261                         combobox
19262                     ]
19263                 }
19264             ];
19265             
19266             if(this.indicatorpos == 'right'){
19267                 cfg.cn = [
19268                     {
19269                         tag: 'label',
19270                         cls : 'control-label',
19271                         html : this.fieldLabel,
19272                         cn : [
19273                             required
19274                         ]
19275                     },
19276                     {
19277                         cls : '', 
19278                         cn: [
19279                             combobox
19280                         ]
19281                     }
19282                 ];
19283             }
19284         } else {
19285             cfg.cn = combobox;    
19286         }
19287         
19288         
19289         var settings = this;
19290         
19291         ['xs','sm','md','lg'].map(function(size){
19292             if (settings[size]) {
19293                 cfg.cls += ' col-' + size + '-' + settings[size];
19294             }
19295         });
19296         
19297         return cfg;
19298     },
19299     
19300     initTouchView : function()
19301     {
19302         this.renderTouchView();
19303         
19304         this.touchViewEl.on('scroll', function(){
19305             this.el.dom.scrollTop = 0;
19306         }, this);
19307         
19308         this.originalValue = this.getValue();
19309         
19310         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19311         
19312         this.inputEl().on("click", this.showTouchView, this);
19313         if (this.triggerEl) {
19314             this.triggerEl.on("click", this.showTouchView, this);
19315         }
19316         
19317         
19318         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19319         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19320         
19321         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19322         
19323         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19324         this.store.on('load', this.onTouchViewLoad, this);
19325         this.store.on('loadexception', this.onTouchViewLoadException, this);
19326         
19327         if(this.hiddenName){
19328             
19329             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19330             
19331             this.hiddenField.dom.value =
19332                 this.hiddenValue !== undefined ? this.hiddenValue :
19333                 this.value !== undefined ? this.value : '';
19334         
19335             this.el.dom.removeAttribute('name');
19336             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19337         }
19338         
19339         if(this.multiple){
19340             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19341             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19342         }
19343         
19344         if(this.removable && !this.multiple){
19345             var close = this.closeTriggerEl();
19346             if(close){
19347                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19348                 close.on('click', this.removeBtnClick, this, close);
19349             }
19350         }
19351         /*
19352          * fix the bug in Safari iOS8
19353          */
19354         this.inputEl().on("focus", function(e){
19355             document.activeElement.blur();
19356         }, this);
19357         
19358         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19359         
19360         return;
19361         
19362         
19363     },
19364     
19365     renderTouchView : function()
19366     {
19367         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19368         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19371         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         
19373         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19374         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375         this.touchViewBodyEl.setStyle('overflow', 'auto');
19376         
19377         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19378         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19381         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19382         
19383     },
19384     
19385     showTouchView : function()
19386     {
19387         if(this.disabled){
19388             return;
19389         }
19390         
19391         this.touchViewHeaderEl.hide();
19392
19393         if(this.modalTitle.length){
19394             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19395             this.touchViewHeaderEl.show();
19396         }
19397
19398         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19399         this.touchViewEl.show();
19400
19401         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19402         
19403         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19404         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19405
19406         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19407
19408         if(this.modalTitle.length){
19409             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19410         }
19411         
19412         this.touchViewBodyEl.setHeight(bodyHeight);
19413
19414         if(this.animate){
19415             var _this = this;
19416             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19417         }else{
19418             this.touchViewEl.addClass(['in','show']);
19419         }
19420         
19421         if(this._touchViewMask){
19422             Roo.get(document.body).addClass("x-body-masked");
19423             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19424             this._touchViewMask.setStyle('z-index', 10000);
19425             this._touchViewMask.addClass('show');
19426         }
19427         
19428         this.doTouchViewQuery();
19429         
19430     },
19431     
19432     hideTouchView : function()
19433     {
19434         this.touchViewEl.removeClass(['in','show']);
19435
19436         if(this.animate){
19437             var _this = this;
19438             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19439         }else{
19440             this.touchViewEl.setStyle('display', 'none');
19441         }
19442         
19443         if(this._touchViewMask){
19444             this._touchViewMask.removeClass('show');
19445             Roo.get(document.body).removeClass("x-body-masked");
19446         }
19447     },
19448     
19449     setTouchViewValue : function()
19450     {
19451         if(this.multiple){
19452             this.clearItem();
19453         
19454             var _this = this;
19455
19456             Roo.each(this.tickItems, function(o){
19457                 this.addItem(o);
19458             }, this);
19459         }
19460         
19461         this.hideTouchView();
19462     },
19463     
19464     doTouchViewQuery : function()
19465     {
19466         var qe = {
19467             query: '',
19468             forceAll: true,
19469             combo: this,
19470             cancel:false
19471         };
19472         
19473         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19474             return false;
19475         }
19476         
19477         if(!this.alwaysQuery || this.mode == 'local'){
19478             this.onTouchViewLoad();
19479             return;
19480         }
19481         
19482         this.store.load();
19483     },
19484     
19485     onTouchViewBeforeLoad : function(combo,opts)
19486     {
19487         return;
19488     },
19489
19490     // private
19491     onTouchViewLoad : function()
19492     {
19493         if(this.store.getCount() < 1){
19494             this.onTouchViewEmptyResults();
19495             return;
19496         }
19497         
19498         this.clearTouchView();
19499         
19500         var rawValue = this.getRawValue();
19501         
19502         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19503         
19504         this.tickItems = [];
19505         
19506         this.store.data.each(function(d, rowIndex){
19507             var row = this.touchViewListGroup.createChild(template);
19508             
19509             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19510                 row.addClass(d.data.cls);
19511             }
19512             
19513             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19514                 var cfg = {
19515                     data : d.data,
19516                     html : d.data[this.displayField]
19517                 };
19518                 
19519                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19520                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19521                 }
19522             }
19523             row.removeClass('selected');
19524             if(!this.multiple && this.valueField &&
19525                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19526             {
19527                 // radio buttons..
19528                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19529                 row.addClass('selected');
19530             }
19531             
19532             if(this.multiple && this.valueField &&
19533                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19534             {
19535                 
19536                 // checkboxes...
19537                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19538                 this.tickItems.push(d.data);
19539             }
19540             
19541             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19542             
19543         }, this);
19544         
19545         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19546         
19547         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19548
19549         if(this.modalTitle.length){
19550             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19551         }
19552
19553         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19554         
19555         if(this.mobile_restrict_height && listHeight < bodyHeight){
19556             this.touchViewBodyEl.setHeight(listHeight);
19557         }
19558         
19559         var _this = this;
19560         
19561         if(firstChecked && listHeight > bodyHeight){
19562             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19563         }
19564         
19565     },
19566     
19567     onTouchViewLoadException : function()
19568     {
19569         this.hideTouchView();
19570     },
19571     
19572     onTouchViewEmptyResults : function()
19573     {
19574         this.clearTouchView();
19575         
19576         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19577         
19578         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19579         
19580     },
19581     
19582     clearTouchView : function()
19583     {
19584         this.touchViewListGroup.dom.innerHTML = '';
19585     },
19586     
19587     onTouchViewClick : function(e, el, o)
19588     {
19589         e.preventDefault();
19590         
19591         var row = o.row;
19592         var rowIndex = o.rowIndex;
19593         
19594         var r = this.store.getAt(rowIndex);
19595         
19596         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19597             
19598             if(!this.multiple){
19599                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19600                     c.dom.removeAttribute('checked');
19601                 }, this);
19602
19603                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19604
19605                 this.setFromData(r.data);
19606
19607                 var close = this.closeTriggerEl();
19608
19609                 if(close){
19610                     close.show();
19611                 }
19612
19613                 this.hideTouchView();
19614
19615                 this.fireEvent('select', this, r, rowIndex);
19616
19617                 return;
19618             }
19619
19620             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19621                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19622                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19623                 return;
19624             }
19625
19626             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19627             this.addItem(r.data);
19628             this.tickItems.push(r.data);
19629         }
19630     },
19631     
19632     getAutoCreateNativeIOS : function()
19633     {
19634         var cfg = {
19635             cls: 'form-group' //input-group,
19636         };
19637         
19638         var combobox =  {
19639             tag: 'select',
19640             cls : 'roo-ios-select'
19641         };
19642         
19643         if (this.name) {
19644             combobox.name = this.name;
19645         }
19646         
19647         if (this.disabled) {
19648             combobox.disabled = true;
19649         }
19650         
19651         var settings = this;
19652         
19653         ['xs','sm','md','lg'].map(function(size){
19654             if (settings[size]) {
19655                 cfg.cls += ' col-' + size + '-' + settings[size];
19656             }
19657         });
19658         
19659         cfg.cn = combobox;
19660         
19661         return cfg;
19662         
19663     },
19664     
19665     initIOSView : function()
19666     {
19667         this.store.on('load', this.onIOSViewLoad, this);
19668         
19669         return;
19670     },
19671     
19672     onIOSViewLoad : function()
19673     {
19674         if(this.store.getCount() < 1){
19675             return;
19676         }
19677         
19678         this.clearIOSView();
19679         
19680         if(this.allowBlank) {
19681             
19682             var default_text = '-- SELECT --';
19683             
19684             if(this.placeholder.length){
19685                 default_text = this.placeholder;
19686             }
19687             
19688             if(this.emptyTitle.length){
19689                 default_text += ' - ' + this.emptyTitle + ' -';
19690             }
19691             
19692             var opt = this.inputEl().createChild({
19693                 tag: 'option',
19694                 value : 0,
19695                 html : default_text
19696             });
19697             
19698             var o = {};
19699             o[this.valueField] = 0;
19700             o[this.displayField] = default_text;
19701             
19702             this.ios_options.push({
19703                 data : o,
19704                 el : opt
19705             });
19706             
19707         }
19708         
19709         this.store.data.each(function(d, rowIndex){
19710             
19711             var html = '';
19712             
19713             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19714                 html = d.data[this.displayField];
19715             }
19716             
19717             var value = '';
19718             
19719             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19720                 value = d.data[this.valueField];
19721             }
19722             
19723             var option = {
19724                 tag: 'option',
19725                 value : value,
19726                 html : html
19727             };
19728             
19729             if(this.value == d.data[this.valueField]){
19730                 option['selected'] = true;
19731             }
19732             
19733             var opt = this.inputEl().createChild(option);
19734             
19735             this.ios_options.push({
19736                 data : d.data,
19737                 el : opt
19738             });
19739             
19740         }, this);
19741         
19742         this.inputEl().on('change', function(){
19743            this.fireEvent('select', this);
19744         }, this);
19745         
19746     },
19747     
19748     clearIOSView: function()
19749     {
19750         this.inputEl().dom.innerHTML = '';
19751         
19752         this.ios_options = [];
19753     },
19754     
19755     setIOSValue: function(v)
19756     {
19757         this.value = v;
19758         
19759         if(!this.ios_options){
19760             return;
19761         }
19762         
19763         Roo.each(this.ios_options, function(opts){
19764            
19765            opts.el.dom.removeAttribute('selected');
19766            
19767            if(opts.data[this.valueField] != v){
19768                return;
19769            }
19770            
19771            opts.el.dom.setAttribute('selected', true);
19772            
19773         }, this);
19774     }
19775
19776     /** 
19777     * @cfg {Boolean} grow 
19778     * @hide 
19779     */
19780     /** 
19781     * @cfg {Number} growMin 
19782     * @hide 
19783     */
19784     /** 
19785     * @cfg {Number} growMax 
19786     * @hide 
19787     */
19788     /**
19789      * @hide
19790      * @method autoSize
19791      */
19792 });
19793
19794 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19795     
19796     header : {
19797         tag: 'div',
19798         cls: 'modal-header',
19799         cn: [
19800             {
19801                 tag: 'h4',
19802                 cls: 'modal-title'
19803             }
19804         ]
19805     },
19806     
19807     body : {
19808         tag: 'div',
19809         cls: 'modal-body',
19810         cn: [
19811             {
19812                 tag: 'ul',
19813                 cls: 'list-group'
19814             }
19815         ]
19816     },
19817     
19818     listItemRadio : {
19819         tag: 'li',
19820         cls: 'list-group-item',
19821         cn: [
19822             {
19823                 tag: 'span',
19824                 cls: 'roo-combobox-list-group-item-value'
19825             },
19826             {
19827                 tag: 'div',
19828                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19829                 cn: [
19830                     {
19831                         tag: 'input',
19832                         type: 'radio'
19833                     },
19834                     {
19835                         tag: 'label'
19836                     }
19837                 ]
19838             }
19839         ]
19840     },
19841     
19842     listItemCheckbox : {
19843         tag: 'li',
19844         cls: 'list-group-item',
19845         cn: [
19846             {
19847                 tag: 'span',
19848                 cls: 'roo-combobox-list-group-item-value'
19849             },
19850             {
19851                 tag: 'div',
19852                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19853                 cn: [
19854                     {
19855                         tag: 'input',
19856                         type: 'checkbox'
19857                     },
19858                     {
19859                         tag: 'label'
19860                     }
19861                 ]
19862             }
19863         ]
19864     },
19865     
19866     emptyResult : {
19867         tag: 'div',
19868         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19869     },
19870     
19871     footer : {
19872         tag: 'div',
19873         cls: 'modal-footer',
19874         cn: [
19875             {
19876                 tag: 'div',
19877                 cls: 'row',
19878                 cn: [
19879                     {
19880                         tag: 'div',
19881                         cls: 'col-xs-6 text-left',
19882                         cn: {
19883                             tag: 'button',
19884                             cls: 'btn btn-danger roo-touch-view-cancel',
19885                             html: 'Cancel'
19886                         }
19887                     },
19888                     {
19889                         tag: 'div',
19890                         cls: 'col-xs-6 text-right',
19891                         cn: {
19892                             tag: 'button',
19893                             cls: 'btn btn-success roo-touch-view-ok',
19894                             html: 'OK'
19895                         }
19896                     }
19897                 ]
19898             }
19899         ]
19900         
19901     }
19902 });
19903
19904 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19905     
19906     touchViewTemplate : {
19907         tag: 'div',
19908         cls: 'modal fade roo-combobox-touch-view',
19909         cn: [
19910             {
19911                 tag: 'div',
19912                 cls: 'modal-dialog',
19913                 style : 'position:fixed', // we have to fix position....
19914                 cn: [
19915                     {
19916                         tag: 'div',
19917                         cls: 'modal-content',
19918                         cn: [
19919                             Roo.bootstrap.form.ComboBox.header,
19920                             Roo.bootstrap.form.ComboBox.body,
19921                             Roo.bootstrap.form.ComboBox.footer
19922                         ]
19923                     }
19924                 ]
19925             }
19926         ]
19927     }
19928 });/*
19929  * Based on:
19930  * Ext JS Library 1.1.1
19931  * Copyright(c) 2006-2007, Ext JS, LLC.
19932  *
19933  * Originally Released Under LGPL - original licence link has changed is not relivant.
19934  *
19935  * Fork - LGPL
19936  * <script type="text/javascript">
19937  */
19938
19939 /**
19940  * @class Roo.View
19941  * @extends Roo.util.Observable
19942  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19943  * This class also supports single and multi selection modes. <br>
19944  * Create a data model bound view:
19945  <pre><code>
19946  var store = new Roo.data.Store(...);
19947
19948  var view = new Roo.View({
19949     el : "my-element",
19950     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19951  
19952     singleSelect: true,
19953     selectedClass: "ydataview-selected",
19954     store: store
19955  });
19956
19957  // listen for node click?
19958  view.on("click", function(vw, index, node, e){
19959  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19960  });
19961
19962  // load XML data
19963  dataModel.load("foobar.xml");
19964  </code></pre>
19965  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19966  * <br><br>
19967  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19968  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19969  * 
19970  * Note: old style constructor is still suported (container, template, config)
19971  * 
19972  * @constructor
19973  * Create a new View
19974  * @param {Object} config The config object
19975  * 
19976  */
19977 Roo.View = function(config, depreciated_tpl, depreciated_config){
19978     
19979     this.parent = false;
19980     
19981     if (typeof(depreciated_tpl) == 'undefined') {
19982         // new way.. - universal constructor.
19983         Roo.apply(this, config);
19984         this.el  = Roo.get(this.el);
19985     } else {
19986         // old format..
19987         this.el  = Roo.get(config);
19988         this.tpl = depreciated_tpl;
19989         Roo.apply(this, depreciated_config);
19990     }
19991     this.wrapEl  = this.el.wrap().wrap();
19992     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19993     
19994     
19995     if(typeof(this.tpl) == "string"){
19996         this.tpl = new Roo.Template(this.tpl);
19997     } else {
19998         // support xtype ctors..
19999         this.tpl = new Roo.factory(this.tpl, Roo);
20000     }
20001     
20002     
20003     this.tpl.compile();
20004     
20005     /** @private */
20006     this.addEvents({
20007         /**
20008          * @event beforeclick
20009          * Fires before a click is processed. Returns false to cancel the default action.
20010          * @param {Roo.View} this
20011          * @param {Number} index The index of the target node
20012          * @param {HTMLElement} node The target node
20013          * @param {Roo.EventObject} e The raw event object
20014          */
20015             "beforeclick" : true,
20016         /**
20017          * @event click
20018          * Fires when a template node is clicked.
20019          * @param {Roo.View} this
20020          * @param {Number} index The index of the target node
20021          * @param {HTMLElement} node The target node
20022          * @param {Roo.EventObject} e The raw event object
20023          */
20024             "click" : true,
20025         /**
20026          * @event dblclick
20027          * Fires when a template node is double clicked.
20028          * @param {Roo.View} this
20029          * @param {Number} index The index of the target node
20030          * @param {HTMLElement} node The target node
20031          * @param {Roo.EventObject} e The raw event object
20032          */
20033             "dblclick" : true,
20034         /**
20035          * @event contextmenu
20036          * Fires when a template node is right clicked.
20037          * @param {Roo.View} this
20038          * @param {Number} index The index of the target node
20039          * @param {HTMLElement} node The target node
20040          * @param {Roo.EventObject} e The raw event object
20041          */
20042             "contextmenu" : true,
20043         /**
20044          * @event selectionchange
20045          * Fires when the selected nodes change.
20046          * @param {Roo.View} this
20047          * @param {Array} selections Array of the selected nodes
20048          */
20049             "selectionchange" : true,
20050     
20051         /**
20052          * @event beforeselect
20053          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20054          * @param {Roo.View} this
20055          * @param {HTMLElement} node The node to be selected
20056          * @param {Array} selections Array of currently selected nodes
20057          */
20058             "beforeselect" : true,
20059         /**
20060          * @event preparedata
20061          * Fires on every row to render, to allow you to change the data.
20062          * @param {Roo.View} this
20063          * @param {Object} data to be rendered (change this)
20064          */
20065           "preparedata" : true
20066           
20067           
20068         });
20069
20070
20071
20072     this.el.on({
20073         "click": this.onClick,
20074         "dblclick": this.onDblClick,
20075         "contextmenu": this.onContextMenu,
20076         scope:this
20077     });
20078
20079     this.selections = [];
20080     this.nodes = [];
20081     this.cmp = new Roo.CompositeElementLite([]);
20082     if(this.store){
20083         this.store = Roo.factory(this.store, Roo.data);
20084         this.setStore(this.store, true);
20085     }
20086     
20087     if ( this.footer && this.footer.xtype) {
20088            
20089          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20090         
20091         this.footer.dataSource = this.store;
20092         this.footer.container = fctr;
20093         this.footer = Roo.factory(this.footer, Roo);
20094         fctr.insertFirst(this.el);
20095         
20096         // this is a bit insane - as the paging toolbar seems to detach the el..
20097 //        dom.parentNode.parentNode.parentNode
20098          // they get detached?
20099     }
20100     
20101     
20102     Roo.View.superclass.constructor.call(this);
20103     
20104     
20105 };
20106
20107 Roo.extend(Roo.View, Roo.util.Observable, {
20108     
20109      /**
20110      * @cfg {Roo.data.Store} store Data store to load data from.
20111      */
20112     store : false,
20113     
20114     /**
20115      * @cfg {String|Roo.Element} el The container element.
20116      */
20117     el : '',
20118     
20119     /**
20120      * @cfg {String|Roo.Template} tpl The template used by this View 
20121      */
20122     tpl : false,
20123     /**
20124      * @cfg {String} dataName the named area of the template to use as the data area
20125      *                          Works with domtemplates roo-name="name"
20126      */
20127     dataName: false,
20128     /**
20129      * @cfg {String} selectedClass The css class to add to selected nodes
20130      */
20131     selectedClass : "x-view-selected",
20132      /**
20133      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20134      */
20135     emptyText : "",
20136     
20137     /**
20138      * @cfg {String} text to display on mask (default Loading)
20139      */
20140     mask : false,
20141     /**
20142      * @cfg {Boolean} multiSelect Allow multiple selection
20143      */
20144     multiSelect : false,
20145     /**
20146      * @cfg {Boolean} singleSelect Allow single selection
20147      */
20148     singleSelect:  false,
20149     
20150     /**
20151      * @cfg {Boolean} toggleSelect - selecting 
20152      */
20153     toggleSelect : false,
20154     
20155     /**
20156      * @cfg {Boolean} tickable - selecting 
20157      */
20158     tickable : false,
20159     
20160     /**
20161      * Returns the element this view is bound to.
20162      * @return {Roo.Element}
20163      */
20164     getEl : function(){
20165         return this.wrapEl;
20166     },
20167     
20168     
20169
20170     /**
20171      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20172      */
20173     refresh : function(){
20174         //Roo.log('refresh');
20175         var t = this.tpl;
20176         
20177         // if we are using something like 'domtemplate', then
20178         // the what gets used is:
20179         // t.applySubtemplate(NAME, data, wrapping data..)
20180         // the outer template then get' applied with
20181         //     the store 'extra data'
20182         // and the body get's added to the
20183         //      roo-name="data" node?
20184         //      <span class='roo-tpl-{name}'></span> ?????
20185         
20186         
20187         
20188         this.clearSelections();
20189         this.el.update("");
20190         var html = [];
20191         var records = this.store.getRange();
20192         if(records.length < 1) {
20193             
20194             // is this valid??  = should it render a template??
20195             
20196             this.el.update(this.emptyText);
20197             return;
20198         }
20199         var el = this.el;
20200         if (this.dataName) {
20201             this.el.update(t.apply(this.store.meta)); //????
20202             el = this.el.child('.roo-tpl-' + this.dataName);
20203         }
20204         
20205         for(var i = 0, len = records.length; i < len; i++){
20206             var data = this.prepareData(records[i].data, i, records[i]);
20207             this.fireEvent("preparedata", this, data, i, records[i]);
20208             
20209             var d = Roo.apply({}, data);
20210             
20211             if(this.tickable){
20212                 Roo.apply(d, {'roo-id' : Roo.id()});
20213                 
20214                 var _this = this;
20215             
20216                 Roo.each(this.parent.item, function(item){
20217                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20218                         return;
20219                     }
20220                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20221                 });
20222             }
20223             
20224             html[html.length] = Roo.util.Format.trim(
20225                 this.dataName ?
20226                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20227                     t.apply(d)
20228             );
20229         }
20230         
20231         
20232         
20233         el.update(html.join(""));
20234         this.nodes = el.dom.childNodes;
20235         this.updateIndexes(0);
20236     },
20237     
20238
20239     /**
20240      * Function to override to reformat the data that is sent to
20241      * the template for each node.
20242      * DEPRICATED - use the preparedata event handler.
20243      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20244      * a JSON object for an UpdateManager bound view).
20245      */
20246     prepareData : function(data, index, record)
20247     {
20248         this.fireEvent("preparedata", this, data, index, record);
20249         return data;
20250     },
20251
20252     onUpdate : function(ds, record){
20253         // Roo.log('on update');   
20254         this.clearSelections();
20255         var index = this.store.indexOf(record);
20256         var n = this.nodes[index];
20257         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20258         n.parentNode.removeChild(n);
20259         this.updateIndexes(index, index);
20260     },
20261
20262     
20263     
20264 // --------- FIXME     
20265     onAdd : function(ds, records, index)
20266     {
20267         //Roo.log(['on Add', ds, records, index] );        
20268         this.clearSelections();
20269         if(this.nodes.length == 0){
20270             this.refresh();
20271             return;
20272         }
20273         var n = this.nodes[index];
20274         for(var i = 0, len = records.length; i < len; i++){
20275             var d = this.prepareData(records[i].data, i, records[i]);
20276             if(n){
20277                 this.tpl.insertBefore(n, d);
20278             }else{
20279                 
20280                 this.tpl.append(this.el, d);
20281             }
20282         }
20283         this.updateIndexes(index);
20284     },
20285
20286     onRemove : function(ds, record, index){
20287        // Roo.log('onRemove');
20288         this.clearSelections();
20289         var el = this.dataName  ?
20290             this.el.child('.roo-tpl-' + this.dataName) :
20291             this.el; 
20292         
20293         el.dom.removeChild(this.nodes[index]);
20294         this.updateIndexes(index);
20295     },
20296
20297     /**
20298      * Refresh an individual node.
20299      * @param {Number} index
20300      */
20301     refreshNode : function(index){
20302         this.onUpdate(this.store, this.store.getAt(index));
20303     },
20304
20305     updateIndexes : function(startIndex, endIndex){
20306         var ns = this.nodes;
20307         startIndex = startIndex || 0;
20308         endIndex = endIndex || ns.length - 1;
20309         for(var i = startIndex; i <= endIndex; i++){
20310             ns[i].nodeIndex = i;
20311         }
20312     },
20313
20314     /**
20315      * Changes the data store this view uses and refresh the view.
20316      * @param {Store} store
20317      */
20318     setStore : function(store, initial){
20319         if(!initial && this.store){
20320             this.store.un("datachanged", this.refresh);
20321             this.store.un("add", this.onAdd);
20322             this.store.un("remove", this.onRemove);
20323             this.store.un("update", this.onUpdate);
20324             this.store.un("clear", this.refresh);
20325             this.store.un("beforeload", this.onBeforeLoad);
20326             this.store.un("load", this.onLoad);
20327             this.store.un("loadexception", this.onLoad);
20328         }
20329         if(store){
20330           
20331             store.on("datachanged", this.refresh, this);
20332             store.on("add", this.onAdd, this);
20333             store.on("remove", this.onRemove, this);
20334             store.on("update", this.onUpdate, this);
20335             store.on("clear", this.refresh, this);
20336             store.on("beforeload", this.onBeforeLoad, this);
20337             store.on("load", this.onLoad, this);
20338             store.on("loadexception", this.onLoad, this);
20339         }
20340         
20341         if(store){
20342             this.refresh();
20343         }
20344     },
20345     /**
20346      * onbeforeLoad - masks the loading area.
20347      *
20348      */
20349     onBeforeLoad : function(store,opts)
20350     {
20351          //Roo.log('onBeforeLoad');   
20352         if (!opts.add) {
20353             this.el.update("");
20354         }
20355         this.el.mask(this.mask ? this.mask : "Loading" ); 
20356     },
20357     onLoad : function ()
20358     {
20359         this.el.unmask();
20360     },
20361     
20362
20363     /**
20364      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20365      * @param {HTMLElement} node
20366      * @return {HTMLElement} The template node
20367      */
20368     findItemFromChild : function(node){
20369         var el = this.dataName  ?
20370             this.el.child('.roo-tpl-' + this.dataName,true) :
20371             this.el.dom; 
20372         
20373         if(!node || node.parentNode == el){
20374                     return node;
20375             }
20376             var p = node.parentNode;
20377             while(p && p != el){
20378             if(p.parentNode == el){
20379                 return p;
20380             }
20381             p = p.parentNode;
20382         }
20383             return null;
20384     },
20385
20386     /** @ignore */
20387     onClick : function(e){
20388         var item = this.findItemFromChild(e.getTarget());
20389         if(item){
20390             var index = this.indexOf(item);
20391             if(this.onItemClick(item, index, e) !== false){
20392                 this.fireEvent("click", this, index, item, e);
20393             }
20394         }else{
20395             this.clearSelections();
20396         }
20397     },
20398
20399     /** @ignore */
20400     onContextMenu : function(e){
20401         var item = this.findItemFromChild(e.getTarget());
20402         if(item){
20403             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20404         }
20405     },
20406
20407     /** @ignore */
20408     onDblClick : function(e){
20409         var item = this.findItemFromChild(e.getTarget());
20410         if(item){
20411             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20412         }
20413     },
20414
20415     onItemClick : function(item, index, e)
20416     {
20417         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20418             return false;
20419         }
20420         if (this.toggleSelect) {
20421             var m = this.isSelected(item) ? 'unselect' : 'select';
20422             //Roo.log(m);
20423             var _t = this;
20424             _t[m](item, true, false);
20425             return true;
20426         }
20427         if(this.multiSelect || this.singleSelect){
20428             if(this.multiSelect && e.shiftKey && this.lastSelection){
20429                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20430             }else{
20431                 this.select(item, this.multiSelect && e.ctrlKey);
20432                 this.lastSelection = item;
20433             }
20434             
20435             if(!this.tickable){
20436                 e.preventDefault();
20437             }
20438             
20439         }
20440         return true;
20441     },
20442
20443     /**
20444      * Get the number of selected nodes.
20445      * @return {Number}
20446      */
20447     getSelectionCount : function(){
20448         return this.selections.length;
20449     },
20450
20451     /**
20452      * Get the currently selected nodes.
20453      * @return {Array} An array of HTMLElements
20454      */
20455     getSelectedNodes : function(){
20456         return this.selections;
20457     },
20458
20459     /**
20460      * Get the indexes of the selected nodes.
20461      * @return {Array}
20462      */
20463     getSelectedIndexes : function(){
20464         var indexes = [], s = this.selections;
20465         for(var i = 0, len = s.length; i < len; i++){
20466             indexes.push(s[i].nodeIndex);
20467         }
20468         return indexes;
20469     },
20470
20471     /**
20472      * Clear all selections
20473      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20474      */
20475     clearSelections : function(suppressEvent){
20476         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20477             this.cmp.elements = this.selections;
20478             this.cmp.removeClass(this.selectedClass);
20479             this.selections = [];
20480             if(!suppressEvent){
20481                 this.fireEvent("selectionchange", this, this.selections);
20482             }
20483         }
20484     },
20485
20486     /**
20487      * Returns true if the passed node is selected
20488      * @param {HTMLElement/Number} node The node or node index
20489      * @return {Boolean}
20490      */
20491     isSelected : function(node){
20492         var s = this.selections;
20493         if(s.length < 1){
20494             return false;
20495         }
20496         node = this.getNode(node);
20497         return s.indexOf(node) !== -1;
20498     },
20499
20500     /**
20501      * Selects nodes.
20502      * @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
20503      * @param {Boolean} keepExisting (optional) true to keep existing selections
20504      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20505      */
20506     select : function(nodeInfo, keepExisting, suppressEvent){
20507         if(nodeInfo instanceof Array){
20508             if(!keepExisting){
20509                 this.clearSelections(true);
20510             }
20511             for(var i = 0, len = nodeInfo.length; i < len; i++){
20512                 this.select(nodeInfo[i], true, true);
20513             }
20514             return;
20515         } 
20516         var node = this.getNode(nodeInfo);
20517         if(!node || this.isSelected(node)){
20518             return; // already selected.
20519         }
20520         if(!keepExisting){
20521             this.clearSelections(true);
20522         }
20523         
20524         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20525             Roo.fly(node).addClass(this.selectedClass);
20526             this.selections.push(node);
20527             if(!suppressEvent){
20528                 this.fireEvent("selectionchange", this, this.selections);
20529             }
20530         }
20531         
20532         
20533     },
20534       /**
20535      * Unselects nodes.
20536      * @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
20537      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20538      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20539      */
20540     unselect : function(nodeInfo, keepExisting, suppressEvent)
20541     {
20542         if(nodeInfo instanceof Array){
20543             Roo.each(this.selections, function(s) {
20544                 this.unselect(s, nodeInfo);
20545             }, this);
20546             return;
20547         }
20548         var node = this.getNode(nodeInfo);
20549         if(!node || !this.isSelected(node)){
20550             //Roo.log("not selected");
20551             return; // not selected.
20552         }
20553         // fireevent???
20554         var ns = [];
20555         Roo.each(this.selections, function(s) {
20556             if (s == node ) {
20557                 Roo.fly(node).removeClass(this.selectedClass);
20558
20559                 return;
20560             }
20561             ns.push(s);
20562         },this);
20563         
20564         this.selections= ns;
20565         this.fireEvent("selectionchange", this, this.selections);
20566     },
20567
20568     /**
20569      * Gets a template node.
20570      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20571      * @return {HTMLElement} The node or null if it wasn't found
20572      */
20573     getNode : function(nodeInfo){
20574         if(typeof nodeInfo == "string"){
20575             return document.getElementById(nodeInfo);
20576         }else if(typeof nodeInfo == "number"){
20577             return this.nodes[nodeInfo];
20578         }
20579         return nodeInfo;
20580     },
20581
20582     /**
20583      * Gets a range template nodes.
20584      * @param {Number} startIndex
20585      * @param {Number} endIndex
20586      * @return {Array} An array of nodes
20587      */
20588     getNodes : function(start, end){
20589         var ns = this.nodes;
20590         start = start || 0;
20591         end = typeof end == "undefined" ? ns.length - 1 : end;
20592         var nodes = [];
20593         if(start <= end){
20594             for(var i = start; i <= end; i++){
20595                 nodes.push(ns[i]);
20596             }
20597         } else{
20598             for(var i = start; i >= end; i--){
20599                 nodes.push(ns[i]);
20600             }
20601         }
20602         return nodes;
20603     },
20604
20605     /**
20606      * Finds the index of the passed node
20607      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20608      * @return {Number} The index of the node or -1
20609      */
20610     indexOf : function(node){
20611         node = this.getNode(node);
20612         if(typeof node.nodeIndex == "number"){
20613             return node.nodeIndex;
20614         }
20615         var ns = this.nodes;
20616         for(var i = 0, len = ns.length; i < len; i++){
20617             if(ns[i] == node){
20618                 return i;
20619             }
20620         }
20621         return -1;
20622     }
20623 });
20624 /*
20625  * - LGPL
20626  *
20627  * based on jquery fullcalendar
20628  * 
20629  */
20630
20631 Roo.bootstrap = Roo.bootstrap || {};
20632 /**
20633  * @class Roo.bootstrap.Calendar
20634  * @extends Roo.bootstrap.Component
20635  * Bootstrap Calendar class
20636  * @cfg {Boolean} loadMask (true|false) default false
20637  * @cfg {Object} header generate the user specific header of the calendar, default false
20638
20639  * @constructor
20640  * Create a new Container
20641  * @param {Object} config The config object
20642  */
20643
20644
20645
20646 Roo.bootstrap.Calendar = function(config){
20647     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20648      this.addEvents({
20649         /**
20650              * @event select
20651              * Fires when a date is selected
20652              * @param {DatePicker} this
20653              * @param {Date} date The selected date
20654              */
20655         'select': true,
20656         /**
20657              * @event monthchange
20658              * Fires when the displayed month changes 
20659              * @param {DatePicker} this
20660              * @param {Date} date The selected month
20661              */
20662         'monthchange': true,
20663         /**
20664              * @event evententer
20665              * Fires when mouse over an event
20666              * @param {Calendar} this
20667              * @param {event} Event
20668              */
20669         'evententer': true,
20670         /**
20671              * @event eventleave
20672              * Fires when the mouse leaves an
20673              * @param {Calendar} this
20674              * @param {event}
20675              */
20676         'eventleave': true,
20677         /**
20678              * @event eventclick
20679              * Fires when the mouse click an
20680              * @param {Calendar} this
20681              * @param {event}
20682              */
20683         'eventclick': true
20684         
20685     });
20686
20687 };
20688
20689 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20690     
20691           /**
20692      * @cfg {Roo.data.Store} store
20693      * The data source for the calendar
20694      */
20695         store : false,
20696      /**
20697      * @cfg {Number} startDay
20698      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20699      */
20700     startDay : 0,
20701     
20702     loadMask : false,
20703     
20704     header : false,
20705       
20706     getAutoCreate : function(){
20707         
20708         
20709         var fc_button = function(name, corner, style, content ) {
20710             return Roo.apply({},{
20711                 tag : 'span',
20712                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20713                          (corner.length ?
20714                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20715                             ''
20716                         ),
20717                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20718                 unselectable: 'on'
20719             });
20720         };
20721         
20722         var header = {};
20723         
20724         if(!this.header){
20725             header = {
20726                 tag : 'table',
20727                 cls : 'fc-header',
20728                 style : 'width:100%',
20729                 cn : [
20730                     {
20731                         tag: 'tr',
20732                         cn : [
20733                             {
20734                                 tag : 'td',
20735                                 cls : 'fc-header-left',
20736                                 cn : [
20737                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20738                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20739                                     { tag: 'span', cls: 'fc-header-space' },
20740                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20741
20742
20743                                 ]
20744                             },
20745
20746                             {
20747                                 tag : 'td',
20748                                 cls : 'fc-header-center',
20749                                 cn : [
20750                                     {
20751                                         tag: 'span',
20752                                         cls: 'fc-header-title',
20753                                         cn : {
20754                                             tag: 'H2',
20755                                             html : 'month / year'
20756                                         }
20757                                     }
20758
20759                                 ]
20760                             },
20761                             {
20762                                 tag : 'td',
20763                                 cls : 'fc-header-right',
20764                                 cn : [
20765                               /*      fc_button('month', 'left', '', 'month' ),
20766                                     fc_button('week', '', '', 'week' ),
20767                                     fc_button('day', 'right', '', 'day' )
20768                                 */    
20769
20770                                 ]
20771                             }
20772
20773                         ]
20774                     }
20775                 ]
20776             };
20777         }
20778         
20779         header = this.header;
20780         
20781        
20782         var cal_heads = function() {
20783             var ret = [];
20784             // fixme - handle this.
20785             
20786             for (var i =0; i < Date.dayNames.length; i++) {
20787                 var d = Date.dayNames[i];
20788                 ret.push({
20789                     tag: 'th',
20790                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20791                     html : d.substring(0,3)
20792                 });
20793                 
20794             }
20795             ret[0].cls += ' fc-first';
20796             ret[6].cls += ' fc-last';
20797             return ret;
20798         };
20799         var cal_cell = function(n) {
20800             return  {
20801                 tag: 'td',
20802                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20803                 cn : [
20804                     {
20805                         cn : [
20806                             {
20807                                 cls: 'fc-day-number',
20808                                 html: 'D'
20809                             },
20810                             {
20811                                 cls: 'fc-day-content',
20812                              
20813                                 cn : [
20814                                      {
20815                                         style: 'position: relative;' // height: 17px;
20816                                     }
20817                                 ]
20818                             }
20819                             
20820                             
20821                         ]
20822                     }
20823                 ]
20824                 
20825             }
20826         };
20827         var cal_rows = function() {
20828             
20829             var ret = [];
20830             for (var r = 0; r < 6; r++) {
20831                 var row= {
20832                     tag : 'tr',
20833                     cls : 'fc-week',
20834                     cn : []
20835                 };
20836                 
20837                 for (var i =0; i < Date.dayNames.length; i++) {
20838                     var d = Date.dayNames[i];
20839                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20840
20841                 }
20842                 row.cn[0].cls+=' fc-first';
20843                 row.cn[0].cn[0].style = 'min-height:90px';
20844                 row.cn[6].cls+=' fc-last';
20845                 ret.push(row);
20846                 
20847             }
20848             ret[0].cls += ' fc-first';
20849             ret[4].cls += ' fc-prev-last';
20850             ret[5].cls += ' fc-last';
20851             return ret;
20852             
20853         };
20854         
20855         var cal_table = {
20856             tag: 'table',
20857             cls: 'fc-border-separate',
20858             style : 'width:100%',
20859             cellspacing  : 0,
20860             cn : [
20861                 { 
20862                     tag: 'thead',
20863                     cn : [
20864                         { 
20865                             tag: 'tr',
20866                             cls : 'fc-first fc-last',
20867                             cn : cal_heads()
20868                         }
20869                     ]
20870                 },
20871                 { 
20872                     tag: 'tbody',
20873                     cn : cal_rows()
20874                 }
20875                   
20876             ]
20877         };
20878          
20879          var cfg = {
20880             cls : 'fc fc-ltr',
20881             cn : [
20882                 header,
20883                 {
20884                     cls : 'fc-content',
20885                     style : "position: relative;",
20886                     cn : [
20887                         {
20888                             cls : 'fc-view fc-view-month fc-grid',
20889                             style : 'position: relative',
20890                             unselectable : 'on',
20891                             cn : [
20892                                 {
20893                                     cls : 'fc-event-container',
20894                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20895                                 },
20896                                 cal_table
20897                             ]
20898                         }
20899                     ]
20900     
20901                 }
20902            ] 
20903             
20904         };
20905         
20906          
20907         
20908         return cfg;
20909     },
20910     
20911     
20912     initEvents : function()
20913     {
20914         if(!this.store){
20915             throw "can not find store for calendar";
20916         }
20917         
20918         var mark = {
20919             tag: "div",
20920             cls:"x-dlg-mask",
20921             style: "text-align:center",
20922             cn: [
20923                 {
20924                     tag: "div",
20925                     style: "background-color:white;width:50%;margin:250 auto",
20926                     cn: [
20927                         {
20928                             tag: "img",
20929                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20930                         },
20931                         {
20932                             tag: "span",
20933                             html: "Loading"
20934                         }
20935                         
20936                     ]
20937                 }
20938             ]
20939         };
20940         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20941         
20942         var size = this.el.select('.fc-content', true).first().getSize();
20943         this.maskEl.setSize(size.width, size.height);
20944         this.maskEl.enableDisplayMode("block");
20945         if(!this.loadMask){
20946             this.maskEl.hide();
20947         }
20948         
20949         this.store = Roo.factory(this.store, Roo.data);
20950         this.store.on('load', this.onLoad, this);
20951         this.store.on('beforeload', this.onBeforeLoad, this);
20952         
20953         this.resize();
20954         
20955         this.cells = this.el.select('.fc-day',true);
20956         //Roo.log(this.cells);
20957         this.textNodes = this.el.query('.fc-day-number');
20958         this.cells.addClassOnOver('fc-state-hover');
20959         
20960         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20961         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20962         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20963         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20964         
20965         this.on('monthchange', this.onMonthChange, this);
20966         
20967         this.update(new Date().clearTime());
20968     },
20969     
20970     resize : function() {
20971         var sz  = this.el.getSize();
20972         
20973         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20974         this.el.select('.fc-day-content div',true).setHeight(34);
20975     },
20976     
20977     
20978     // private
20979     showPrevMonth : function(e){
20980         this.update(this.activeDate.add("mo", -1));
20981     },
20982     showToday : function(e){
20983         this.update(new Date().clearTime());
20984     },
20985     // private
20986     showNextMonth : function(e){
20987         this.update(this.activeDate.add("mo", 1));
20988     },
20989
20990     // private
20991     showPrevYear : function(){
20992         this.update(this.activeDate.add("y", -1));
20993     },
20994
20995     // private
20996     showNextYear : function(){
20997         this.update(this.activeDate.add("y", 1));
20998     },
20999
21000     
21001    // private
21002     update : function(date)
21003     {
21004         var vd = this.activeDate;
21005         this.activeDate = date;
21006 //        if(vd && this.el){
21007 //            var t = date.getTime();
21008 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21009 //                Roo.log('using add remove');
21010 //                
21011 //                this.fireEvent('monthchange', this, date);
21012 //                
21013 //                this.cells.removeClass("fc-state-highlight");
21014 //                this.cells.each(function(c){
21015 //                   if(c.dateValue == t){
21016 //                       c.addClass("fc-state-highlight");
21017 //                       setTimeout(function(){
21018 //                            try{c.dom.firstChild.focus();}catch(e){}
21019 //                       }, 50);
21020 //                       return false;
21021 //                   }
21022 //                   return true;
21023 //                });
21024 //                return;
21025 //            }
21026 //        }
21027         
21028         var days = date.getDaysInMonth();
21029         
21030         var firstOfMonth = date.getFirstDateOfMonth();
21031         var startingPos = firstOfMonth.getDay()-this.startDay;
21032         
21033         if(startingPos < this.startDay){
21034             startingPos += 7;
21035         }
21036         
21037         var pm = date.add(Date.MONTH, -1);
21038         var prevStart = pm.getDaysInMonth()-startingPos;
21039 //        
21040         this.cells = this.el.select('.fc-day',true);
21041         this.textNodes = this.el.query('.fc-day-number');
21042         this.cells.addClassOnOver('fc-state-hover');
21043         
21044         var cells = this.cells.elements;
21045         var textEls = this.textNodes;
21046         
21047         Roo.each(cells, function(cell){
21048             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21049         });
21050         
21051         days += startingPos;
21052
21053         // convert everything to numbers so it's fast
21054         var day = 86400000;
21055         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21056         //Roo.log(d);
21057         //Roo.log(pm);
21058         //Roo.log(prevStart);
21059         
21060         var today = new Date().clearTime().getTime();
21061         var sel = date.clearTime().getTime();
21062         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21063         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21064         var ddMatch = this.disabledDatesRE;
21065         var ddText = this.disabledDatesText;
21066         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21067         var ddaysText = this.disabledDaysText;
21068         var format = this.format;
21069         
21070         var setCellClass = function(cal, cell){
21071             cell.row = 0;
21072             cell.events = [];
21073             cell.more = [];
21074             //Roo.log('set Cell Class');
21075             cell.title = "";
21076             var t = d.getTime();
21077             
21078             //Roo.log(d);
21079             
21080             cell.dateValue = t;
21081             if(t == today){
21082                 cell.className += " fc-today";
21083                 cell.className += " fc-state-highlight";
21084                 cell.title = cal.todayText;
21085             }
21086             if(t == sel){
21087                 // disable highlight in other month..
21088                 //cell.className += " fc-state-highlight";
21089                 
21090             }
21091             // disabling
21092             if(t < min) {
21093                 cell.className = " fc-state-disabled";
21094                 cell.title = cal.minText;
21095                 return;
21096             }
21097             if(t > max) {
21098                 cell.className = " fc-state-disabled";
21099                 cell.title = cal.maxText;
21100                 return;
21101             }
21102             if(ddays){
21103                 if(ddays.indexOf(d.getDay()) != -1){
21104                     cell.title = ddaysText;
21105                     cell.className = " fc-state-disabled";
21106                 }
21107             }
21108             if(ddMatch && format){
21109                 var fvalue = d.dateFormat(format);
21110                 if(ddMatch.test(fvalue)){
21111                     cell.title = ddText.replace("%0", fvalue);
21112                     cell.className = " fc-state-disabled";
21113                 }
21114             }
21115             
21116             if (!cell.initialClassName) {
21117                 cell.initialClassName = cell.dom.className;
21118             }
21119             
21120             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21121         };
21122
21123         var i = 0;
21124         
21125         for(; i < startingPos; i++) {
21126             textEls[i].innerHTML = (++prevStart);
21127             d.setDate(d.getDate()+1);
21128             
21129             cells[i].className = "fc-past fc-other-month";
21130             setCellClass(this, cells[i]);
21131         }
21132         
21133         var intDay = 0;
21134         
21135         for(; i < days; i++){
21136             intDay = i - startingPos + 1;
21137             textEls[i].innerHTML = (intDay);
21138             d.setDate(d.getDate()+1);
21139             
21140             cells[i].className = ''; // "x-date-active";
21141             setCellClass(this, cells[i]);
21142         }
21143         var extraDays = 0;
21144         
21145         for(; i < 42; i++) {
21146             textEls[i].innerHTML = (++extraDays);
21147             d.setDate(d.getDate()+1);
21148             
21149             cells[i].className = "fc-future fc-other-month";
21150             setCellClass(this, cells[i]);
21151         }
21152         
21153         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21154         
21155         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21156         
21157         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21158         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21159         
21160         if(totalRows != 6){
21161             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21162             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21163         }
21164         
21165         this.fireEvent('monthchange', this, date);
21166         
21167         
21168         /*
21169         if(!this.internalRender){
21170             var main = this.el.dom.firstChild;
21171             var w = main.offsetWidth;
21172             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21173             Roo.fly(main).setWidth(w);
21174             this.internalRender = true;
21175             // opera does not respect the auto grow header center column
21176             // then, after it gets a width opera refuses to recalculate
21177             // without a second pass
21178             if(Roo.isOpera && !this.secondPass){
21179                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21180                 this.secondPass = true;
21181                 this.update.defer(10, this, [date]);
21182             }
21183         }
21184         */
21185         
21186     },
21187     
21188     findCell : function(dt) {
21189         dt = dt.clearTime().getTime();
21190         var ret = false;
21191         this.cells.each(function(c){
21192             //Roo.log("check " +c.dateValue + '?=' + dt);
21193             if(c.dateValue == dt){
21194                 ret = c;
21195                 return false;
21196             }
21197             return true;
21198         });
21199         
21200         return ret;
21201     },
21202     
21203     findCells : function(ev) {
21204         var s = ev.start.clone().clearTime().getTime();
21205        // Roo.log(s);
21206         var e= ev.end.clone().clearTime().getTime();
21207        // Roo.log(e);
21208         var ret = [];
21209         this.cells.each(function(c){
21210              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21211             
21212             if(c.dateValue > e){
21213                 return ;
21214             }
21215             if(c.dateValue < s){
21216                 return ;
21217             }
21218             ret.push(c);
21219         });
21220         
21221         return ret;    
21222     },
21223     
21224 //    findBestRow: function(cells)
21225 //    {
21226 //        var ret = 0;
21227 //        
21228 //        for (var i =0 ; i < cells.length;i++) {
21229 //            ret  = Math.max(cells[i].rows || 0,ret);
21230 //        }
21231 //        return ret;
21232 //        
21233 //    },
21234     
21235     
21236     addItem : function(ev)
21237     {
21238         // look for vertical location slot in
21239         var cells = this.findCells(ev);
21240         
21241 //        ev.row = this.findBestRow(cells);
21242         
21243         // work out the location.
21244         
21245         var crow = false;
21246         var rows = [];
21247         for(var i =0; i < cells.length; i++) {
21248             
21249             cells[i].row = cells[0].row;
21250             
21251             if(i == 0){
21252                 cells[i].row = cells[i].row + 1;
21253             }
21254             
21255             if (!crow) {
21256                 crow = {
21257                     start : cells[i],
21258                     end :  cells[i]
21259                 };
21260                 continue;
21261             }
21262             if (crow.start.getY() == cells[i].getY()) {
21263                 // on same row.
21264                 crow.end = cells[i];
21265                 continue;
21266             }
21267             // different row.
21268             rows.push(crow);
21269             crow = {
21270                 start: cells[i],
21271                 end : cells[i]
21272             };
21273             
21274         }
21275         
21276         rows.push(crow);
21277         ev.els = [];
21278         ev.rows = rows;
21279         ev.cells = cells;
21280         
21281         cells[0].events.push(ev);
21282         
21283         this.calevents.push(ev);
21284     },
21285     
21286     clearEvents: function() {
21287         
21288         if(!this.calevents){
21289             return;
21290         }
21291         
21292         Roo.each(this.cells.elements, function(c){
21293             c.row = 0;
21294             c.events = [];
21295             c.more = [];
21296         });
21297         
21298         Roo.each(this.calevents, function(e) {
21299             Roo.each(e.els, function(el) {
21300                 el.un('mouseenter' ,this.onEventEnter, this);
21301                 el.un('mouseleave' ,this.onEventLeave, this);
21302                 el.remove();
21303             },this);
21304         },this);
21305         
21306         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21307             e.remove();
21308         });
21309         
21310     },
21311     
21312     renderEvents: function()
21313     {   
21314         var _this = this;
21315         
21316         this.cells.each(function(c) {
21317             
21318             if(c.row < 5){
21319                 return;
21320             }
21321             
21322             var ev = c.events;
21323             
21324             var r = 4;
21325             if(c.row != c.events.length){
21326                 r = 4 - (4 - (c.row - c.events.length));
21327             }
21328             
21329             c.events = ev.slice(0, r);
21330             c.more = ev.slice(r);
21331             
21332             if(c.more.length && c.more.length == 1){
21333                 c.events.push(c.more.pop());
21334             }
21335             
21336             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21337             
21338         });
21339             
21340         this.cells.each(function(c) {
21341             
21342             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21343             
21344             
21345             for (var e = 0; e < c.events.length; e++){
21346                 var ev = c.events[e];
21347                 var rows = ev.rows;
21348                 
21349                 for(var i = 0; i < rows.length; i++) {
21350                 
21351                     // how many rows should it span..
21352
21353                     var  cfg = {
21354                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21355                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21356
21357                         unselectable : "on",
21358                         cn : [
21359                             {
21360                                 cls: 'fc-event-inner',
21361                                 cn : [
21362     //                                {
21363     //                                  tag:'span',
21364     //                                  cls: 'fc-event-time',
21365     //                                  html : cells.length > 1 ? '' : ev.time
21366     //                                },
21367                                     {
21368                                       tag:'span',
21369                                       cls: 'fc-event-title',
21370                                       html : String.format('{0}', ev.title)
21371                                     }
21372
21373
21374                                 ]
21375                             },
21376                             {
21377                                 cls: 'ui-resizable-handle ui-resizable-e',
21378                                 html : '&nbsp;&nbsp;&nbsp'
21379                             }
21380
21381                         ]
21382                     };
21383
21384                     if (i == 0) {
21385                         cfg.cls += ' fc-event-start';
21386                     }
21387                     if ((i+1) == rows.length) {
21388                         cfg.cls += ' fc-event-end';
21389                     }
21390
21391                     var ctr = _this.el.select('.fc-event-container',true).first();
21392                     var cg = ctr.createChild(cfg);
21393
21394                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21395                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21396
21397                     var r = (c.more.length) ? 1 : 0;
21398                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21399                     cg.setWidth(ebox.right - sbox.x -2);
21400
21401                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21402                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21403                     cg.on('click', _this.onEventClick, _this, ev);
21404
21405                     ev.els.push(cg);
21406                     
21407                 }
21408                 
21409             }
21410             
21411             
21412             if(c.more.length){
21413                 var  cfg = {
21414                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21415                     style : 'position: absolute',
21416                     unselectable : "on",
21417                     cn : [
21418                         {
21419                             cls: 'fc-event-inner',
21420                             cn : [
21421                                 {
21422                                   tag:'span',
21423                                   cls: 'fc-event-title',
21424                                   html : 'More'
21425                                 }
21426
21427
21428                             ]
21429                         },
21430                         {
21431                             cls: 'ui-resizable-handle ui-resizable-e',
21432                             html : '&nbsp;&nbsp;&nbsp'
21433                         }
21434
21435                     ]
21436                 };
21437
21438                 var ctr = _this.el.select('.fc-event-container',true).first();
21439                 var cg = ctr.createChild(cfg);
21440
21441                 var sbox = c.select('.fc-day-content',true).first().getBox();
21442                 var ebox = c.select('.fc-day-content',true).first().getBox();
21443                 //Roo.log(cg);
21444                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21445                 cg.setWidth(ebox.right - sbox.x -2);
21446
21447                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21448                 
21449             }
21450             
21451         });
21452         
21453         
21454         
21455     },
21456     
21457     onEventEnter: function (e, el,event,d) {
21458         this.fireEvent('evententer', this, el, event);
21459     },
21460     
21461     onEventLeave: function (e, el,event,d) {
21462         this.fireEvent('eventleave', this, el, event);
21463     },
21464     
21465     onEventClick: function (e, el,event,d) {
21466         this.fireEvent('eventclick', this, el, event);
21467     },
21468     
21469     onMonthChange: function () {
21470         this.store.load();
21471     },
21472     
21473     onMoreEventClick: function(e, el, more)
21474     {
21475         var _this = this;
21476         
21477         this.calpopover.placement = 'right';
21478         this.calpopover.setTitle('More');
21479         
21480         this.calpopover.setContent('');
21481         
21482         var ctr = this.calpopover.el.select('.popover-content', true).first();
21483         
21484         Roo.each(more, function(m){
21485             var cfg = {
21486                 cls : 'fc-event-hori fc-event-draggable',
21487                 html : m.title
21488             };
21489             var cg = ctr.createChild(cfg);
21490             
21491             cg.on('click', _this.onEventClick, _this, m);
21492         });
21493         
21494         this.calpopover.show(el);
21495         
21496         
21497     },
21498     
21499     onLoad: function () 
21500     {   
21501         this.calevents = [];
21502         var cal = this;
21503         
21504         if(this.store.getCount() > 0){
21505             this.store.data.each(function(d){
21506                cal.addItem({
21507                     id : d.data.id,
21508                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21509                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21510                     time : d.data.start_time,
21511                     title : d.data.title,
21512                     description : d.data.description,
21513                     venue : d.data.venue
21514                 });
21515             });
21516         }
21517         
21518         this.renderEvents();
21519         
21520         if(this.calevents.length && this.loadMask){
21521             this.maskEl.hide();
21522         }
21523     },
21524     
21525     onBeforeLoad: function()
21526     {
21527         this.clearEvents();
21528         if(this.loadMask){
21529             this.maskEl.show();
21530         }
21531     }
21532 });
21533
21534  
21535  /*
21536  * - LGPL
21537  *
21538  * element
21539  * 
21540  */
21541
21542 /**
21543  * @class Roo.bootstrap.Popover
21544  * @extends Roo.bootstrap.Component
21545  * @parent none builder
21546  * @children Roo.bootstrap.Component
21547  * Bootstrap Popover class
21548  * @cfg {String} html contents of the popover   (or false to use children..)
21549  * @cfg {String} title of popover (or false to hide)
21550  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21551  * @cfg {String} trigger click || hover (or false to trigger manually)
21552  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21553  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21554  *      - if false and it has a 'parent' then it will be automatically added to that element
21555  *      - if string - Roo.get  will be called 
21556  * @cfg {Number} delay - delay before showing
21557  
21558  * @constructor
21559  * Create a new Popover
21560  * @param {Object} config The config object
21561  */
21562
21563 Roo.bootstrap.Popover = function(config){
21564     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21565     
21566     this.addEvents({
21567         // raw events
21568          /**
21569          * @event show
21570          * After the popover show
21571          * 
21572          * @param {Roo.bootstrap.Popover} this
21573          */
21574         "show" : true,
21575         /**
21576          * @event hide
21577          * After the popover hide
21578          * 
21579          * @param {Roo.bootstrap.Popover} this
21580          */
21581         "hide" : true
21582     });
21583 };
21584
21585 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21586     
21587     title: false,
21588     html: false,
21589     
21590     placement : 'right',
21591     trigger : 'hover', // hover
21592     modal : false,
21593     delay : 0,
21594     
21595     over: false,
21596     
21597     can_build_overlaid : false,
21598     
21599     maskEl : false, // the mask element
21600     headerEl : false,
21601     contentEl : false,
21602     alignEl : false, // when show is called with an element - this get's stored.
21603     
21604     getChildContainer : function()
21605     {
21606         return this.contentEl;
21607         
21608     },
21609     getPopoverHeader : function()
21610     {
21611         this.title = true; // flag not to hide it..
21612         this.headerEl.addClass('p-0');
21613         return this.headerEl
21614     },
21615     
21616     
21617     getAutoCreate : function(){
21618          
21619         var cfg = {
21620            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21621            style: 'display:block',
21622            cn : [
21623                 {
21624                     cls : 'arrow'
21625                 },
21626                 {
21627                     cls : 'popover-inner ',
21628                     cn : [
21629                         {
21630                             tag: 'h3',
21631                             cls: 'popover-title popover-header',
21632                             html : this.title === false ? '' : this.title
21633                         },
21634                         {
21635                             cls : 'popover-content popover-body '  + (this.cls || ''),
21636                             html : this.html || ''
21637                         }
21638                     ]
21639                     
21640                 }
21641            ]
21642         };
21643         
21644         return cfg;
21645     },
21646     /**
21647      * @param {string} the title
21648      */
21649     setTitle: function(str)
21650     {
21651         this.title = str;
21652         if (this.el) {
21653             this.headerEl.dom.innerHTML = str;
21654         }
21655         
21656     },
21657     /**
21658      * @param {string} the body content
21659      */
21660     setContent: function(str)
21661     {
21662         this.html = str;
21663         if (this.contentEl) {
21664             this.contentEl.dom.innerHTML = str;
21665         }
21666         
21667     },
21668     // as it get's added to the bottom of the page.
21669     onRender : function(ct, position)
21670     {
21671         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21672         
21673         
21674         
21675         if(!this.el){
21676             var cfg = Roo.apply({},  this.getAutoCreate());
21677             cfg.id = Roo.id();
21678             
21679             if (this.cls) {
21680                 cfg.cls += ' ' + this.cls;
21681             }
21682             if (this.style) {
21683                 cfg.style = this.style;
21684             }
21685             //Roo.log("adding to ");
21686             this.el = Roo.get(document.body).createChild(cfg, position);
21687 //            Roo.log(this.el);
21688         }
21689         
21690         this.contentEl = this.el.select('.popover-content',true).first();
21691         this.headerEl =  this.el.select('.popover-title',true).first();
21692         
21693         var nitems = [];
21694         if(typeof(this.items) != 'undefined'){
21695             var items = this.items;
21696             delete this.items;
21697
21698             for(var i =0;i < items.length;i++) {
21699                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21700             }
21701         }
21702
21703         this.items = nitems;
21704         
21705         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21706         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21707         
21708         
21709         
21710         this.initEvents();
21711     },
21712     
21713     resizeMask : function()
21714     {
21715         this.maskEl.setSize(
21716             Roo.lib.Dom.getViewWidth(true),
21717             Roo.lib.Dom.getViewHeight(true)
21718         );
21719     },
21720     
21721     initEvents : function()
21722     {
21723         
21724         if (!this.modal) { 
21725             Roo.bootstrap.Popover.register(this);
21726         }
21727          
21728         this.arrowEl = this.el.select('.arrow',true).first();
21729         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21730         this.el.enableDisplayMode('block');
21731         this.el.hide();
21732  
21733         
21734         if (this.over === false && !this.parent()) {
21735             return; 
21736         }
21737         if (this.triggers === false) {
21738             return;
21739         }
21740          
21741         // support parent
21742         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21743         var triggers = this.trigger ? this.trigger.split(' ') : [];
21744         Roo.each(triggers, function(trigger) {
21745         
21746             if (trigger == 'click') {
21747                 on_el.on('click', this.toggle, this);
21748             } else if (trigger != 'manual') {
21749                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21750                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21751       
21752                 on_el.on(eventIn  ,this.enter, this);
21753                 on_el.on(eventOut, this.leave, this);
21754             }
21755         }, this);
21756     },
21757     
21758     
21759     // private
21760     timeout : null,
21761     hoverState : null,
21762     
21763     toggle : function () {
21764         this.hoverState == 'in' ? this.leave() : this.enter();
21765     },
21766     
21767     enter : function () {
21768         
21769         clearTimeout(this.timeout);
21770     
21771         this.hoverState = 'in';
21772     
21773         if (!this.delay || !this.delay.show) {
21774             this.show();
21775             return;
21776         }
21777         var _t = this;
21778         this.timeout = setTimeout(function () {
21779             if (_t.hoverState == 'in') {
21780                 _t.show();
21781             }
21782         }, this.delay.show)
21783     },
21784     
21785     leave : function() {
21786         clearTimeout(this.timeout);
21787     
21788         this.hoverState = 'out';
21789     
21790         if (!this.delay || !this.delay.hide) {
21791             this.hide();
21792             return;
21793         }
21794         var _t = this;
21795         this.timeout = setTimeout(function () {
21796             if (_t.hoverState == 'out') {
21797                 _t.hide();
21798             }
21799         }, this.delay.hide)
21800     },
21801     
21802     /**
21803      * update the position of the dialog
21804      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21805      * 
21806      *
21807      */
21808     
21809     doAlign : function()
21810     {
21811         
21812         if (this.alignEl) {
21813             this.updatePosition(this.placement, true);
21814              
21815         } else {
21816             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21817             var es = this.el.getSize();
21818             var x = Roo.lib.Dom.getViewWidth()/2;
21819             var y = Roo.lib.Dom.getViewHeight()/2;
21820             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21821             
21822         }
21823
21824          
21825          
21826         
21827         
21828     },
21829     
21830     /**
21831      * Show the popover
21832      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21833      * @param {string} (left|right|top|bottom) position
21834      */
21835     show : function (on_el, placement)
21836     {
21837         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21838         on_el = on_el || false; // default to false
21839          
21840         if (!on_el) {
21841             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21842                 on_el = this.parent().el;
21843             } else if (this.over) {
21844                 on_el = Roo.get(this.over);
21845             }
21846             
21847         }
21848         
21849         this.alignEl = Roo.get( on_el );
21850
21851         if (!this.el) {
21852             this.render(document.body);
21853         }
21854         
21855         
21856          
21857         
21858         if (this.title === false) {
21859             this.headerEl.hide();
21860         }
21861         
21862        
21863         this.el.show();
21864         this.el.dom.style.display = 'block';
21865          
21866         this.doAlign();
21867         
21868         //var arrow = this.el.select('.arrow',true).first();
21869         //arrow.set(align[2], 
21870         
21871         this.el.addClass('in');
21872         
21873          
21874         
21875         this.hoverState = 'in';
21876         
21877         if (this.modal) {
21878             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21879             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21880             this.maskEl.dom.style.display = 'block';
21881             this.maskEl.addClass('show');
21882         }
21883         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21884  
21885         this.fireEvent('show', this);
21886         
21887     },
21888     /**
21889      * fire this manually after loading a grid in the table for example
21890      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21891      * @param {Boolean} try and move it if we cant get right position.
21892      */
21893     updatePosition : function(placement, try_move)
21894     {
21895         // allow for calling with no parameters
21896         placement = placement   ? placement :  this.placement;
21897         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21898         
21899         this.el.removeClass([
21900             'fade','top','bottom', 'left', 'right','in',
21901             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21902         ]);
21903         this.el.addClass(placement + ' bs-popover-' + placement);
21904         
21905         if (!this.alignEl ) {
21906             return false;
21907         }
21908         
21909         switch (placement) {
21910             case 'right':
21911                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21912                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21913                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21914                     //normal display... or moved up/down.
21915                     this.el.setXY(offset);
21916                     var xy = this.alignEl.getAnchorXY('tr', false);
21917                     xy[0]+=2;xy[1]+=5;
21918                     this.arrowEl.setXY(xy);
21919                     return true;
21920                 }
21921                 // continue through...
21922                 return this.updatePosition('left', false);
21923                 
21924             
21925             case 'left':
21926                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21927                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21928                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21929                     //normal display... or moved up/down.
21930                     this.el.setXY(offset);
21931                     var xy = this.alignEl.getAnchorXY('tl', false);
21932                     xy[0]-=10;xy[1]+=5; // << fix me
21933                     this.arrowEl.setXY(xy);
21934                     return true;
21935                 }
21936                 // call self...
21937                 return this.updatePosition('right', false);
21938             
21939             case 'top':
21940                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21941                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21942                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21943                     //normal display... or moved up/down.
21944                     this.el.setXY(offset);
21945                     var xy = this.alignEl.getAnchorXY('t', false);
21946                     xy[1]-=10; // << fix me
21947                     this.arrowEl.setXY(xy);
21948                     return true;
21949                 }
21950                 // fall through
21951                return this.updatePosition('bottom', false);
21952             
21953             case 'bottom':
21954                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21955                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21956                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21957                     //normal display... or moved up/down.
21958                     this.el.setXY(offset);
21959                     var xy = this.alignEl.getAnchorXY('b', false);
21960                      xy[1]+=2; // << fix me
21961                     this.arrowEl.setXY(xy);
21962                     return true;
21963                 }
21964                 // fall through
21965                 return this.updatePosition('top', false);
21966                 
21967             
21968         }
21969         
21970         
21971         return false;
21972     },
21973     
21974     hide : function()
21975     {
21976         this.el.setXY([0,0]);
21977         this.el.removeClass('in');
21978         this.el.hide();
21979         this.hoverState = null;
21980         this.maskEl.hide(); // always..
21981         this.fireEvent('hide', this);
21982     }
21983     
21984 });
21985
21986
21987 Roo.apply(Roo.bootstrap.Popover, {
21988
21989     alignment : {
21990         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21991         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21992         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21993         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21994     },
21995     
21996     zIndex : 20001,
21997
21998     clickHander : false,
21999     
22000     
22001
22002     onMouseDown : function(e)
22003     {
22004         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22005             /// what is nothing is showing..
22006             this.hideAll();
22007         }
22008          
22009     },
22010     
22011     
22012     popups : [],
22013     
22014     register : function(popup)
22015     {
22016         if (!Roo.bootstrap.Popover.clickHandler) {
22017             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22018         }
22019         // hide other popups.
22020         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22021         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22022         this.hideAll(); //<< why?
22023         //this.popups.push(popup);
22024     },
22025     hideAll : function()
22026     {
22027         this.popups.forEach(function(p) {
22028             p.hide();
22029         });
22030     },
22031     onShow : function() {
22032         Roo.bootstrap.Popover.popups.push(this);
22033     },
22034     onHide : function() {
22035         Roo.bootstrap.Popover.popups.remove(this);
22036     } 
22037
22038 });
22039 /**
22040  * @class Roo.bootstrap.PopoverNav
22041  * @extends Roo.bootstrap.nav.Simplebar
22042  * @parent Roo.bootstrap.Popover
22043  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22044  * @licence LGPL
22045  * Bootstrap Popover header navigation class
22046  * FIXME? should this go under nav?
22047  *
22048  * 
22049  * @constructor
22050  * Create a new Popover Header Navigation 
22051  * @param {Object} config The config object
22052  */
22053
22054 Roo.bootstrap.PopoverNav = function(config){
22055     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22059     
22060     
22061     container_method : 'getPopoverHeader' 
22062     
22063      
22064     
22065     
22066    
22067 });
22068
22069  
22070
22071  /*
22072  * - LGPL
22073  *
22074  * Progress
22075  * 
22076  */
22077
22078 /**
22079  * @class Roo.bootstrap.Progress
22080  * @extends Roo.bootstrap.Component
22081  * @children Roo.bootstrap.ProgressBar
22082  * Bootstrap Progress class
22083  * @cfg {Boolean} striped striped of the progress bar
22084  * @cfg {Boolean} active animated of the progress bar
22085  * 
22086  * 
22087  * @constructor
22088  * Create a new Progress
22089  * @param {Object} config The config object
22090  */
22091
22092 Roo.bootstrap.Progress = function(config){
22093     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22094 };
22095
22096 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22097     
22098     striped : false,
22099     active: false,
22100     
22101     getAutoCreate : function(){
22102         var cfg = {
22103             tag: 'div',
22104             cls: 'progress'
22105         };
22106         
22107         
22108         if(this.striped){
22109             cfg.cls += ' progress-striped';
22110         }
22111       
22112         if(this.active){
22113             cfg.cls += ' active';
22114         }
22115         
22116         
22117         return cfg;
22118     }
22119    
22120 });
22121
22122  
22123
22124  /*
22125  * - LGPL
22126  *
22127  * ProgressBar
22128  * 
22129  */
22130
22131 /**
22132  * @class Roo.bootstrap.ProgressBar
22133  * @extends Roo.bootstrap.Component
22134  * Bootstrap ProgressBar class
22135  * @cfg {Number} aria_valuenow aria-value now
22136  * @cfg {Number} aria_valuemin aria-value min
22137  * @cfg {Number} aria_valuemax aria-value max
22138  * @cfg {String} label label for the progress bar
22139  * @cfg {String} panel (success | info | warning | danger )
22140  * @cfg {String} role role of the progress bar
22141  * @cfg {String} sr_only text
22142  * 
22143  * 
22144  * @constructor
22145  * Create a new ProgressBar
22146  * @param {Object} config The config object
22147  */
22148
22149 Roo.bootstrap.ProgressBar = function(config){
22150     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22151 };
22152
22153 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22154     
22155     aria_valuenow : 0,
22156     aria_valuemin : 0,
22157     aria_valuemax : 100,
22158     label : false,
22159     panel : false,
22160     role : false,
22161     sr_only: false,
22162     
22163     getAutoCreate : function()
22164     {
22165         
22166         var cfg = {
22167             tag: 'div',
22168             cls: 'progress-bar',
22169             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22170         };
22171         
22172         if(this.sr_only){
22173             cfg.cn = {
22174                 tag: 'span',
22175                 cls: 'sr-only',
22176                 html: this.sr_only
22177             }
22178         }
22179         
22180         if(this.role){
22181             cfg.role = this.role;
22182         }
22183         
22184         if(this.aria_valuenow){
22185             cfg['aria-valuenow'] = this.aria_valuenow;
22186         }
22187         
22188         if(this.aria_valuemin){
22189             cfg['aria-valuemin'] = this.aria_valuemin;
22190         }
22191         
22192         if(this.aria_valuemax){
22193             cfg['aria-valuemax'] = this.aria_valuemax;
22194         }
22195         
22196         if(this.label && !this.sr_only){
22197             cfg.html = this.label;
22198         }
22199         
22200         if(this.panel){
22201             cfg.cls += ' progress-bar-' + this.panel;
22202         }
22203         
22204         return cfg;
22205     },
22206     
22207     update : function(aria_valuenow)
22208     {
22209         this.aria_valuenow = aria_valuenow;
22210         
22211         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22212     }
22213    
22214 });
22215
22216  
22217
22218  /**
22219  * @class Roo.bootstrap.TabGroup
22220  * @extends Roo.bootstrap.Column
22221  * @children Roo.bootstrap.TabPanel
22222  * Bootstrap Column class
22223  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22224  * @cfg {Boolean} carousel true to make the group behave like a carousel
22225  * @cfg {Boolean} bullets show bullets for the panels
22226  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22227  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22228  * @cfg {Boolean} showarrow (true|false) show arrow default true
22229  * 
22230  * @constructor
22231  * Create a new TabGroup
22232  * @param {Object} config The config object
22233  */
22234
22235 Roo.bootstrap.TabGroup = function(config){
22236     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22237     if (!this.navId) {
22238         this.navId = Roo.id();
22239     }
22240     this.tabs = [];
22241     Roo.bootstrap.TabGroup.register(this);
22242     
22243 };
22244
22245 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22246     
22247     carousel : false,
22248     transition : false,
22249     bullets : 0,
22250     timer : 0,
22251     autoslide : false,
22252     slideFn : false,
22253     slideOnTouch : false,
22254     showarrow : true,
22255     
22256     getAutoCreate : function()
22257     {
22258         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22259         
22260         cfg.cls += ' tab-content';
22261         
22262         if (this.carousel) {
22263             cfg.cls += ' carousel slide';
22264             
22265             cfg.cn = [{
22266                cls : 'carousel-inner',
22267                cn : []
22268             }];
22269         
22270             if(this.bullets  && !Roo.isTouch){
22271                 
22272                 var bullets = {
22273                     cls : 'carousel-bullets',
22274                     cn : []
22275                 };
22276                
22277                 if(this.bullets_cls){
22278                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22279                 }
22280                 
22281                 bullets.cn.push({
22282                     cls : 'clear'
22283                 });
22284                 
22285                 cfg.cn[0].cn.push(bullets);
22286             }
22287             
22288             if(this.showarrow){
22289                 cfg.cn[0].cn.push({
22290                     tag : 'div',
22291                     class : 'carousel-arrow',
22292                     cn : [
22293                         {
22294                             tag : 'div',
22295                             class : 'carousel-prev',
22296                             cn : [
22297                                 {
22298                                     tag : 'i',
22299                                     class : 'fa fa-chevron-left'
22300                                 }
22301                             ]
22302                         },
22303                         {
22304                             tag : 'div',
22305                             class : 'carousel-next',
22306                             cn : [
22307                                 {
22308                                     tag : 'i',
22309                                     class : 'fa fa-chevron-right'
22310                                 }
22311                             ]
22312                         }
22313                     ]
22314                 });
22315             }
22316             
22317         }
22318         
22319         return cfg;
22320     },
22321     
22322     initEvents:  function()
22323     {
22324 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22325 //            this.el.on("touchstart", this.onTouchStart, this);
22326 //        }
22327         
22328         if(this.autoslide){
22329             var _this = this;
22330             
22331             this.slideFn = window.setInterval(function() {
22332                 _this.showPanelNext();
22333             }, this.timer);
22334         }
22335         
22336         if(this.showarrow){
22337             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22338             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22339         }
22340         
22341         
22342     },
22343     
22344 //    onTouchStart : function(e, el, o)
22345 //    {
22346 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22347 //            return;
22348 //        }
22349 //        
22350 //        this.showPanelNext();
22351 //    },
22352     
22353     
22354     getChildContainer : function()
22355     {
22356         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22357     },
22358     
22359     /**
22360     * register a Navigation item
22361     * @param {Roo.bootstrap.nav.Item} the navitem to add
22362     */
22363     register : function(item)
22364     {
22365         this.tabs.push( item);
22366         item.navId = this.navId; // not really needed..
22367         this.addBullet();
22368     
22369     },
22370     
22371     getActivePanel : function()
22372     {
22373         var r = false;
22374         Roo.each(this.tabs, function(t) {
22375             if (t.active) {
22376                 r = t;
22377                 return false;
22378             }
22379             return null;
22380         });
22381         return r;
22382         
22383     },
22384     getPanelByName : function(n)
22385     {
22386         var r = false;
22387         Roo.each(this.tabs, function(t) {
22388             if (t.tabId == n) {
22389                 r = t;
22390                 return false;
22391             }
22392             return null;
22393         });
22394         return r;
22395     },
22396     indexOfPanel : function(p)
22397     {
22398         var r = false;
22399         Roo.each(this.tabs, function(t,i) {
22400             if (t.tabId == p.tabId) {
22401                 r = i;
22402                 return false;
22403             }
22404             return null;
22405         });
22406         return r;
22407     },
22408     /**
22409      * show a specific panel
22410      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22411      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22412      */
22413     showPanel : function (pan)
22414     {
22415         if(this.transition || typeof(pan) == 'undefined'){
22416             Roo.log("waiting for the transitionend");
22417             return false;
22418         }
22419         
22420         if (typeof(pan) == 'number') {
22421             pan = this.tabs[pan];
22422         }
22423         
22424         if (typeof(pan) == 'string') {
22425             pan = this.getPanelByName(pan);
22426         }
22427         
22428         var cur = this.getActivePanel();
22429         
22430         if(!pan || !cur){
22431             Roo.log('pan or acitve pan is undefined');
22432             return false;
22433         }
22434         
22435         if (pan.tabId == this.getActivePanel().tabId) {
22436             return true;
22437         }
22438         
22439         if (false === cur.fireEvent('beforedeactivate')) {
22440             return false;
22441         }
22442         
22443         if(this.bullets > 0 && !Roo.isTouch){
22444             this.setActiveBullet(this.indexOfPanel(pan));
22445         }
22446         
22447         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22448             
22449             //class="carousel-item carousel-item-next carousel-item-left"
22450             
22451             this.transition = true;
22452             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22453             var lr = dir == 'next' ? 'left' : 'right';
22454             pan.el.addClass(dir); // or prev
22455             pan.el.addClass('carousel-item-' + dir); // or prev
22456             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22457             cur.el.addClass(lr); // or right
22458             pan.el.addClass(lr);
22459             cur.el.addClass('carousel-item-' +lr); // or right
22460             pan.el.addClass('carousel-item-' +lr);
22461             
22462             
22463             var _this = this;
22464             cur.el.on('transitionend', function() {
22465                 Roo.log("trans end?");
22466                 
22467                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22468                 pan.setActive(true);
22469                 
22470                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22471                 cur.setActive(false);
22472                 
22473                 _this.transition = false;
22474                 
22475             }, this, { single:  true } );
22476             
22477             return true;
22478         }
22479         
22480         cur.setActive(false);
22481         pan.setActive(true);
22482         
22483         return true;
22484         
22485     },
22486     showPanelNext : function()
22487     {
22488         var i = this.indexOfPanel(this.getActivePanel());
22489         
22490         if (i >= this.tabs.length - 1 && !this.autoslide) {
22491             return;
22492         }
22493         
22494         if (i >= this.tabs.length - 1 && this.autoslide) {
22495             i = -1;
22496         }
22497         
22498         this.showPanel(this.tabs[i+1]);
22499     },
22500     
22501     showPanelPrev : function()
22502     {
22503         var i = this.indexOfPanel(this.getActivePanel());
22504         
22505         if (i  < 1 && !this.autoslide) {
22506             return;
22507         }
22508         
22509         if (i < 1 && this.autoslide) {
22510             i = this.tabs.length;
22511         }
22512         
22513         this.showPanel(this.tabs[i-1]);
22514     },
22515     
22516     
22517     addBullet: function()
22518     {
22519         if(!this.bullets || Roo.isTouch){
22520             return;
22521         }
22522         var ctr = this.el.select('.carousel-bullets',true).first();
22523         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22524         var bullet = ctr.createChild({
22525             cls : 'bullet bullet-' + i
22526         },ctr.dom.lastChild);
22527         
22528         
22529         var _this = this;
22530         
22531         bullet.on('click', (function(e, el, o, ii, t){
22532
22533             e.preventDefault();
22534
22535             this.showPanel(ii);
22536
22537             if(this.autoslide && this.slideFn){
22538                 clearInterval(this.slideFn);
22539                 this.slideFn = window.setInterval(function() {
22540                     _this.showPanelNext();
22541                 }, this.timer);
22542             }
22543
22544         }).createDelegate(this, [i, bullet], true));
22545                 
22546         
22547     },
22548      
22549     setActiveBullet : function(i)
22550     {
22551         if(Roo.isTouch){
22552             return;
22553         }
22554         
22555         Roo.each(this.el.select('.bullet', true).elements, function(el){
22556             el.removeClass('selected');
22557         });
22558
22559         var bullet = this.el.select('.bullet-' + i, true).first();
22560         
22561         if(!bullet){
22562             return;
22563         }
22564         
22565         bullet.addClass('selected');
22566     }
22567     
22568     
22569   
22570 });
22571
22572  
22573
22574  
22575  
22576 Roo.apply(Roo.bootstrap.TabGroup, {
22577     
22578     groups: {},
22579      /**
22580     * register a Navigation Group
22581     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22582     */
22583     register : function(navgrp)
22584     {
22585         this.groups[navgrp.navId] = navgrp;
22586         
22587     },
22588     /**
22589     * fetch a Navigation Group based on the navigation ID
22590     * if one does not exist , it will get created.
22591     * @param {string} the navgroup to add
22592     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22593     */
22594     get: function(navId) {
22595         if (typeof(this.groups[navId]) == 'undefined') {
22596             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22597         }
22598         return this.groups[navId] ;
22599     }
22600     
22601     
22602     
22603 });
22604
22605  /*
22606  * - LGPL
22607  *
22608  * TabPanel
22609  * 
22610  */
22611
22612 /**
22613  * @class Roo.bootstrap.TabPanel
22614  * @extends Roo.bootstrap.Component
22615  * @children Roo.bootstrap.Component
22616  * Bootstrap TabPanel class
22617  * @cfg {Boolean} active panel active
22618  * @cfg {String} html panel content
22619  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22620  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22621  * @cfg {String} href click to link..
22622  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22623  * 
22624  * 
22625  * @constructor
22626  * Create a new TabPanel
22627  * @param {Object} config The config object
22628  */
22629
22630 Roo.bootstrap.TabPanel = function(config){
22631     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22632     this.addEvents({
22633         /**
22634              * @event changed
22635              * Fires when the active status changes
22636              * @param {Roo.bootstrap.TabPanel} this
22637              * @param {Boolean} state the new state
22638             
22639          */
22640         'changed': true,
22641         /**
22642              * @event beforedeactivate
22643              * Fires before a tab is de-activated - can be used to do validation on a form.
22644              * @param {Roo.bootstrap.TabPanel} this
22645              * @return {Boolean} false if there is an error
22646             
22647          */
22648         'beforedeactivate': true
22649      });
22650     
22651     this.tabId = this.tabId || Roo.id();
22652   
22653 };
22654
22655 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22656     
22657     active: false,
22658     html: false,
22659     tabId: false,
22660     navId : false,
22661     href : '',
22662     touchSlide : false,
22663     getAutoCreate : function(){
22664         
22665         
22666         var cfg = {
22667             tag: 'div',
22668             // item is needed for carousel - not sure if it has any effect otherwise
22669             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22670             html: this.html || ''
22671         };
22672         
22673         if(this.active){
22674             cfg.cls += ' active';
22675         }
22676         
22677         if(this.tabId){
22678             cfg.tabId = this.tabId;
22679         }
22680         
22681         
22682         
22683         return cfg;
22684     },
22685     
22686     initEvents:  function()
22687     {
22688         var p = this.parent();
22689         
22690         this.navId = this.navId || p.navId;
22691         
22692         if (typeof(this.navId) != 'undefined') {
22693             // not really needed.. but just in case.. parent should be a NavGroup.
22694             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22695             
22696             tg.register(this);
22697             
22698             var i = tg.tabs.length - 1;
22699             
22700             if(this.active && tg.bullets > 0 && i < tg.bullets){
22701                 tg.setActiveBullet(i);
22702             }
22703         }
22704         
22705         this.el.on('click', this.onClick, this);
22706         
22707         if(Roo.isTouch && this.touchSlide){
22708             this.el.on("touchstart", this.onTouchStart, this);
22709             this.el.on("touchmove", this.onTouchMove, this);
22710             this.el.on("touchend", this.onTouchEnd, this);
22711         }
22712         
22713     },
22714     
22715     onRender : function(ct, position)
22716     {
22717         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22718     },
22719     
22720     setActive : function(state)
22721     {
22722         Roo.log("panel - set active " + this.tabId + "=" + state);
22723         
22724         this.active = state;
22725         if (!state) {
22726             this.el.removeClass('active');
22727             
22728         } else  if (!this.el.hasClass('active')) {
22729             this.el.addClass('active');
22730         }
22731         
22732         this.fireEvent('changed', this, state);
22733     },
22734     
22735     onClick : function(e)
22736     {
22737         e.preventDefault();
22738         
22739         if(!this.href.length){
22740             return;
22741         }
22742         
22743         window.location.href = this.href;
22744     },
22745     
22746     startX : 0,
22747     startY : 0,
22748     endX : 0,
22749     endY : 0,
22750     swiping : false,
22751     
22752     onTouchStart : function(e)
22753     {
22754         this.swiping = false;
22755         
22756         this.startX = e.browserEvent.touches[0].clientX;
22757         this.startY = e.browserEvent.touches[0].clientY;
22758     },
22759     
22760     onTouchMove : function(e)
22761     {
22762         this.swiping = true;
22763         
22764         this.endX = e.browserEvent.touches[0].clientX;
22765         this.endY = e.browserEvent.touches[0].clientY;
22766     },
22767     
22768     onTouchEnd : function(e)
22769     {
22770         if(!this.swiping){
22771             this.onClick(e);
22772             return;
22773         }
22774         
22775         var tabGroup = this.parent();
22776         
22777         if(this.endX > this.startX){ // swiping right
22778             tabGroup.showPanelPrev();
22779             return;
22780         }
22781         
22782         if(this.startX > this.endX){ // swiping left
22783             tabGroup.showPanelNext();
22784             return;
22785         }
22786     }
22787     
22788     
22789 });
22790  
22791
22792  
22793
22794  /*
22795  * - LGPL
22796  *
22797  * DateField
22798  * 
22799  */
22800
22801 /**
22802  * @class Roo.bootstrap.form.DateField
22803  * @extends Roo.bootstrap.form.Input
22804  * Bootstrap DateField class
22805  * @cfg {Number} weekStart default 0
22806  * @cfg {String} viewMode default empty, (months|years)
22807  * @cfg {String} minViewMode default empty, (months|years)
22808  * @cfg {Number} startDate default -Infinity
22809  * @cfg {Number} endDate default Infinity
22810  * @cfg {Boolean} todayHighlight default false
22811  * @cfg {Boolean} todayBtn default false
22812  * @cfg {Boolean} calendarWeeks default false
22813  * @cfg {Object} daysOfWeekDisabled default empty
22814  * @cfg {Boolean} singleMode default false (true | false)
22815  * 
22816  * @cfg {Boolean} keyboardNavigation default true
22817  * @cfg {String} language default en
22818  * 
22819  * @constructor
22820  * Create a new DateField
22821  * @param {Object} config The config object
22822  */
22823
22824 Roo.bootstrap.form.DateField = function(config){
22825     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22826      this.addEvents({
22827             /**
22828              * @event show
22829              * Fires when this field show.
22830              * @param {Roo.bootstrap.form.DateField} this
22831              * @param {Mixed} date The date value
22832              */
22833             show : true,
22834             /**
22835              * @event show
22836              * Fires when this field hide.
22837              * @param {Roo.bootstrap.form.DateField} this
22838              * @param {Mixed} date The date value
22839              */
22840             hide : true,
22841             /**
22842              * @event select
22843              * Fires when select a date.
22844              * @param {Roo.bootstrap.form.DateField} this
22845              * @param {Mixed} date The date value
22846              */
22847             select : true,
22848             /**
22849              * @event beforeselect
22850              * Fires when before select a date.
22851              * @param {Roo.bootstrap.form.DateField} this
22852              * @param {Mixed} date The date value
22853              */
22854             beforeselect : true
22855         });
22856 };
22857
22858 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22859     
22860     /**
22861      * @cfg {String} format
22862      * The default date format string which can be overriden for localization support.  The format must be
22863      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22864      */
22865     format : "m/d/y",
22866     /**
22867      * @cfg {String} altFormats
22868      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22869      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22870      */
22871     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22872     
22873     weekStart : 0,
22874     
22875     viewMode : '',
22876     
22877     minViewMode : '',
22878     
22879     todayHighlight : false,
22880     
22881     todayBtn: false,
22882     
22883     language: 'en',
22884     
22885     keyboardNavigation: true,
22886     
22887     calendarWeeks: false,
22888     
22889     startDate: -Infinity,
22890     
22891     endDate: Infinity,
22892     
22893     daysOfWeekDisabled: [],
22894     
22895     _events: [],
22896     
22897     singleMode : false,
22898     
22899     UTCDate: function()
22900     {
22901         return new Date(Date.UTC.apply(Date, arguments));
22902     },
22903     
22904     UTCToday: function()
22905     {
22906         var today = new Date();
22907         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22908     },
22909     
22910     getDate: function() {
22911             var d = this.getUTCDate();
22912             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22913     },
22914     
22915     getUTCDate: function() {
22916             return this.date;
22917     },
22918     
22919     setDate: function(d) {
22920             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22921     },
22922     
22923     setUTCDate: function(d) {
22924             this.date = d;
22925             this.setValue(this.formatDate(this.date));
22926     },
22927         
22928     onRender: function(ct, position)
22929     {
22930         
22931         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22932         
22933         this.language = this.language || 'en';
22934         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22935         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22936         
22937         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22938         this.format = this.format || 'm/d/y';
22939         this.isInline = false;
22940         this.isInput = true;
22941         this.component = this.el.select('.add-on', true).first() || false;
22942         this.component = (this.component && this.component.length === 0) ? false : this.component;
22943         this.hasInput = this.component && this.inputEl().length;
22944         
22945         if (typeof(this.minViewMode === 'string')) {
22946             switch (this.minViewMode) {
22947                 case 'months':
22948                     this.minViewMode = 1;
22949                     break;
22950                 case 'years':
22951                     this.minViewMode = 2;
22952                     break;
22953                 default:
22954                     this.minViewMode = 0;
22955                     break;
22956             }
22957         }
22958         
22959         if (typeof(this.viewMode === 'string')) {
22960             switch (this.viewMode) {
22961                 case 'months':
22962                     this.viewMode = 1;
22963                     break;
22964                 case 'years':
22965                     this.viewMode = 2;
22966                     break;
22967                 default:
22968                     this.viewMode = 0;
22969                     break;
22970             }
22971         }
22972                 
22973         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22974         
22975 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22976         
22977         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22978         
22979         this.picker().on('mousedown', this.onMousedown, this);
22980         this.picker().on('click', this.onClick, this);
22981         
22982         this.picker().addClass('datepicker-dropdown');
22983         
22984         this.startViewMode = this.viewMode;
22985         
22986         if(this.singleMode){
22987             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22988                 v.setVisibilityMode(Roo.Element.DISPLAY);
22989                 v.hide();
22990             });
22991             
22992             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22993                 v.setStyle('width', '189px');
22994             });
22995         }
22996         
22997         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22998             if(!this.calendarWeeks){
22999                 v.remove();
23000                 return;
23001             }
23002             
23003             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23004             v.attr('colspan', function(i, val){
23005                 return parseInt(val) + 1;
23006             });
23007         });
23008                         
23009         
23010         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23011         
23012         this.setStartDate(this.startDate);
23013         this.setEndDate(this.endDate);
23014         
23015         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23016         
23017         this.fillDow();
23018         this.fillMonths();
23019         this.update();
23020         this.showMode();
23021         
23022         if(this.isInline) {
23023             this.showPopup();
23024         }
23025     },
23026     
23027     picker : function()
23028     {
23029         return this.pickerEl;
23030 //        return this.el.select('.datepicker', true).first();
23031     },
23032     
23033     fillDow: function()
23034     {
23035         var dowCnt = this.weekStart;
23036         
23037         var dow = {
23038             tag: 'tr',
23039             cn: [
23040                 
23041             ]
23042         };
23043         
23044         if(this.calendarWeeks){
23045             dow.cn.push({
23046                 tag: 'th',
23047                 cls: 'cw',
23048                 html: '&nbsp;'
23049             })
23050         }
23051         
23052         while (dowCnt < this.weekStart + 7) {
23053             dow.cn.push({
23054                 tag: 'th',
23055                 cls: 'dow',
23056                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23057             });
23058         }
23059         
23060         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23061     },
23062     
23063     fillMonths: function()
23064     {    
23065         var i = 0;
23066         var months = this.picker().select('>.datepicker-months td', true).first();
23067         
23068         months.dom.innerHTML = '';
23069         
23070         while (i < 12) {
23071             var month = {
23072                 tag: 'span',
23073                 cls: 'month',
23074                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23075             };
23076             
23077             months.createChild(month);
23078         }
23079         
23080     },
23081     
23082     update: function()
23083     {
23084         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;
23085         
23086         if (this.date < this.startDate) {
23087             this.viewDate = new Date(this.startDate);
23088         } else if (this.date > this.endDate) {
23089             this.viewDate = new Date(this.endDate);
23090         } else {
23091             this.viewDate = new Date(this.date);
23092         }
23093         
23094         this.fill();
23095     },
23096     
23097     fill: function() 
23098     {
23099         var d = new Date(this.viewDate),
23100                 year = d.getUTCFullYear(),
23101                 month = d.getUTCMonth(),
23102                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23103                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23104                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23105                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23106                 currentDate = this.date && this.date.valueOf(),
23107                 today = this.UTCToday();
23108         
23109         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23110         
23111 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23112         
23113 //        this.picker.select('>tfoot th.today').
23114 //                                              .text(dates[this.language].today)
23115 //                                              .toggle(this.todayBtn !== false);
23116     
23117         this.updateNavArrows();
23118         this.fillMonths();
23119                                                 
23120         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23121         
23122         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23123          
23124         prevMonth.setUTCDate(day);
23125         
23126         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23127         
23128         var nextMonth = new Date(prevMonth);
23129         
23130         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23131         
23132         nextMonth = nextMonth.valueOf();
23133         
23134         var fillMonths = false;
23135         
23136         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23137         
23138         while(prevMonth.valueOf() <= nextMonth) {
23139             var clsName = '';
23140             
23141             if (prevMonth.getUTCDay() === this.weekStart) {
23142                 if(fillMonths){
23143                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23144                 }
23145                     
23146                 fillMonths = {
23147                     tag: 'tr',
23148                     cn: []
23149                 };
23150                 
23151                 if(this.calendarWeeks){
23152                     // ISO 8601: First week contains first thursday.
23153                     // ISO also states week starts on Monday, but we can be more abstract here.
23154                     var
23155                     // Start of current week: based on weekstart/current date
23156                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23157                     // Thursday of this week
23158                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23159                     // First Thursday of year, year from thursday
23160                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23161                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23162                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23163                     
23164                     fillMonths.cn.push({
23165                         tag: 'td',
23166                         cls: 'cw',
23167                         html: calWeek
23168                     });
23169                 }
23170             }
23171             
23172             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23173                 clsName += ' old';
23174             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23175                 clsName += ' new';
23176             }
23177             if (this.todayHighlight &&
23178                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23179                 prevMonth.getUTCMonth() == today.getMonth() &&
23180                 prevMonth.getUTCDate() == today.getDate()) {
23181                 clsName += ' today';
23182             }
23183             
23184             if (currentDate && prevMonth.valueOf() === currentDate) {
23185                 clsName += ' active';
23186             }
23187             
23188             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23189                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23190                     clsName += ' disabled';
23191             }
23192             
23193             fillMonths.cn.push({
23194                 tag: 'td',
23195                 cls: 'day ' + clsName,
23196                 html: prevMonth.getDate()
23197             });
23198             
23199             prevMonth.setDate(prevMonth.getDate()+1);
23200         }
23201           
23202         var currentYear = this.date && this.date.getUTCFullYear();
23203         var currentMonth = this.date && this.date.getUTCMonth();
23204         
23205         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23206         
23207         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23208             v.removeClass('active');
23209             
23210             if(currentYear === year && k === currentMonth){
23211                 v.addClass('active');
23212             }
23213             
23214             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23215                 v.addClass('disabled');
23216             }
23217             
23218         });
23219         
23220         
23221         year = parseInt(year/10, 10) * 10;
23222         
23223         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23224         
23225         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23226         
23227         year -= 1;
23228         for (var i = -1; i < 11; i++) {
23229             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23230                 tag: 'span',
23231                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23232                 html: year
23233             });
23234             
23235             year += 1;
23236         }
23237     },
23238     
23239     showMode: function(dir) 
23240     {
23241         if (dir) {
23242             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23243         }
23244         
23245         Roo.each(this.picker().select('>div',true).elements, function(v){
23246             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23247             v.hide();
23248         });
23249         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23250     },
23251     
23252     place: function()
23253     {
23254         if(this.isInline) {
23255             return;
23256         }
23257         
23258         this.picker().removeClass(['bottom', 'top']);
23259         
23260         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23261             /*
23262              * place to the top of element!
23263              *
23264              */
23265             
23266             this.picker().addClass('top');
23267             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23268             
23269             return;
23270         }
23271         
23272         this.picker().addClass('bottom');
23273         
23274         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23275     },
23276     
23277     parseDate : function(value)
23278     {
23279         if(!value || value instanceof Date){
23280             return value;
23281         }
23282         var v = Date.parseDate(value, this.format);
23283         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23284             v = Date.parseDate(value, 'Y-m-d');
23285         }
23286         if(!v && this.altFormats){
23287             if(!this.altFormatsArray){
23288                 this.altFormatsArray = this.altFormats.split("|");
23289             }
23290             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23291                 v = Date.parseDate(value, this.altFormatsArray[i]);
23292             }
23293         }
23294         return v;
23295     },
23296     
23297     formatDate : function(date, fmt)
23298     {   
23299         return (!date || !(date instanceof Date)) ?
23300         date : date.dateFormat(fmt || this.format);
23301     },
23302     
23303     onFocus : function()
23304     {
23305         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23306         this.showPopup();
23307     },
23308     
23309     onBlur : function()
23310     {
23311         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23312         
23313         var d = this.inputEl().getValue();
23314         
23315         this.setValue(d);
23316                 
23317         this.hidePopup();
23318     },
23319     
23320     showPopup : function()
23321     {
23322         this.picker().show();
23323         this.update();
23324         this.place();
23325         
23326         this.fireEvent('showpopup', this, this.date);
23327     },
23328     
23329     hidePopup : function()
23330     {
23331         if(this.isInline) {
23332             return;
23333         }
23334         this.picker().hide();
23335         this.viewMode = this.startViewMode;
23336         this.showMode();
23337         
23338         this.fireEvent('hidepopup', this, this.date);
23339         
23340     },
23341     
23342     onMousedown: function(e)
23343     {
23344         e.stopPropagation();
23345         e.preventDefault();
23346     },
23347     
23348     keyup: function(e)
23349     {
23350         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23351         this.update();
23352     },
23353
23354     setValue: function(v)
23355     {
23356         if(this.fireEvent('beforeselect', this, v) !== false){
23357             var d = new Date(this.parseDate(v) ).clearTime();
23358         
23359             if(isNaN(d.getTime())){
23360                 this.date = this.viewDate = '';
23361                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23362                 return;
23363             }
23364
23365             v = this.formatDate(d);
23366
23367             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23368
23369             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23370
23371             this.update();
23372
23373             this.fireEvent('select', this, this.date);
23374         }
23375     },
23376     
23377     getValue: function()
23378     {
23379         return this.formatDate(this.date);
23380     },
23381     
23382     fireKey: function(e)
23383     {
23384         if (!this.picker().isVisible()){
23385             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23386                 this.showPopup();
23387             }
23388             return;
23389         }
23390         
23391         var dateChanged = false,
23392         dir, day, month,
23393         newDate, newViewDate;
23394         
23395         switch(e.keyCode){
23396             case 27: // escape
23397                 this.hidePopup();
23398                 e.preventDefault();
23399                 break;
23400             case 37: // left
23401             case 39: // right
23402                 if (!this.keyboardNavigation) {
23403                     break;
23404                 }
23405                 dir = e.keyCode == 37 ? -1 : 1;
23406                 
23407                 if (e.ctrlKey){
23408                     newDate = this.moveYear(this.date, dir);
23409                     newViewDate = this.moveYear(this.viewDate, dir);
23410                 } else if (e.shiftKey){
23411                     newDate = this.moveMonth(this.date, dir);
23412                     newViewDate = this.moveMonth(this.viewDate, dir);
23413                 } else {
23414                     newDate = new Date(this.date);
23415                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23416                     newViewDate = new Date(this.viewDate);
23417                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23418                 }
23419                 if (this.dateWithinRange(newDate)){
23420                     this.date = newDate;
23421                     this.viewDate = newViewDate;
23422                     this.setValue(this.formatDate(this.date));
23423 //                    this.update();
23424                     e.preventDefault();
23425                     dateChanged = true;
23426                 }
23427                 break;
23428             case 38: // up
23429             case 40: // down
23430                 if (!this.keyboardNavigation) {
23431                     break;
23432                 }
23433                 dir = e.keyCode == 38 ? -1 : 1;
23434                 if (e.ctrlKey){
23435                     newDate = this.moveYear(this.date, dir);
23436                     newViewDate = this.moveYear(this.viewDate, dir);
23437                 } else if (e.shiftKey){
23438                     newDate = this.moveMonth(this.date, dir);
23439                     newViewDate = this.moveMonth(this.viewDate, dir);
23440                 } else {
23441                     newDate = new Date(this.date);
23442                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23443                     newViewDate = new Date(this.viewDate);
23444                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23445                 }
23446                 if (this.dateWithinRange(newDate)){
23447                     this.date = newDate;
23448                     this.viewDate = newViewDate;
23449                     this.setValue(this.formatDate(this.date));
23450 //                    this.update();
23451                     e.preventDefault();
23452                     dateChanged = true;
23453                 }
23454                 break;
23455             case 13: // enter
23456                 this.setValue(this.formatDate(this.date));
23457                 this.hidePopup();
23458                 e.preventDefault();
23459                 break;
23460             case 9: // tab
23461                 this.setValue(this.formatDate(this.date));
23462                 this.hidePopup();
23463                 break;
23464             case 16: // shift
23465             case 17: // ctrl
23466             case 18: // alt
23467                 break;
23468             default :
23469                 this.hidePopup();
23470                 
23471         }
23472     },
23473     
23474     
23475     onClick: function(e) 
23476     {
23477         e.stopPropagation();
23478         e.preventDefault();
23479         
23480         var target = e.getTarget();
23481         
23482         if(target.nodeName.toLowerCase() === 'i'){
23483             target = Roo.get(target).dom.parentNode;
23484         }
23485         
23486         var nodeName = target.nodeName;
23487         var className = target.className;
23488         var html = target.innerHTML;
23489         //Roo.log(nodeName);
23490         
23491         switch(nodeName.toLowerCase()) {
23492             case 'th':
23493                 switch(className) {
23494                     case 'switch':
23495                         this.showMode(1);
23496                         break;
23497                     case 'prev':
23498                     case 'next':
23499                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23500                         switch(this.viewMode){
23501                                 case 0:
23502                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23503                                         break;
23504                                 case 1:
23505                                 case 2:
23506                                         this.viewDate = this.moveYear(this.viewDate, dir);
23507                                         break;
23508                         }
23509                         this.fill();
23510                         break;
23511                     case 'today':
23512                         var date = new Date();
23513                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23514 //                        this.fill()
23515                         this.setValue(this.formatDate(this.date));
23516                         
23517                         this.hidePopup();
23518                         break;
23519                 }
23520                 break;
23521             case 'span':
23522                 if (className.indexOf('disabled') < 0) {
23523                 if (!this.viewDate) {
23524                     this.viewDate = new Date();
23525                 }
23526                 this.viewDate.setUTCDate(1);
23527                     if (className.indexOf('month') > -1) {
23528                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23529                     } else {
23530                         var year = parseInt(html, 10) || 0;
23531                         this.viewDate.setUTCFullYear(year);
23532                         
23533                     }
23534                     
23535                     if(this.singleMode){
23536                         this.setValue(this.formatDate(this.viewDate));
23537                         this.hidePopup();
23538                         return;
23539                     }
23540                     
23541                     this.showMode(-1);
23542                     this.fill();
23543                 }
23544                 break;
23545                 
23546             case 'td':
23547                 //Roo.log(className);
23548                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23549                     var day = parseInt(html, 10) || 1;
23550                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23551                         month = (this.viewDate || new Date()).getUTCMonth();
23552
23553                     if (className.indexOf('old') > -1) {
23554                         if(month === 0 ){
23555                             month = 11;
23556                             year -= 1;
23557                         }else{
23558                             month -= 1;
23559                         }
23560                     } else if (className.indexOf('new') > -1) {
23561                         if (month == 11) {
23562                             month = 0;
23563                             year += 1;
23564                         } else {
23565                             month += 1;
23566                         }
23567                     }
23568                     //Roo.log([year,month,day]);
23569                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23570                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23571 //                    this.fill();
23572                     //Roo.log(this.formatDate(this.date));
23573                     this.setValue(this.formatDate(this.date));
23574                     this.hidePopup();
23575                 }
23576                 break;
23577         }
23578     },
23579     
23580     setStartDate: function(startDate)
23581     {
23582         this.startDate = startDate || -Infinity;
23583         if (this.startDate !== -Infinity) {
23584             this.startDate = this.parseDate(this.startDate);
23585         }
23586         this.update();
23587         this.updateNavArrows();
23588     },
23589
23590     setEndDate: function(endDate)
23591     {
23592         this.endDate = endDate || Infinity;
23593         if (this.endDate !== Infinity) {
23594             this.endDate = this.parseDate(this.endDate);
23595         }
23596         this.update();
23597         this.updateNavArrows();
23598     },
23599     
23600     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23601     {
23602         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23603         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23604             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23605         }
23606         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23607             return parseInt(d, 10);
23608         });
23609         this.update();
23610         this.updateNavArrows();
23611     },
23612     
23613     updateNavArrows: function() 
23614     {
23615         if(this.singleMode){
23616             return;
23617         }
23618         
23619         var d = new Date(this.viewDate),
23620         year = d.getUTCFullYear(),
23621         month = d.getUTCMonth();
23622         
23623         Roo.each(this.picker().select('.prev', true).elements, function(v){
23624             v.show();
23625             switch (this.viewMode) {
23626                 case 0:
23627
23628                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23629                         v.hide();
23630                     }
23631                     break;
23632                 case 1:
23633                 case 2:
23634                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23635                         v.hide();
23636                     }
23637                     break;
23638             }
23639         });
23640         
23641         Roo.each(this.picker().select('.next', true).elements, function(v){
23642             v.show();
23643             switch (this.viewMode) {
23644                 case 0:
23645
23646                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23647                         v.hide();
23648                     }
23649                     break;
23650                 case 1:
23651                 case 2:
23652                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23653                         v.hide();
23654                     }
23655                     break;
23656             }
23657         })
23658     },
23659     
23660     moveMonth: function(date, dir)
23661     {
23662         if (!dir) {
23663             return date;
23664         }
23665         var new_date = new Date(date.valueOf()),
23666         day = new_date.getUTCDate(),
23667         month = new_date.getUTCMonth(),
23668         mag = Math.abs(dir),
23669         new_month, test;
23670         dir = dir > 0 ? 1 : -1;
23671         if (mag == 1){
23672             test = dir == -1
23673             // If going back one month, make sure month is not current month
23674             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23675             ? function(){
23676                 return new_date.getUTCMonth() == month;
23677             }
23678             // If going forward one month, make sure month is as expected
23679             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23680             : function(){
23681                 return new_date.getUTCMonth() != new_month;
23682             };
23683             new_month = month + dir;
23684             new_date.setUTCMonth(new_month);
23685             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23686             if (new_month < 0 || new_month > 11) {
23687                 new_month = (new_month + 12) % 12;
23688             }
23689         } else {
23690             // For magnitudes >1, move one month at a time...
23691             for (var i=0; i<mag; i++) {
23692                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23693                 new_date = this.moveMonth(new_date, dir);
23694             }
23695             // ...then reset the day, keeping it in the new month
23696             new_month = new_date.getUTCMonth();
23697             new_date.setUTCDate(day);
23698             test = function(){
23699                 return new_month != new_date.getUTCMonth();
23700             };
23701         }
23702         // Common date-resetting loop -- if date is beyond end of month, make it
23703         // end of month
23704         while (test()){
23705             new_date.setUTCDate(--day);
23706             new_date.setUTCMonth(new_month);
23707         }
23708         return new_date;
23709     },
23710
23711     moveYear: function(date, dir)
23712     {
23713         return this.moveMonth(date, dir*12);
23714     },
23715
23716     dateWithinRange: function(date)
23717     {
23718         return date >= this.startDate && date <= this.endDate;
23719     },
23720
23721     
23722     remove: function() 
23723     {
23724         this.picker().remove();
23725     },
23726     
23727     validateValue : function(value)
23728     {
23729         if(this.getVisibilityEl().hasClass('hidden')){
23730             return true;
23731         }
23732         
23733         if(value.length < 1)  {
23734             if(this.allowBlank){
23735                 return true;
23736             }
23737             return false;
23738         }
23739         
23740         if(value.length < this.minLength){
23741             return false;
23742         }
23743         if(value.length > this.maxLength){
23744             return false;
23745         }
23746         if(this.vtype){
23747             var vt = Roo.form.VTypes;
23748             if(!vt[this.vtype](value, this)){
23749                 return false;
23750             }
23751         }
23752         if(typeof this.validator == "function"){
23753             var msg = this.validator(value);
23754             if(msg !== true){
23755                 return false;
23756             }
23757         }
23758         
23759         if(this.regex && !this.regex.test(value)){
23760             return false;
23761         }
23762         
23763         if(typeof(this.parseDate(value)) == 'undefined'){
23764             return false;
23765         }
23766         
23767         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23768             return false;
23769         }      
23770         
23771         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23772             return false;
23773         } 
23774         
23775         
23776         return true;
23777     },
23778     
23779     reset : function()
23780     {
23781         this.date = this.viewDate = '';
23782         
23783         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23784     }
23785    
23786 });
23787
23788 Roo.apply(Roo.bootstrap.form.DateField,  {
23789     
23790     head : {
23791         tag: 'thead',
23792         cn: [
23793         {
23794             tag: 'tr',
23795             cn: [
23796             {
23797                 tag: 'th',
23798                 cls: 'prev',
23799                 html: '<i class="fa fa-arrow-left"/>'
23800             },
23801             {
23802                 tag: 'th',
23803                 cls: 'switch',
23804                 colspan: '5'
23805             },
23806             {
23807                 tag: 'th',
23808                 cls: 'next',
23809                 html: '<i class="fa fa-arrow-right"/>'
23810             }
23811
23812             ]
23813         }
23814         ]
23815     },
23816     
23817     content : {
23818         tag: 'tbody',
23819         cn: [
23820         {
23821             tag: 'tr',
23822             cn: [
23823             {
23824                 tag: 'td',
23825                 colspan: '7'
23826             }
23827             ]
23828         }
23829         ]
23830     },
23831     
23832     footer : {
23833         tag: 'tfoot',
23834         cn: [
23835         {
23836             tag: 'tr',
23837             cn: [
23838             {
23839                 tag: 'th',
23840                 colspan: '7',
23841                 cls: 'today'
23842             }
23843                     
23844             ]
23845         }
23846         ]
23847     },
23848     
23849     dates:{
23850         en: {
23851             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23852             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23853             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23854             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23855             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23856             today: "Today"
23857         }
23858     },
23859     
23860     modes: [
23861     {
23862         clsName: 'days',
23863         navFnc: 'Month',
23864         navStep: 1
23865     },
23866     {
23867         clsName: 'months',
23868         navFnc: 'FullYear',
23869         navStep: 1
23870     },
23871     {
23872         clsName: 'years',
23873         navFnc: 'FullYear',
23874         navStep: 10
23875     }]
23876 });
23877
23878 Roo.apply(Roo.bootstrap.form.DateField,  {
23879   
23880     template : {
23881         tag: 'div',
23882         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23883         cn: [
23884         {
23885             tag: 'div',
23886             cls: 'datepicker-days',
23887             cn: [
23888             {
23889                 tag: 'table',
23890                 cls: 'table-condensed',
23891                 cn:[
23892                 Roo.bootstrap.form.DateField.head,
23893                 {
23894                     tag: 'tbody'
23895                 },
23896                 Roo.bootstrap.form.DateField.footer
23897                 ]
23898             }
23899             ]
23900         },
23901         {
23902             tag: 'div',
23903             cls: 'datepicker-months',
23904             cn: [
23905             {
23906                 tag: 'table',
23907                 cls: 'table-condensed',
23908                 cn:[
23909                 Roo.bootstrap.form.DateField.head,
23910                 Roo.bootstrap.form.DateField.content,
23911                 Roo.bootstrap.form.DateField.footer
23912                 ]
23913             }
23914             ]
23915         },
23916         {
23917             tag: 'div',
23918             cls: 'datepicker-years',
23919             cn: [
23920             {
23921                 tag: 'table',
23922                 cls: 'table-condensed',
23923                 cn:[
23924                 Roo.bootstrap.form.DateField.head,
23925                 Roo.bootstrap.form.DateField.content,
23926                 Roo.bootstrap.form.DateField.footer
23927                 ]
23928             }
23929             ]
23930         }
23931         ]
23932     }
23933 });
23934
23935  
23936
23937  /*
23938  * - LGPL
23939  *
23940  * TimeField
23941  * 
23942  */
23943
23944 /**
23945  * @class Roo.bootstrap.form.TimeField
23946  * @extends Roo.bootstrap.form.Input
23947  * Bootstrap DateField class
23948  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23949  * 
23950  * 
23951  * @constructor
23952  * Create a new TimeField
23953  * @param {Object} config The config object
23954  */
23955
23956 Roo.bootstrap.form.TimeField = function(config){
23957     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23958     this.addEvents({
23959             /**
23960              * @event show
23961              * Fires when this field show.
23962              * @param {Roo.bootstrap.form.DateField} thisthis
23963              * @param {Mixed} date The date value
23964              */
23965             show : true,
23966             /**
23967              * @event show
23968              * Fires when this field hide.
23969              * @param {Roo.bootstrap.form.DateField} this
23970              * @param {Mixed} date The date value
23971              */
23972             hide : true,
23973             /**
23974              * @event select
23975              * Fires when select a date.
23976              * @param {Roo.bootstrap.form.DateField} this
23977              * @param {Mixed} date The date value
23978              */
23979             select : true
23980         });
23981 };
23982
23983 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23984     
23985     /**
23986      * @cfg {String} format
23987      * The default time format string which can be overriden for localization support.  The format must be
23988      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23989      */
23990     format : "H:i",
23991     minuteStep : 1,
23992
23993     getAutoCreate : function()
23994     {
23995         this.after = '<i class="fa far fa-clock"></i>';
23996         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23997         
23998          
23999     },
24000     onRender: function(ct, position)
24001     {
24002         
24003         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24004                 
24005         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24006         
24007         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24008         
24009         this.pop = this.picker().select('>.datepicker-time',true).first();
24010         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24011         
24012         this.picker().on('mousedown', this.onMousedown, this);
24013         this.picker().on('click', this.onClick, this);
24014         
24015         this.picker().addClass('datepicker-dropdown');
24016     
24017         this.fillTime();
24018         this.update();
24019             
24020         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24021         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24022         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24023         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24024         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24025         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24026
24027     },
24028     
24029     fireKey: function(e){
24030         if (!this.picker().isVisible()){
24031             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24032                 this.show();
24033             }
24034             return;
24035         }
24036
24037         e.preventDefault();
24038         
24039         switch(e.keyCode){
24040             case 27: // escape
24041                 this.hide();
24042                 break;
24043             case 37: // left
24044             case 39: // right
24045                 this.onTogglePeriod();
24046                 break;
24047             case 38: // up
24048                 this.onIncrementMinutes();
24049                 break;
24050             case 40: // down
24051                 this.onDecrementMinutes();
24052                 break;
24053             case 13: // enter
24054             case 9: // tab
24055                 this.setTime();
24056                 break;
24057         }
24058     },
24059     
24060     onClick: function(e) {
24061         e.stopPropagation();
24062         e.preventDefault();
24063     },
24064     
24065     picker : function()
24066     {
24067         return this.pickerEl;
24068     },
24069     
24070     fillTime: function()
24071     {    
24072         var time = this.pop.select('tbody', true).first();
24073         
24074         time.dom.innerHTML = '';
24075         
24076         time.createChild({
24077             tag: 'tr',
24078             cn: [
24079                 {
24080                     tag: 'td',
24081                     cn: [
24082                         {
24083                             tag: 'a',
24084                             href: '#',
24085                             cls: 'btn',
24086                             cn: [
24087                                 {
24088                                     tag: 'i',
24089                                     cls: 'hours-up fa fas fa-chevron-up'
24090                                 }
24091                             ]
24092                         } 
24093                     ]
24094                 },
24095                 {
24096                     tag: 'td',
24097                     cls: 'separator'
24098                 },
24099                 {
24100                     tag: 'td',
24101                     cn: [
24102                         {
24103                             tag: 'a',
24104                             href: '#',
24105                             cls: 'btn',
24106                             cn: [
24107                                 {
24108                                     tag: 'i',
24109                                     cls: 'minutes-up fa fas fa-chevron-up'
24110                                 }
24111                             ]
24112                         }
24113                     ]
24114                 },
24115                 {
24116                     tag: 'td',
24117                     cls: 'separator'
24118                 }
24119             ]
24120         });
24121         
24122         time.createChild({
24123             tag: 'tr',
24124             cn: [
24125                 {
24126                     tag: 'td',
24127                     cn: [
24128                         {
24129                             tag: 'span',
24130                             cls: 'timepicker-hour',
24131                             html: '00'
24132                         }  
24133                     ]
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cls: 'separator',
24138                     html: ':'
24139                 },
24140                 {
24141                     tag: 'td',
24142                     cn: [
24143                         {
24144                             tag: 'span',
24145                             cls: 'timepicker-minute',
24146                             html: '00'
24147                         }  
24148                     ]
24149                 },
24150                 {
24151                     tag: 'td',
24152                     cls: 'separator'
24153                 },
24154                 {
24155                     tag: 'td',
24156                     cn: [
24157                         {
24158                             tag: 'button',
24159                             type: 'button',
24160                             cls: 'btn btn-primary period',
24161                             html: 'AM'
24162                             
24163                         }
24164                     ]
24165                 }
24166             ]
24167         });
24168         
24169         time.createChild({
24170             tag: 'tr',
24171             cn: [
24172                 {
24173                     tag: 'td',
24174                     cn: [
24175                         {
24176                             tag: 'a',
24177                             href: '#',
24178                             cls: 'btn',
24179                             cn: [
24180                                 {
24181                                     tag: 'span',
24182                                     cls: 'hours-down fa fas fa-chevron-down'
24183                                 }
24184                             ]
24185                         }
24186                     ]
24187                 },
24188                 {
24189                     tag: 'td',
24190                     cls: 'separator'
24191                 },
24192                 {
24193                     tag: 'td',
24194                     cn: [
24195                         {
24196                             tag: 'a',
24197                             href: '#',
24198                             cls: 'btn',
24199                             cn: [
24200                                 {
24201                                     tag: 'span',
24202                                     cls: 'minutes-down fa fas fa-chevron-down'
24203                                 }
24204                             ]
24205                         }
24206                     ]
24207                 },
24208                 {
24209                     tag: 'td',
24210                     cls: 'separator'
24211                 }
24212             ]
24213         });
24214         
24215     },
24216     
24217     update: function()
24218     {
24219         // default minute is a multiple of minuteStep
24220         if(typeof(this.time) === 'undefined') {
24221             this.time = new Date();
24222             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24223         }
24224         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24225         
24226         this.fill();
24227     },
24228     
24229     fill: function() 
24230     {
24231         var hours = this.time.getHours();
24232         var minutes = this.time.getMinutes();
24233         var period = 'AM';
24234         
24235         if(hours > 11){
24236             period = 'PM';
24237         }
24238         
24239         if(hours == 0){
24240             hours = 12;
24241         }
24242         
24243         
24244         if(hours > 12){
24245             hours = hours - 12;
24246         }
24247         
24248         if(hours < 10){
24249             hours = '0' + hours;
24250         }
24251         
24252         if(minutes < 10){
24253             minutes = '0' + minutes;
24254         }
24255         
24256         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24257         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24258         this.pop.select('button', true).first().dom.innerHTML = period;
24259         
24260     },
24261     
24262     place: function()
24263     {   
24264         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24265         
24266         var cls = ['bottom'];
24267         
24268         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24269             cls.pop();
24270             cls.push('top');
24271         }
24272         
24273         cls.push('right');
24274         
24275         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24276             cls.pop();
24277             cls.push('left');
24278         }
24279         //this.picker().setXY(20000,20000);
24280         this.picker().addClass(cls.join('-'));
24281         
24282         var _this = this;
24283         
24284         Roo.each(cls, function(c){
24285             if(c == 'bottom'){
24286                 (function() {
24287                  //  
24288                 }).defer(200);
24289                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24290                 //_this.picker().setTop(_this.inputEl().getHeight());
24291                 return;
24292             }
24293             if(c == 'top'){
24294                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24295                 
24296                 //_this.picker().setTop(0 - _this.picker().getHeight());
24297                 return;
24298             }
24299             /*
24300             if(c == 'left'){
24301                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24302                 return;
24303             }
24304             if(c == 'right'){
24305                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24306                 return;
24307             }
24308             */
24309         });
24310         
24311     },
24312   
24313     onFocus : function()
24314     {
24315         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24316         this.show();
24317     },
24318     
24319     onBlur : function()
24320     {
24321         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24322         this.hide();
24323     },
24324     
24325     show : function()
24326     {
24327         this.picker().show();
24328         this.pop.show();
24329         this.update();
24330         this.place();
24331         
24332         this.fireEvent('show', this, this.date);
24333     },
24334     
24335     hide : function()
24336     {
24337         this.picker().hide();
24338         this.pop.hide();
24339         
24340         this.fireEvent('hide', this, this.date);
24341     },
24342     
24343     setTime : function()
24344     {
24345         this.hide();
24346         this.setValue(this.time.format(this.format));
24347         
24348         this.fireEvent('select', this, this.date);
24349         
24350         
24351     },
24352     
24353     onMousedown: function(e){
24354         e.stopPropagation();
24355         e.preventDefault();
24356     },
24357     
24358     onIncrementHours: function()
24359     {
24360         Roo.log('onIncrementHours');
24361         this.time = this.time.add(Date.HOUR, 1);
24362         this.update();
24363         
24364     },
24365     
24366     onDecrementHours: function()
24367     {
24368         Roo.log('onDecrementHours');
24369         this.time = this.time.add(Date.HOUR, -1);
24370         this.update();
24371     },
24372     
24373     onIncrementMinutes: function()
24374     {
24375         Roo.log('onIncrementMinutes');
24376         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24377         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24378         this.update();
24379     },
24380     
24381     onDecrementMinutes: function()
24382     {
24383         Roo.log('onDecrementMinutes');
24384         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24385         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24386         this.update();
24387     },
24388     
24389     onTogglePeriod: function()
24390     {
24391         Roo.log('onTogglePeriod');
24392         this.time = this.time.add(Date.HOUR, 12);
24393         this.update();
24394     }
24395     
24396    
24397 });
24398  
24399
24400 Roo.apply(Roo.bootstrap.form.TimeField,  {
24401   
24402     template : {
24403         tag: 'div',
24404         cls: 'datepicker dropdown-menu',
24405         cn: [
24406             {
24407                 tag: 'div',
24408                 cls: 'datepicker-time',
24409                 cn: [
24410                 {
24411                     tag: 'table',
24412                     cls: 'table-condensed',
24413                     cn:[
24414                         {
24415                             tag: 'tbody',
24416                             cn: [
24417                                 {
24418                                     tag: 'tr',
24419                                     cn: [
24420                                     {
24421                                         tag: 'td',
24422                                         colspan: '7'
24423                                     }
24424                                     ]
24425                                 }
24426                             ]
24427                         },
24428                         {
24429                             tag: 'tfoot',
24430                             cn: [
24431                                 {
24432                                     tag: 'tr',
24433                                     cn: [
24434                                     {
24435                                         tag: 'th',
24436                                         colspan: '7',
24437                                         cls: '',
24438                                         cn: [
24439                                             {
24440                                                 tag: 'button',
24441                                                 cls: 'btn btn-info ok',
24442                                                 html: 'OK'
24443                                             }
24444                                         ]
24445                                     }
24446                     
24447                                     ]
24448                                 }
24449                             ]
24450                         }
24451                     ]
24452                 }
24453                 ]
24454             }
24455         ]
24456     }
24457 });
24458
24459  
24460
24461  /*
24462  * - LGPL
24463  *
24464  * MonthField
24465  * 
24466  */
24467
24468 /**
24469  * @class Roo.bootstrap.form.MonthField
24470  * @extends Roo.bootstrap.form.Input
24471  * Bootstrap MonthField class
24472  * 
24473  * @cfg {String} language default en
24474  * 
24475  * @constructor
24476  * Create a new MonthField
24477  * @param {Object} config The config object
24478  */
24479
24480 Roo.bootstrap.form.MonthField = function(config){
24481     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24482     
24483     this.addEvents({
24484         /**
24485          * @event show
24486          * Fires when this field show.
24487          * @param {Roo.bootstrap.form.MonthField} this
24488          * @param {Mixed} date The date value
24489          */
24490         show : true,
24491         /**
24492          * @event show
24493          * Fires when this field hide.
24494          * @param {Roo.bootstrap.form.MonthField} this
24495          * @param {Mixed} date The date value
24496          */
24497         hide : true,
24498         /**
24499          * @event select
24500          * Fires when select a date.
24501          * @param {Roo.bootstrap.form.MonthField} this
24502          * @param {String} oldvalue The old value
24503          * @param {String} newvalue The new value
24504          */
24505         select : true
24506     });
24507 };
24508
24509 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24510     
24511     onRender: function(ct, position)
24512     {
24513         
24514         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24515         
24516         this.language = this.language || 'en';
24517         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24518         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24519         
24520         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24521         this.isInline = false;
24522         this.isInput = true;
24523         this.component = this.el.select('.add-on', true).first() || false;
24524         this.component = (this.component && this.component.length === 0) ? false : this.component;
24525         this.hasInput = this.component && this.inputEL().length;
24526         
24527         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24528         
24529         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24530         
24531         this.picker().on('mousedown', this.onMousedown, this);
24532         this.picker().on('click', this.onClick, this);
24533         
24534         this.picker().addClass('datepicker-dropdown');
24535         
24536         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24537             v.setStyle('width', '189px');
24538         });
24539         
24540         this.fillMonths();
24541         
24542         this.update();
24543         
24544         if(this.isInline) {
24545             this.show();
24546         }
24547         
24548     },
24549     
24550     setValue: function(v, suppressEvent)
24551     {   
24552         var o = this.getValue();
24553         
24554         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24555         
24556         this.update();
24557
24558         if(suppressEvent !== true){
24559             this.fireEvent('select', this, o, v);
24560         }
24561         
24562     },
24563     
24564     getValue: function()
24565     {
24566         return this.value;
24567     },
24568     
24569     onClick: function(e) 
24570     {
24571         e.stopPropagation();
24572         e.preventDefault();
24573         
24574         var target = e.getTarget();
24575         
24576         if(target.nodeName.toLowerCase() === 'i'){
24577             target = Roo.get(target).dom.parentNode;
24578         }
24579         
24580         var nodeName = target.nodeName;
24581         var className = target.className;
24582         var html = target.innerHTML;
24583         
24584         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24585             return;
24586         }
24587         
24588         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24589         
24590         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24591         
24592         this.hide();
24593                         
24594     },
24595     
24596     picker : function()
24597     {
24598         return this.pickerEl;
24599     },
24600     
24601     fillMonths: function()
24602     {    
24603         var i = 0;
24604         var months = this.picker().select('>.datepicker-months td', true).first();
24605         
24606         months.dom.innerHTML = '';
24607         
24608         while (i < 12) {
24609             var month = {
24610                 tag: 'span',
24611                 cls: 'month',
24612                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24613             };
24614             
24615             months.createChild(month);
24616         }
24617         
24618     },
24619     
24620     update: function()
24621     {
24622         var _this = this;
24623         
24624         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24625             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24626         }
24627         
24628         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24629             e.removeClass('active');
24630             
24631             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24632                 e.addClass('active');
24633             }
24634         })
24635     },
24636     
24637     place: function()
24638     {
24639         if(this.isInline) {
24640             return;
24641         }
24642         
24643         this.picker().removeClass(['bottom', 'top']);
24644         
24645         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24646             /*
24647              * place to the top of element!
24648              *
24649              */
24650             
24651             this.picker().addClass('top');
24652             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24653             
24654             return;
24655         }
24656         
24657         this.picker().addClass('bottom');
24658         
24659         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24660     },
24661     
24662     onFocus : function()
24663     {
24664         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24665         this.show();
24666     },
24667     
24668     onBlur : function()
24669     {
24670         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24671         
24672         var d = this.inputEl().getValue();
24673         
24674         this.setValue(d);
24675                 
24676         this.hide();
24677     },
24678     
24679     show : function()
24680     {
24681         this.picker().show();
24682         this.picker().select('>.datepicker-months', true).first().show();
24683         this.update();
24684         this.place();
24685         
24686         this.fireEvent('show', this, this.date);
24687     },
24688     
24689     hide : function()
24690     {
24691         if(this.isInline) {
24692             return;
24693         }
24694         this.picker().hide();
24695         this.fireEvent('hide', this, this.date);
24696         
24697     },
24698     
24699     onMousedown: function(e)
24700     {
24701         e.stopPropagation();
24702         e.preventDefault();
24703     },
24704     
24705     keyup: function(e)
24706     {
24707         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24708         this.update();
24709     },
24710
24711     fireKey: function(e)
24712     {
24713         if (!this.picker().isVisible()){
24714             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24715                 this.show();
24716             }
24717             return;
24718         }
24719         
24720         var dir;
24721         
24722         switch(e.keyCode){
24723             case 27: // escape
24724                 this.hide();
24725                 e.preventDefault();
24726                 break;
24727             case 37: // left
24728             case 39: // right
24729                 dir = e.keyCode == 37 ? -1 : 1;
24730                 
24731                 this.vIndex = this.vIndex + dir;
24732                 
24733                 if(this.vIndex < 0){
24734                     this.vIndex = 0;
24735                 }
24736                 
24737                 if(this.vIndex > 11){
24738                     this.vIndex = 11;
24739                 }
24740                 
24741                 if(isNaN(this.vIndex)){
24742                     this.vIndex = 0;
24743                 }
24744                 
24745                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24746                 
24747                 break;
24748             case 38: // up
24749             case 40: // down
24750                 
24751                 dir = e.keyCode == 38 ? -1 : 1;
24752                 
24753                 this.vIndex = this.vIndex + dir * 4;
24754                 
24755                 if(this.vIndex < 0){
24756                     this.vIndex = 0;
24757                 }
24758                 
24759                 if(this.vIndex > 11){
24760                     this.vIndex = 11;
24761                 }
24762                 
24763                 if(isNaN(this.vIndex)){
24764                     this.vIndex = 0;
24765                 }
24766                 
24767                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24768                 break;
24769                 
24770             case 13: // enter
24771                 
24772                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24773                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24774                 }
24775                 
24776                 this.hide();
24777                 e.preventDefault();
24778                 break;
24779             case 9: // tab
24780                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24781                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24782                 }
24783                 this.hide();
24784                 break;
24785             case 16: // shift
24786             case 17: // ctrl
24787             case 18: // alt
24788                 break;
24789             default :
24790                 this.hide();
24791                 
24792         }
24793     },
24794     
24795     remove: function() 
24796     {
24797         this.picker().remove();
24798     }
24799    
24800 });
24801
24802 Roo.apply(Roo.bootstrap.form.MonthField,  {
24803     
24804     content : {
24805         tag: 'tbody',
24806         cn: [
24807         {
24808             tag: 'tr',
24809             cn: [
24810             {
24811                 tag: 'td',
24812                 colspan: '7'
24813             }
24814             ]
24815         }
24816         ]
24817     },
24818     
24819     dates:{
24820         en: {
24821             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24822             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24823         }
24824     }
24825 });
24826
24827 Roo.apply(Roo.bootstrap.form.MonthField,  {
24828   
24829     template : {
24830         tag: 'div',
24831         cls: 'datepicker dropdown-menu roo-dynamic',
24832         cn: [
24833             {
24834                 tag: 'div',
24835                 cls: 'datepicker-months',
24836                 cn: [
24837                 {
24838                     tag: 'table',
24839                     cls: 'table-condensed',
24840                     cn:[
24841                         Roo.bootstrap.form.DateField.content
24842                     ]
24843                 }
24844                 ]
24845             }
24846         ]
24847     }
24848 });
24849
24850  
24851
24852  
24853  /*
24854  * - LGPL
24855  *
24856  * CheckBox
24857  * 
24858  */
24859
24860 /**
24861  * @class Roo.bootstrap.form.CheckBox
24862  * @extends Roo.bootstrap.form.Input
24863  * Bootstrap CheckBox class
24864  * 
24865  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24866  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24867  * @cfg {String} boxLabel The text that appears beside the checkbox
24868  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24869  * @cfg {Boolean} checked initnal the element
24870  * @cfg {Boolean} inline inline the element (default false)
24871  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24872  * @cfg {String} tooltip label tooltip
24873  * 
24874  * @constructor
24875  * Create a new CheckBox
24876  * @param {Object} config The config object
24877  */
24878
24879 Roo.bootstrap.form.CheckBox = function(config){
24880     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24881    
24882     this.addEvents({
24883         /**
24884         * @event check
24885         * Fires when the element is checked or unchecked.
24886         * @param {Roo.bootstrap.form.CheckBox} this This input
24887         * @param {Boolean} checked The new checked value
24888         */
24889        check : true,
24890        /**
24891         * @event click
24892         * Fires when the element is click.
24893         * @param {Roo.bootstrap.form.CheckBox} this This input
24894         */
24895        click : true
24896     });
24897     
24898 };
24899
24900 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24901   
24902     inputType: 'checkbox',
24903     inputValue: 1,
24904     valueOff: 0,
24905     boxLabel: false,
24906     checked: false,
24907     weight : false,
24908     inline: false,
24909     tooltip : '',
24910     
24911     // checkbox success does not make any sense really.. 
24912     invalidClass : "",
24913     validClass : "",
24914     
24915     
24916     getAutoCreate : function()
24917     {
24918         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24919         
24920         var id = Roo.id();
24921         
24922         var cfg = {};
24923         
24924         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24925         
24926         if(this.inline){
24927             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24928         }
24929         
24930         var input =  {
24931             tag: 'input',
24932             id : id,
24933             type : this.inputType,
24934             value : this.inputValue,
24935             cls : 'roo-' + this.inputType, //'form-box',
24936             placeholder : this.placeholder || ''
24937             
24938         };
24939         
24940         if(this.inputType != 'radio'){
24941             var hidden =  {
24942                 tag: 'input',
24943                 type : 'hidden',
24944                 cls : 'roo-hidden-value',
24945                 value : this.checked ? this.inputValue : this.valueOff
24946             };
24947         }
24948         
24949             
24950         if (this.weight) { // Validity check?
24951             cfg.cls += " " + this.inputType + "-" + this.weight;
24952         }
24953         
24954         if (this.disabled) {
24955             input.disabled=true;
24956         }
24957         
24958         if(this.checked){
24959             input.checked = this.checked;
24960         }
24961         
24962         if (this.name) {
24963             
24964             input.name = this.name;
24965             
24966             if(this.inputType != 'radio'){
24967                 hidden.name = this.name;
24968                 input.name = '_hidden_' + this.name;
24969             }
24970         }
24971         
24972         if (this.size) {
24973             input.cls += ' input-' + this.size;
24974         }
24975         
24976         var settings=this;
24977         
24978         ['xs','sm','md','lg'].map(function(size){
24979             if (settings[size]) {
24980                 cfg.cls += ' col-' + size + '-' + settings[size];
24981             }
24982         });
24983         
24984         var inputblock = input;
24985          
24986         if (this.before || this.after) {
24987             
24988             inputblock = {
24989                 cls : 'input-group',
24990                 cn :  [] 
24991             };
24992             
24993             if (this.before) {
24994                 inputblock.cn.push({
24995                     tag :'span',
24996                     cls : 'input-group-addon',
24997                     html : this.before
24998                 });
24999             }
25000             
25001             inputblock.cn.push(input);
25002             
25003             if(this.inputType != 'radio'){
25004                 inputblock.cn.push(hidden);
25005             }
25006             
25007             if (this.after) {
25008                 inputblock.cn.push({
25009                     tag :'span',
25010                     cls : 'input-group-addon',
25011                     html : this.after
25012                 });
25013             }
25014             
25015         }
25016         var boxLabelCfg = false;
25017         
25018         if(this.boxLabel){
25019            
25020             boxLabelCfg = {
25021                 tag: 'label',
25022                 //'for': id, // box label is handled by onclick - so no for...
25023                 cls: 'box-label',
25024                 html: this.boxLabel
25025             };
25026             if(this.tooltip){
25027                 boxLabelCfg.tooltip = this.tooltip;
25028             }
25029              
25030         }
25031         
25032         
25033         if (align ==='left' && this.fieldLabel.length) {
25034 //                Roo.log("left and has label");
25035             cfg.cn = [
25036                 {
25037                     tag: 'label',
25038                     'for' :  id,
25039                     cls : 'control-label',
25040                     html : this.fieldLabel
25041                 },
25042                 {
25043                     cls : "", 
25044                     cn: [
25045                         inputblock
25046                     ]
25047                 }
25048             ];
25049             
25050             if (boxLabelCfg) {
25051                 cfg.cn[1].cn.push(boxLabelCfg);
25052             }
25053             
25054             if(this.labelWidth > 12){
25055                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25056             }
25057             
25058             if(this.labelWidth < 13 && this.labelmd == 0){
25059                 this.labelmd = this.labelWidth;
25060             }
25061             
25062             if(this.labellg > 0){
25063                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25064                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25065             }
25066             
25067             if(this.labelmd > 0){
25068                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25069                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25070             }
25071             
25072             if(this.labelsm > 0){
25073                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25074                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25075             }
25076             
25077             if(this.labelxs > 0){
25078                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25079                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25080             }
25081             
25082         } else if ( this.fieldLabel.length) {
25083 //                Roo.log(" label");
25084                 cfg.cn = [
25085                    
25086                     {
25087                         tag: this.boxLabel ? 'span' : 'label',
25088                         'for': id,
25089                         cls: 'control-label box-input-label',
25090                         //cls : 'input-group-addon',
25091                         html : this.fieldLabel
25092                     },
25093                     
25094                     inputblock
25095                     
25096                 ];
25097                 if (boxLabelCfg) {
25098                     cfg.cn.push(boxLabelCfg);
25099                 }
25100
25101         } else {
25102             
25103 //                Roo.log(" no label && no align");
25104                 cfg.cn = [  inputblock ] ;
25105                 if (boxLabelCfg) {
25106                     cfg.cn.push(boxLabelCfg);
25107                 }
25108
25109                 
25110         }
25111         
25112        
25113         
25114         if(this.inputType != 'radio'){
25115             cfg.cn.push(hidden);
25116         }
25117         
25118         return cfg;
25119         
25120     },
25121     
25122     /**
25123      * return the real input element.
25124      */
25125     inputEl: function ()
25126     {
25127         return this.el.select('input.roo-' + this.inputType,true).first();
25128     },
25129     hiddenEl: function ()
25130     {
25131         return this.el.select('input.roo-hidden-value',true).first();
25132     },
25133     
25134     labelEl: function()
25135     {
25136         return this.el.select('label.control-label',true).first();
25137     },
25138     /* depricated... */
25139     
25140     label: function()
25141     {
25142         return this.labelEl();
25143     },
25144     
25145     boxLabelEl: function()
25146     {
25147         return this.el.select('label.box-label',true).first();
25148     },
25149     
25150     initEvents : function()
25151     {
25152 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25153         
25154         this.inputEl().on('click', this.onClick,  this);
25155         
25156         if (this.boxLabel) { 
25157             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25158         }
25159         
25160         this.startValue = this.getValue();
25161         
25162         if(this.groupId){
25163             Roo.bootstrap.form.CheckBox.register(this);
25164         }
25165     },
25166     
25167     onClick : function(e)
25168     {   
25169         if(this.fireEvent('click', this, e) !== false){
25170             this.setChecked(!this.checked);
25171         }
25172         
25173     },
25174     
25175     setChecked : function(state,suppressEvent)
25176     {
25177         this.startValue = this.getValue();
25178
25179         if(this.inputType == 'radio'){
25180             
25181             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25182                 e.dom.checked = false;
25183             });
25184             
25185             this.inputEl().dom.checked = true;
25186             
25187             this.inputEl().dom.value = this.inputValue;
25188             
25189             if(suppressEvent !== true){
25190                 this.fireEvent('check', this, true);
25191             }
25192             
25193             this.validate();
25194             
25195             return;
25196         }
25197         
25198         this.checked = state;
25199         
25200         this.inputEl().dom.checked = state;
25201         
25202         
25203         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25204         
25205         if(suppressEvent !== true){
25206             this.fireEvent('check', this, state);
25207         }
25208         
25209         this.validate();
25210     },
25211     
25212     getValue : function()
25213     {
25214         if(this.inputType == 'radio'){
25215             return this.getGroupValue();
25216         }
25217         
25218         return this.hiddenEl().dom.value;
25219         
25220     },
25221     
25222     getGroupValue : function()
25223     {
25224         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25225             return '';
25226         }
25227         
25228         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25229     },
25230     
25231     setValue : function(v,suppressEvent)
25232     {
25233         if(this.inputType == 'radio'){
25234             this.setGroupValue(v, suppressEvent);
25235             return;
25236         }
25237         
25238         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25239         
25240         this.validate();
25241     },
25242     
25243     setGroupValue : function(v, suppressEvent)
25244     {
25245         this.startValue = this.getValue();
25246         
25247         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25248             e.dom.checked = false;
25249             
25250             if(e.dom.value == v){
25251                 e.dom.checked = true;
25252             }
25253         });
25254         
25255         if(suppressEvent !== true){
25256             this.fireEvent('check', this, true);
25257         }
25258
25259         this.validate();
25260         
25261         return;
25262     },
25263     
25264     validate : function()
25265     {
25266         if(this.getVisibilityEl().hasClass('hidden')){
25267             return true;
25268         }
25269         
25270         if(
25271                 this.disabled || 
25272                 (this.inputType == 'radio' && this.validateRadio()) ||
25273                 (this.inputType == 'checkbox' && this.validateCheckbox())
25274         ){
25275             this.markValid();
25276             return true;
25277         }
25278         
25279         this.markInvalid();
25280         return false;
25281     },
25282     
25283     validateRadio : function()
25284     {
25285         if(this.getVisibilityEl().hasClass('hidden')){
25286             return true;
25287         }
25288         
25289         if(this.allowBlank){
25290             return true;
25291         }
25292         
25293         var valid = false;
25294         
25295         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25296             if(!e.dom.checked){
25297                 return;
25298             }
25299             
25300             valid = true;
25301             
25302             return false;
25303         });
25304         
25305         return valid;
25306     },
25307     
25308     validateCheckbox : function()
25309     {
25310         if(!this.groupId){
25311             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25312             //return (this.getValue() == this.inputValue) ? true : false;
25313         }
25314         
25315         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25316         
25317         if(!group){
25318             return false;
25319         }
25320         
25321         var r = false;
25322         
25323         for(var i in group){
25324             if(group[i].el.isVisible(true)){
25325                 r = false;
25326                 break;
25327             }
25328             
25329             r = true;
25330         }
25331         
25332         for(var i in group){
25333             if(r){
25334                 break;
25335             }
25336             
25337             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25338         }
25339         
25340         return r;
25341     },
25342     
25343     /**
25344      * Mark this field as valid
25345      */
25346     markValid : function()
25347     {
25348         var _this = this;
25349         
25350         this.fireEvent('valid', this);
25351         
25352         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25353         
25354         if(this.groupId){
25355             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25356         }
25357         
25358         if(label){
25359             label.markValid();
25360         }
25361
25362         if(this.inputType == 'radio'){
25363             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25364                 var fg = e.findParent('.form-group', false, true);
25365                 if (Roo.bootstrap.version == 3) {
25366                     fg.removeClass([_this.invalidClass, _this.validClass]);
25367                     fg.addClass(_this.validClass);
25368                 } else {
25369                     fg.removeClass(['is-valid', 'is-invalid']);
25370                     fg.addClass('is-valid');
25371                 }
25372             });
25373             
25374             return;
25375         }
25376
25377         if(!this.groupId){
25378             var fg = this.el.findParent('.form-group', false, true);
25379             if (Roo.bootstrap.version == 3) {
25380                 fg.removeClass([this.invalidClass, this.validClass]);
25381                 fg.addClass(this.validClass);
25382             } else {
25383                 fg.removeClass(['is-valid', 'is-invalid']);
25384                 fg.addClass('is-valid');
25385             }
25386             return;
25387         }
25388         
25389         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25390         
25391         if(!group){
25392             return;
25393         }
25394         
25395         for(var i in group){
25396             var fg = group[i].el.findParent('.form-group', false, true);
25397             if (Roo.bootstrap.version == 3) {
25398                 fg.removeClass([this.invalidClass, this.validClass]);
25399                 fg.addClass(this.validClass);
25400             } else {
25401                 fg.removeClass(['is-valid', 'is-invalid']);
25402                 fg.addClass('is-valid');
25403             }
25404         }
25405     },
25406     
25407      /**
25408      * Mark this field as invalid
25409      * @param {String} msg The validation message
25410      */
25411     markInvalid : function(msg)
25412     {
25413         if(this.allowBlank){
25414             return;
25415         }
25416         
25417         var _this = this;
25418         
25419         this.fireEvent('invalid', this, msg);
25420         
25421         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25422         
25423         if(this.groupId){
25424             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25425         }
25426         
25427         if(label){
25428             label.markInvalid();
25429         }
25430             
25431         if(this.inputType == 'radio'){
25432             
25433             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25434                 var fg = e.findParent('.form-group', false, true);
25435                 if (Roo.bootstrap.version == 3) {
25436                     fg.removeClass([_this.invalidClass, _this.validClass]);
25437                     fg.addClass(_this.invalidClass);
25438                 } else {
25439                     fg.removeClass(['is-invalid', 'is-valid']);
25440                     fg.addClass('is-invalid');
25441                 }
25442             });
25443             
25444             return;
25445         }
25446         
25447         if(!this.groupId){
25448             var fg = this.el.findParent('.form-group', false, true);
25449             if (Roo.bootstrap.version == 3) {
25450                 fg.removeClass([_this.invalidClass, _this.validClass]);
25451                 fg.addClass(_this.invalidClass);
25452             } else {
25453                 fg.removeClass(['is-invalid', 'is-valid']);
25454                 fg.addClass('is-invalid');
25455             }
25456             return;
25457         }
25458         
25459         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25460         
25461         if(!group){
25462             return;
25463         }
25464         
25465         for(var i in group){
25466             var fg = group[i].el.findParent('.form-group', false, true);
25467             if (Roo.bootstrap.version == 3) {
25468                 fg.removeClass([_this.invalidClass, _this.validClass]);
25469                 fg.addClass(_this.invalidClass);
25470             } else {
25471                 fg.removeClass(['is-invalid', 'is-valid']);
25472                 fg.addClass('is-invalid');
25473             }
25474         }
25475         
25476     },
25477     
25478     clearInvalid : function()
25479     {
25480         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25481         
25482         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25483         
25484         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25485         
25486         if (label && label.iconEl) {
25487             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25488             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25489         }
25490     },
25491     
25492     disable : function()
25493     {
25494         if(this.inputType != 'radio'){
25495             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25496             return;
25497         }
25498         
25499         var _this = this;
25500         
25501         if(this.rendered){
25502             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25503                 _this.getActionEl().addClass(this.disabledClass);
25504                 e.dom.disabled = true;
25505             });
25506         }
25507         
25508         this.disabled = true;
25509         this.fireEvent("disable", this);
25510         return this;
25511     },
25512
25513     enable : function()
25514     {
25515         if(this.inputType != 'radio'){
25516             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25517             return;
25518         }
25519         
25520         var _this = this;
25521         
25522         if(this.rendered){
25523             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25524                 _this.getActionEl().removeClass(this.disabledClass);
25525                 e.dom.disabled = false;
25526             });
25527         }
25528         
25529         this.disabled = false;
25530         this.fireEvent("enable", this);
25531         return this;
25532     },
25533     
25534     setBoxLabel : function(v)
25535     {
25536         this.boxLabel = v;
25537         
25538         if(this.rendered){
25539             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25540         }
25541     }
25542
25543 });
25544
25545 Roo.apply(Roo.bootstrap.form.CheckBox, {
25546     
25547     groups: {},
25548     
25549      /**
25550     * register a CheckBox Group
25551     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25552     */
25553     register : function(checkbox)
25554     {
25555         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25556             this.groups[checkbox.groupId] = {};
25557         }
25558         
25559         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25560             return;
25561         }
25562         
25563         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25564         
25565     },
25566     /**
25567     * fetch a CheckBox Group based on the group ID
25568     * @param {string} the group ID
25569     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25570     */
25571     get: function(groupId) {
25572         if (typeof(this.groups[groupId]) == 'undefined') {
25573             return false;
25574         }
25575         
25576         return this.groups[groupId] ;
25577     }
25578     
25579     
25580 });
25581 /*
25582  * - LGPL
25583  *
25584  * RadioItem
25585  * 
25586  */
25587
25588 /**
25589  * @class Roo.bootstrap.form.Radio
25590  * @extends Roo.bootstrap.Component
25591  * Bootstrap Radio class
25592  * @cfg {String} boxLabel - the label associated
25593  * @cfg {String} value - the value of radio
25594  * 
25595  * @constructor
25596  * Create a new Radio
25597  * @param {Object} config The config object
25598  */
25599 Roo.bootstrap.form.Radio = function(config){
25600     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25601     
25602 };
25603
25604 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25605     
25606     boxLabel : '',
25607     
25608     value : '',
25609     
25610     getAutoCreate : function()
25611     {
25612         var cfg = {
25613             tag : 'div',
25614             cls : 'form-group radio',
25615             cn : [
25616                 {
25617                     tag : 'label',
25618                     cls : 'box-label',
25619                     html : this.boxLabel
25620                 }
25621             ]
25622         };
25623         
25624         return cfg;
25625     },
25626     
25627     initEvents : function() 
25628     {
25629         this.parent().register(this);
25630         
25631         this.el.on('click', this.onClick, this);
25632         
25633     },
25634     
25635     onClick : function(e)
25636     {
25637         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25638             this.setChecked(true);
25639         }
25640     },
25641     
25642     setChecked : function(state, suppressEvent)
25643     {
25644         this.parent().setValue(this.value, suppressEvent);
25645         
25646     },
25647     
25648     setBoxLabel : function(v)
25649     {
25650         this.boxLabel = v;
25651         
25652         if(this.rendered){
25653             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25654         }
25655     }
25656     
25657 });
25658  
25659
25660  /*
25661  * - LGPL
25662  *
25663  * Input
25664  * 
25665  */
25666
25667 /**
25668  * @class Roo.bootstrap.form.SecurePass
25669  * @extends Roo.bootstrap.form.Input
25670  * Bootstrap SecurePass class
25671  *
25672  * 
25673  * @constructor
25674  * Create a new SecurePass
25675  * @param {Object} config The config object
25676  */
25677  
25678 Roo.bootstrap.form.SecurePass = function (config) {
25679     // these go here, so the translation tool can replace them..
25680     this.errors = {
25681         PwdEmpty: "Please type a password, and then retype it to confirm.",
25682         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25683         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25684         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25685         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25686         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25687         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25688         TooWeak: "Your password is Too Weak."
25689     },
25690     this.meterLabel = "Password strength:";
25691     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25692     this.meterClass = [
25693         "roo-password-meter-tooweak", 
25694         "roo-password-meter-weak", 
25695         "roo-password-meter-medium", 
25696         "roo-password-meter-strong", 
25697         "roo-password-meter-grey"
25698     ];
25699     
25700     this.errors = {};
25701     
25702     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25703 }
25704
25705 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25706     /**
25707      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25708      * {
25709      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25710      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25711      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25712      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25713      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25714      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25715      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25716      * })
25717      */
25718     // private
25719     
25720     meterWidth: 300,
25721     errorMsg :'',    
25722     errors: false,
25723     imageRoot: '/',
25724     /**
25725      * @cfg {String/Object} Label for the strength meter (defaults to
25726      * 'Password strength:')
25727      */
25728     // private
25729     meterLabel: '',
25730     /**
25731      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25732      * ['Weak', 'Medium', 'Strong'])
25733      */
25734     // private    
25735     pwdStrengths: false,    
25736     // private
25737     strength: 0,
25738     // private
25739     _lastPwd: null,
25740     // private
25741     kCapitalLetter: 0,
25742     kSmallLetter: 1,
25743     kDigit: 2,
25744     kPunctuation: 3,
25745     
25746     insecure: false,
25747     // private
25748     initEvents: function ()
25749     {
25750         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25751
25752         if (this.el.is('input[type=password]') && Roo.isSafari) {
25753             this.el.on('keydown', this.SafariOnKeyDown, this);
25754         }
25755
25756         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25757     },
25758     // private
25759     onRender: function (ct, position)
25760     {
25761         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25762         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25763         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25764
25765         this.trigger.createChild({
25766                    cn: [
25767                     {
25768                     //id: 'PwdMeter',
25769                     tag: 'div',
25770                     cls: 'roo-password-meter-grey col-xs-12',
25771                     style: {
25772                         //width: 0,
25773                         //width: this.meterWidth + 'px'                                                
25774                         }
25775                     },
25776                     {                            
25777                          cls: 'roo-password-meter-text'                          
25778                     }
25779                 ]            
25780         });
25781
25782          
25783         if (this.hideTrigger) {
25784             this.trigger.setDisplayed(false);
25785         }
25786         this.setSize(this.width || '', this.height || '');
25787     },
25788     // private
25789     onDestroy: function ()
25790     {
25791         if (this.trigger) {
25792             this.trigger.removeAllListeners();
25793             this.trigger.remove();
25794         }
25795         if (this.wrap) {
25796             this.wrap.remove();
25797         }
25798         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25799     },
25800     // private
25801     checkStrength: function ()
25802     {
25803         var pwd = this.inputEl().getValue();
25804         if (pwd == this._lastPwd) {
25805             return;
25806         }
25807
25808         var strength;
25809         if (this.ClientSideStrongPassword(pwd)) {
25810             strength = 3;
25811         } else if (this.ClientSideMediumPassword(pwd)) {
25812             strength = 2;
25813         } else if (this.ClientSideWeakPassword(pwd)) {
25814             strength = 1;
25815         } else {
25816             strength = 0;
25817         }
25818         
25819         Roo.log('strength1: ' + strength);
25820         
25821         //var pm = this.trigger.child('div/div/div').dom;
25822         var pm = this.trigger.child('div/div');
25823         pm.removeClass(this.meterClass);
25824         pm.addClass(this.meterClass[strength]);
25825                 
25826         
25827         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25828                 
25829         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25830         
25831         this._lastPwd = pwd;
25832     },
25833     reset: function ()
25834     {
25835         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25836         
25837         this._lastPwd = '';
25838         
25839         var pm = this.trigger.child('div/div');
25840         pm.removeClass(this.meterClass);
25841         pm.addClass('roo-password-meter-grey');        
25842         
25843         
25844         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25845         
25846         pt.innerHTML = '';
25847         this.inputEl().dom.type='password';
25848     },
25849     // private
25850     validateValue: function (value)
25851     {
25852         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25853             return false;
25854         }
25855         if (value.length == 0) {
25856             if (this.allowBlank) {
25857                 this.clearInvalid();
25858                 return true;
25859             }
25860
25861             this.markInvalid(this.errors.PwdEmpty);
25862             this.errorMsg = this.errors.PwdEmpty;
25863             return false;
25864         }
25865         
25866         if(this.insecure){
25867             return true;
25868         }
25869         
25870         if (!value.match(/[\x21-\x7e]+/)) {
25871             this.markInvalid(this.errors.PwdBadChar);
25872             this.errorMsg = this.errors.PwdBadChar;
25873             return false;
25874         }
25875         if (value.length < 6) {
25876             this.markInvalid(this.errors.PwdShort);
25877             this.errorMsg = this.errors.PwdShort;
25878             return false;
25879         }
25880         if (value.length > 16) {
25881             this.markInvalid(this.errors.PwdLong);
25882             this.errorMsg = this.errors.PwdLong;
25883             return false;
25884         }
25885         var strength;
25886         if (this.ClientSideStrongPassword(value)) {
25887             strength = 3;
25888         } else if (this.ClientSideMediumPassword(value)) {
25889             strength = 2;
25890         } else if (this.ClientSideWeakPassword(value)) {
25891             strength = 1;
25892         } else {
25893             strength = 0;
25894         }
25895
25896         
25897         if (strength < 2) {
25898             //this.markInvalid(this.errors.TooWeak);
25899             this.errorMsg = this.errors.TooWeak;
25900             //return false;
25901         }
25902         
25903         
25904         console.log('strength2: ' + strength);
25905         
25906         //var pm = this.trigger.child('div/div/div').dom;
25907         
25908         var pm = this.trigger.child('div/div');
25909         pm.removeClass(this.meterClass);
25910         pm.addClass(this.meterClass[strength]);
25911                 
25912         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25913                 
25914         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25915         
25916         this.errorMsg = ''; 
25917         return true;
25918     },
25919     // private
25920     CharacterSetChecks: function (type)
25921     {
25922         this.type = type;
25923         this.fResult = false;
25924     },
25925     // private
25926     isctype: function (character, type)
25927     {
25928         switch (type) {  
25929             case this.kCapitalLetter:
25930                 if (character >= 'A' && character <= 'Z') {
25931                     return true;
25932                 }
25933                 break;
25934             
25935             case this.kSmallLetter:
25936                 if (character >= 'a' && character <= 'z') {
25937                     return true;
25938                 }
25939                 break;
25940             
25941             case this.kDigit:
25942                 if (character >= '0' && character <= '9') {
25943                     return true;
25944                 }
25945                 break;
25946             
25947             case this.kPunctuation:
25948                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25949                     return true;
25950                 }
25951                 break;
25952             
25953             default:
25954                 return false;
25955         }
25956
25957     },
25958     // private
25959     IsLongEnough: function (pwd, size)
25960     {
25961         return !(pwd == null || isNaN(size) || pwd.length < size);
25962     },
25963     // private
25964     SpansEnoughCharacterSets: function (word, nb)
25965     {
25966         if (!this.IsLongEnough(word, nb))
25967         {
25968             return false;
25969         }
25970
25971         var characterSetChecks = new Array(
25972             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25973             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25974         );
25975         
25976         for (var index = 0; index < word.length; ++index) {
25977             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25978                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25979                     characterSetChecks[nCharSet].fResult = true;
25980                     break;
25981                 }
25982             }
25983         }
25984
25985         var nCharSets = 0;
25986         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25987             if (characterSetChecks[nCharSet].fResult) {
25988                 ++nCharSets;
25989             }
25990         }
25991
25992         if (nCharSets < nb) {
25993             return false;
25994         }
25995         return true;
25996     },
25997     // private
25998     ClientSideStrongPassword: function (pwd)
25999     {
26000         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26001     },
26002     // private
26003     ClientSideMediumPassword: function (pwd)
26004     {
26005         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26006     },
26007     // private
26008     ClientSideWeakPassword: function (pwd)
26009     {
26010         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26011     }
26012           
26013 });Roo.rtf = {}; // namespace
26014 Roo.rtf.Hex = function(hex)
26015 {
26016     this.hexstr = hex;
26017 };
26018 Roo.rtf.Paragraph = function(opts)
26019 {
26020     this.content = []; ///??? is that used?
26021 };Roo.rtf.Span = function(opts)
26022 {
26023     this.value = opts.value;
26024 };
26025
26026 Roo.rtf.Group = function(parent)
26027 {
26028     // we dont want to acutally store parent - it will make debug a nightmare..
26029     this.content = [];
26030     this.cn  = [];
26031      
26032        
26033     
26034 };
26035
26036 Roo.rtf.Group.prototype = {
26037     ignorable : false,
26038     content: false,
26039     cn: false,
26040     addContent : function(node) {
26041         // could set styles...
26042         this.content.push(node);
26043     },
26044     addChild : function(cn)
26045     {
26046         this.cn.push(cn);
26047     },
26048     // only for images really...
26049     toDataURL : function()
26050     {
26051         var mimetype = false;
26052         switch(true) {
26053             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26054                 mimetype = "image/png";
26055                 break;
26056              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26057                 mimetype = "image/jpeg";
26058                 break;
26059             default :
26060                 return 'about:blank'; // ?? error?
26061         }
26062         
26063         
26064         var hexstring = this.content[this.content.length-1].value;
26065         
26066         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26067             return String.fromCharCode(parseInt(a, 16));
26068         }).join(""));
26069     }
26070     
26071 };
26072 // this looks like it's normally the {rtf{ .... }}
26073 Roo.rtf.Document = function()
26074 {
26075     // we dont want to acutally store parent - it will make debug a nightmare..
26076     this.rtlch  = [];
26077     this.content = [];
26078     this.cn = [];
26079     
26080 };
26081 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26082     addChild : function(cn)
26083     {
26084         this.cn.push(cn);
26085         switch(cn.type) {
26086             case 'rtlch': // most content seems to be inside this??
26087             case 'listtext':
26088             case 'shpinst':
26089                 this.rtlch.push(cn);
26090                 return;
26091             default:
26092                 this[cn.type] = cn;
26093         }
26094         
26095     },
26096     
26097     getElementsByType : function(type)
26098     {
26099         var ret =  [];
26100         this._getElementsByType(type, ret, this.cn, 'rtf');
26101         return ret;
26102     },
26103     _getElementsByType : function (type, ret, search_array, path)
26104     {
26105         search_array.forEach(function(n,i) {
26106             if (n.type == type) {
26107                 n.path = path + '/' + n.type + ':' + i;
26108                 ret.push(n);
26109             }
26110             if (n.cn.length > 0) {
26111                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26112             }
26113         },this);
26114     }
26115     
26116 });
26117  
26118 Roo.rtf.Ctrl = function(opts)
26119 {
26120     this.value = opts.value;
26121     this.param = opts.param;
26122 };
26123 /**
26124  *
26125  *
26126  * based on this https://github.com/iarna/rtf-parser
26127  * it's really only designed to extract pict from pasted RTF 
26128  *
26129  * usage:
26130  *
26131  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26132  *  
26133  *
26134  */
26135
26136  
26137
26138
26139
26140 Roo.rtf.Parser = function(text) {
26141     //super({objectMode: true})
26142     this.text = '';
26143     this.parserState = this.parseText;
26144     
26145     // these are for interpeter...
26146     this.doc = {};
26147     ///this.parserState = this.parseTop
26148     this.groupStack = [];
26149     this.hexStore = [];
26150     this.doc = false;
26151     
26152     this.groups = []; // where we put the return.
26153     
26154     for (var ii = 0; ii < text.length; ++ii) {
26155         ++this.cpos;
26156         
26157         if (text[ii] === '\n') {
26158             ++this.row;
26159             this.col = 1;
26160         } else {
26161             ++this.col;
26162         }
26163         this.parserState(text[ii]);
26164     }
26165     
26166     
26167     
26168 };
26169 Roo.rtf.Parser.prototype = {
26170     text : '', // string being parsed..
26171     controlWord : '',
26172     controlWordParam :  '',
26173     hexChar : '',
26174     doc : false,
26175     group: false,
26176     groupStack : false,
26177     hexStore : false,
26178     
26179     
26180     cpos : 0, 
26181     row : 1, // reportin?
26182     col : 1, //
26183
26184      
26185     push : function (el)
26186     {
26187         var m = 'cmd'+ el.type;
26188         if (typeof(this[m]) == 'undefined') {
26189             Roo.log('invalid cmd:' + el.type);
26190             return;
26191         }
26192         this[m](el);
26193         //Roo.log(el);
26194     },
26195     flushHexStore : function()
26196     {
26197         if (this.hexStore.length < 1) {
26198             return;
26199         }
26200         var hexstr = this.hexStore.map(
26201             function(cmd) {
26202                 return cmd.value;
26203         }).join('');
26204         
26205         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26206               
26207             
26208         this.hexStore.splice(0)
26209         
26210     },
26211     
26212     cmdgroupstart : function()
26213     {
26214         this.flushHexStore();
26215         if (this.group) {
26216             this.groupStack.push(this.group);
26217         }
26218          // parent..
26219         if (this.doc === false) {
26220             this.group = this.doc = new Roo.rtf.Document();
26221             return;
26222             
26223         }
26224         this.group = new Roo.rtf.Group(this.group);
26225     },
26226     cmdignorable : function()
26227     {
26228         this.flushHexStore();
26229         this.group.ignorable = true;
26230     },
26231     cmdendparagraph : function()
26232     {
26233         this.flushHexStore();
26234         this.group.addContent(new Roo.rtf.Paragraph());
26235     },
26236     cmdgroupend : function ()
26237     {
26238         this.flushHexStore();
26239         var endingGroup = this.group;
26240         
26241         
26242         this.group = this.groupStack.pop();
26243         if (this.group) {
26244             this.group.addChild(endingGroup);
26245         }
26246         
26247         
26248         
26249         var doc = this.group || this.doc;
26250         //if (endingGroup instanceof FontTable) {
26251         //  doc.fonts = endingGroup.table
26252         //} else if (endingGroup instanceof ColorTable) {
26253         //  doc.colors = endingGroup.table
26254         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26255         if (endingGroup.ignorable === false) {
26256             //code
26257             this.groups.push(endingGroup);
26258            // Roo.log( endingGroup );
26259         }
26260             //Roo.each(endingGroup.content, function(item)) {
26261             //    doc.addContent(item);
26262             //}
26263             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26264         //}
26265     },
26266     cmdtext : function (cmd)
26267     {
26268         this.flushHexStore();
26269         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26270             //this.group = this.doc
26271             return;  // we really don't care about stray text...
26272         }
26273         this.group.addContent(new Roo.rtf.Span(cmd));
26274     },
26275     cmdcontrolword : function (cmd)
26276     {
26277         this.flushHexStore();
26278         if (!this.group.type) {
26279             this.group.type = cmd.value;
26280             return;
26281         }
26282         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26283         // we actually don't care about ctrl words...
26284         return ;
26285         /*
26286         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26287         if (this[method]) {
26288             this[method](cmd.param)
26289         } else {
26290             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26291         }
26292         */
26293     },
26294     cmdhexchar : function(cmd) {
26295         this.hexStore.push(cmd);
26296     },
26297     cmderror : function(cmd) {
26298         throw cmd.value;
26299     },
26300     
26301     /*
26302       _flush (done) {
26303         if (this.text !== '\u0000') this.emitText()
26304         done()
26305       }
26306       */
26307       
26308       
26309     parseText : function(c)
26310     {
26311         if (c === '\\') {
26312             this.parserState = this.parseEscapes;
26313         } else if (c === '{') {
26314             this.emitStartGroup();
26315         } else if (c === '}') {
26316             this.emitEndGroup();
26317         } else if (c === '\x0A' || c === '\x0D') {
26318             // cr/lf are noise chars
26319         } else {
26320             this.text += c;
26321         }
26322     },
26323     
26324     parseEscapes: function (c)
26325     {
26326         if (c === '\\' || c === '{' || c === '}') {
26327             this.text += c;
26328             this.parserState = this.parseText;
26329         } else {
26330             this.parserState = this.parseControlSymbol;
26331             this.parseControlSymbol(c);
26332         }
26333     },
26334     parseControlSymbol: function(c)
26335     {
26336         if (c === '~') {
26337             this.text += '\u00a0'; // nbsp
26338             this.parserState = this.parseText
26339         } else if (c === '-') {
26340              this.text += '\u00ad'; // soft hyphen
26341         } else if (c === '_') {
26342             this.text += '\u2011'; // non-breaking hyphen
26343         } else if (c === '*') {
26344             this.emitIgnorable();
26345             this.parserState = this.parseText;
26346         } else if (c === "'") {
26347             this.parserState = this.parseHexChar;
26348         } else if (c === '|') { // formula cacter
26349             this.emitFormula();
26350             this.parserState = this.parseText;
26351         } else if (c === ':') { // subentry in an index entry
26352             this.emitIndexSubEntry();
26353             this.parserState = this.parseText;
26354         } else if (c === '\x0a') {
26355             this.emitEndParagraph();
26356             this.parserState = this.parseText;
26357         } else if (c === '\x0d') {
26358             this.emitEndParagraph();
26359             this.parserState = this.parseText;
26360         } else {
26361             this.parserState = this.parseControlWord;
26362             this.parseControlWord(c);
26363         }
26364     },
26365     parseHexChar: function (c)
26366     {
26367         if (/^[A-Fa-f0-9]$/.test(c)) {
26368             this.hexChar += c;
26369             if (this.hexChar.length >= 2) {
26370               this.emitHexChar();
26371               this.parserState = this.parseText;
26372             }
26373             return;
26374         }
26375         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26376         this.parserState = this.parseText;
26377         
26378     },
26379     parseControlWord : function(c)
26380     {
26381         if (c === ' ') {
26382             this.emitControlWord();
26383             this.parserState = this.parseText;
26384         } else if (/^[-\d]$/.test(c)) {
26385             this.parserState = this.parseControlWordParam;
26386             this.controlWordParam += c;
26387         } else if (/^[A-Za-z]$/.test(c)) {
26388           this.controlWord += c;
26389         } else {
26390           this.emitControlWord();
26391           this.parserState = this.parseText;
26392           this.parseText(c);
26393         }
26394     },
26395     parseControlWordParam : function (c) {
26396         if (/^\d$/.test(c)) {
26397           this.controlWordParam += c;
26398         } else if (c === ' ') {
26399           this.emitControlWord();
26400           this.parserState = this.parseText;
26401         } else {
26402           this.emitControlWord();
26403           this.parserState = this.parseText;
26404           this.parseText(c);
26405         }
26406     },
26407     
26408     
26409     
26410     
26411     emitText : function () {
26412         if (this.text === '') {
26413             return;
26414         }
26415         this.push({
26416             type: 'text',
26417             value: this.text,
26418             pos: this.cpos,
26419             row: this.row,
26420             col: this.col
26421         });
26422         this.text = ''
26423     },
26424     emitControlWord : function ()
26425     {
26426         this.emitText();
26427         if (this.controlWord === '') {
26428             // do we want to track this - it seems just to cause problems.
26429             //this.emitError('empty control word');
26430         } else {
26431             this.push({
26432                   type: 'controlword',
26433                   value: this.controlWord,
26434                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26435                   pos: this.cpos,
26436                   row: this.row,
26437                   col: this.col
26438             });
26439         }
26440         this.controlWord = '';
26441         this.controlWordParam = '';
26442     },
26443     emitStartGroup : function ()
26444     {
26445         this.emitText();
26446         this.push({
26447             type: 'groupstart',
26448             pos: this.cpos,
26449             row: this.row,
26450             col: this.col
26451         });
26452     },
26453     emitEndGroup : function ()
26454     {
26455         this.emitText();
26456         this.push({
26457             type: 'groupend',
26458             pos: this.cpos,
26459             row: this.row,
26460             col: this.col
26461         });
26462     },
26463     emitIgnorable : function ()
26464     {
26465         this.emitText();
26466         this.push({
26467             type: 'ignorable',
26468             pos: this.cpos,
26469             row: this.row,
26470             col: this.col
26471         });
26472     },
26473     emitHexChar : function ()
26474     {
26475         this.emitText();
26476         this.push({
26477             type: 'hexchar',
26478             value: this.hexChar,
26479             pos: this.cpos,
26480             row: this.row,
26481             col: this.col
26482         });
26483         this.hexChar = ''
26484     },
26485     emitError : function (message)
26486     {
26487       this.emitText();
26488       this.push({
26489             type: 'error',
26490             value: message,
26491             row: this.row,
26492             col: this.col,
26493             char: this.cpos //,
26494             //stack: new Error().stack
26495         });
26496     },
26497     emitEndParagraph : function () {
26498         this.emitText();
26499         this.push({
26500             type: 'endparagraph',
26501             pos: this.cpos,
26502             row: this.row,
26503             col: this.col
26504         });
26505     }
26506      
26507 } ; 
26508 /**
26509  * @class Roo.htmleditor.Filter
26510  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26511  * @cfg {DomElement} node The node to iterate and filter
26512  * @cfg {boolean|String|Array} tag Tags to replace 
26513  * @constructor
26514  * Create a new Filter.
26515  * @param {Object} config Configuration options
26516  */
26517
26518
26519
26520 Roo.htmleditor.Filter = function(cfg) {
26521     Roo.apply(this.cfg);
26522     // this does not actually call walk as it's really just a abstract class
26523 }
26524
26525
26526 Roo.htmleditor.Filter.prototype = {
26527     
26528     node: false,
26529     
26530     tag: false,
26531
26532     // overrride to do replace comments.
26533     replaceComment : false,
26534     
26535     // overrride to do replace or do stuff with tags..
26536     replaceTag : false,
26537     
26538     walk : function(dom)
26539     {
26540         Roo.each( Array.from(dom.childNodes), function( e ) {
26541             switch(true) {
26542                 
26543                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26544                     this.replaceComment(e);
26545                     return;
26546                 
26547                 case e.nodeType != 1: //not a node.
26548                     return;
26549                 
26550                 case this.tag === true: // everything
26551                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26552                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26553                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26554                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26555                     if (this.replaceTag && false === this.replaceTag(e)) {
26556                         return;
26557                     }
26558                     if (e.hasChildNodes()) {
26559                         this.walk(e);
26560                     }
26561                     return;
26562                 
26563                 default:    // tags .. that do not match.
26564                     if (e.hasChildNodes()) {
26565                         this.walk(e);
26566                     }
26567             }
26568             
26569         }, this);
26570         
26571     },
26572     
26573     
26574     removeNodeKeepChildren : function( node)
26575     {
26576     
26577         ar = Array.from(node.childNodes);
26578         for (var i = 0; i < ar.length; i++) {
26579          
26580             node.removeChild(ar[i]);
26581             // what if we need to walk these???
26582             node.parentNode.insertBefore(ar[i], node);
26583            
26584         }
26585         node.parentNode.removeChild(node);
26586     }
26587 }; 
26588
26589 /**
26590  * @class Roo.htmleditor.FilterAttributes
26591  * clean attributes and  styles including http:// etc.. in attribute
26592  * @constructor
26593 * Run a new Attribute Filter
26594 * @param {Object} config Configuration options
26595  */
26596 Roo.htmleditor.FilterAttributes = function(cfg)
26597 {
26598     Roo.apply(this, cfg);
26599     this.attrib_black = this.attrib_black || [];
26600     this.attrib_white = this.attrib_white || [];
26601
26602     this.attrib_clean = this.attrib_clean || [];
26603     this.style_white = this.style_white || [];
26604     this.style_black = this.style_black || [];
26605     this.walk(cfg.node);
26606 }
26607
26608 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26609 {
26610     tag: true, // all tags
26611     
26612     attrib_black : false, // array
26613     attrib_clean : false,
26614     attrib_white : false,
26615
26616     style_white : false,
26617     style_black : false,
26618      
26619      
26620     replaceTag : function(node)
26621     {
26622         if (!node.attributes || !node.attributes.length) {
26623             return true;
26624         }
26625         
26626         for (var i = node.attributes.length-1; i > -1 ; i--) {
26627             var a = node.attributes[i];
26628             //console.log(a);
26629             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26630                 node.removeAttribute(a.name);
26631                 continue;
26632             }
26633             
26634             
26635             
26636             if (a.name.toLowerCase().substr(0,2)=='on')  {
26637                 node.removeAttribute(a.name);
26638                 continue;
26639             }
26640             
26641             
26642             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26643                 node.removeAttribute(a.name);
26644                 continue;
26645             }
26646             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26647                 this.cleanAttr(node,a.name,a.value); // fixme..
26648                 continue;
26649             }
26650             if (a.name == 'style') {
26651                 this.cleanStyle(node,a.name,a.value);
26652                 continue;
26653             }
26654             /// clean up MS crap..
26655             // tecnically this should be a list of valid class'es..
26656             
26657             
26658             if (a.name == 'class') {
26659                 if (a.value.match(/^Mso/)) {
26660                     node.removeAttribute('class');
26661                 }
26662                 
26663                 if (a.value.match(/^body$/)) {
26664                     node.removeAttribute('class');
26665                 }
26666                 continue;
26667             }
26668             
26669             
26670             // style cleanup!?
26671             // class cleanup?
26672             
26673         }
26674         return true; // clean children
26675     },
26676         
26677     cleanAttr: function(node, n,v)
26678     {
26679         
26680         if (v.match(/^\./) || v.match(/^\//)) {
26681             return;
26682         }
26683         if (v.match(/^(http|https):\/\//)
26684             || v.match(/^mailto:/) 
26685             || v.match(/^ftp:/)
26686             || v.match(/^data:/)
26687             ) {
26688             return;
26689         }
26690         if (v.match(/^#/)) {
26691             return;
26692         }
26693         if (v.match(/^\{/)) { // allow template editing.
26694             return;
26695         }
26696 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26697         node.removeAttribute(n);
26698         
26699     },
26700     cleanStyle : function(node,  n,v)
26701     {
26702         if (v.match(/expression/)) { //XSS?? should we even bother..
26703             node.removeAttribute(n);
26704             return;
26705         }
26706         
26707         var parts = v.split(/;/);
26708         var clean = [];
26709         
26710         Roo.each(parts, function(p) {
26711             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26712             if (!p.length) {
26713                 return true;
26714             }
26715             var l = p.split(':').shift().replace(/\s+/g,'');
26716             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26717             
26718             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26719                 return true;
26720             }
26721             //Roo.log()
26722             // only allow 'c whitelisted system attributes'
26723             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26724                 return true;
26725             }
26726             
26727             
26728             clean.push(p);
26729             return true;
26730         },this);
26731         if (clean.length) { 
26732             node.setAttribute(n, clean.join(';'));
26733         } else {
26734             node.removeAttribute(n);
26735         }
26736         
26737     }
26738         
26739         
26740         
26741     
26742 });/**
26743  * @class Roo.htmleditor.FilterBlack
26744  * remove blacklisted elements.
26745  * @constructor
26746  * Run a new Blacklisted Filter
26747  * @param {Object} config Configuration options
26748  */
26749
26750 Roo.htmleditor.FilterBlack = function(cfg)
26751 {
26752     Roo.apply(this, cfg);
26753     this.walk(cfg.node);
26754 }
26755
26756 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26757 {
26758     tag : true, // all elements.
26759    
26760     replaceTag : function(n)
26761     {
26762         n.parentNode.removeChild(n);
26763     }
26764 });
26765 /**
26766  * @class Roo.htmleditor.FilterComment
26767  * remove comments.
26768  * @constructor
26769 * Run a new Comments Filter
26770 * @param {Object} config Configuration options
26771  */
26772 Roo.htmleditor.FilterComment = function(cfg)
26773 {
26774     this.walk(cfg.node);
26775 }
26776
26777 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26778 {
26779   
26780     replaceComment : function(n)
26781     {
26782         n.parentNode.removeChild(n);
26783     }
26784 });/**
26785  * @class Roo.htmleditor.FilterKeepChildren
26786  * remove tags but keep children
26787  * @constructor
26788  * Run a new Keep Children Filter
26789  * @param {Object} config Configuration options
26790  */
26791
26792 Roo.htmleditor.FilterKeepChildren = function(cfg)
26793 {
26794     Roo.apply(this, cfg);
26795     if (this.tag === false) {
26796         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26797     }
26798     // hacky?
26799     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26800         this.cleanNamespace = true;
26801     }
26802         
26803     this.walk(cfg.node);
26804 }
26805
26806 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26807 {
26808     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26809   
26810     replaceTag : function(node)
26811     {
26812         // walk children...
26813         //Roo.log(node.tagName);
26814         var ar = Array.from(node.childNodes);
26815         //remove first..
26816         
26817         for (var i = 0; i < ar.length; i++) {
26818             var e = ar[i];
26819             if (e.nodeType == 1) {
26820                 if (
26821                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26822                     || // array and it matches
26823                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26824                     ||
26825                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26826                     ||
26827                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26828                 ) {
26829                     this.replaceTag(ar[i]); // child is blacklisted as well...
26830                     continue;
26831                 }
26832             }
26833         }  
26834         ar = Array.from(node.childNodes);
26835         for (var i = 0; i < ar.length; i++) {
26836          
26837             node.removeChild(ar[i]);
26838             // what if we need to walk these???
26839             node.parentNode.insertBefore(ar[i], node);
26840             if (this.tag !== false) {
26841                 this.walk(ar[i]);
26842                 
26843             }
26844         }
26845         //Roo.log("REMOVE:" + node.tagName);
26846         node.parentNode.removeChild(node);
26847         return false; // don't walk children
26848         
26849         
26850     }
26851 });/**
26852  * @class Roo.htmleditor.FilterParagraph
26853  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26854  * like on 'push' to remove the <p> tags and replace them with line breaks.
26855  * @constructor
26856  * Run a new Paragraph Filter
26857  * @param {Object} config Configuration options
26858  */
26859
26860 Roo.htmleditor.FilterParagraph = function(cfg)
26861 {
26862     // no need to apply config.
26863     this.walk(cfg.node);
26864 }
26865
26866 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26867 {
26868     
26869      
26870     tag : 'P',
26871     
26872      
26873     replaceTag : function(node)
26874     {
26875         
26876         if (node.childNodes.length == 1 &&
26877             node.childNodes[0].nodeType == 3 &&
26878             node.childNodes[0].textContent.trim().length < 1
26879             ) {
26880             // remove and replace with '<BR>';
26881             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26882             return false; // no need to walk..
26883         }
26884         var ar = Array.from(node.childNodes);
26885         for (var i = 0; i < ar.length; i++) {
26886             node.removeChild(ar[i]);
26887             // what if we need to walk these???
26888             node.parentNode.insertBefore(ar[i], node);
26889         }
26890         // now what about this?
26891         // <p> &nbsp; </p>
26892         
26893         // double BR.
26894         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26895         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26896         node.parentNode.removeChild(node);
26897         
26898         return false;
26899
26900     }
26901     
26902 });/**
26903  * @class Roo.htmleditor.FilterSpan
26904  * filter span's with no attributes out..
26905  * @constructor
26906  * Run a new Span Filter
26907  * @param {Object} config Configuration options
26908  */
26909
26910 Roo.htmleditor.FilterSpan = function(cfg)
26911 {
26912     // no need to apply config.
26913     this.walk(cfg.node);
26914 }
26915
26916 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26917 {
26918      
26919     tag : 'SPAN',
26920      
26921  
26922     replaceTag : function(node)
26923     {
26924         if (node.attributes && node.attributes.length > 0) {
26925             return true; // walk if there are any.
26926         }
26927         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26928         return false;
26929      
26930     }
26931     
26932 });/**
26933  * @class Roo.htmleditor.FilterTableWidth
26934   try and remove table width data - as that frequently messes up other stuff.
26935  * 
26936  *      was cleanTableWidths.
26937  *
26938  * Quite often pasting from word etc.. results in tables with column and widths.
26939  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26940  *
26941  * @constructor
26942  * Run a new Table Filter
26943  * @param {Object} config Configuration options
26944  */
26945
26946 Roo.htmleditor.FilterTableWidth = function(cfg)
26947 {
26948     // no need to apply config.
26949     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26950     this.walk(cfg.node);
26951 }
26952
26953 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26954 {
26955      
26956      
26957     
26958     replaceTag: function(node) {
26959         
26960         
26961       
26962         if (node.hasAttribute('width')) {
26963             node.removeAttribute('width');
26964         }
26965         
26966          
26967         if (node.hasAttribute("style")) {
26968             // pretty basic...
26969             
26970             var styles = node.getAttribute("style").split(";");
26971             var nstyle = [];
26972             Roo.each(styles, function(s) {
26973                 if (!s.match(/:/)) {
26974                     return;
26975                 }
26976                 var kv = s.split(":");
26977                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26978                     return;
26979                 }
26980                 // what ever is left... we allow.
26981                 nstyle.push(s);
26982             });
26983             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26984             if (!nstyle.length) {
26985                 node.removeAttribute('style');
26986             }
26987         }
26988         
26989         return true; // continue doing children..
26990     }
26991 });/**
26992  * @class Roo.htmleditor.FilterWord
26993  * try and clean up all the mess that Word generates.
26994  * 
26995  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26996  
26997  * @constructor
26998  * Run a new Span Filter
26999  * @param {Object} config Configuration options
27000  */
27001
27002 Roo.htmleditor.FilterWord = function(cfg)
27003 {
27004     // no need to apply config.
27005     this.replaceDocBullets(cfg.node);
27006     
27007     this.replaceAname(cfg.node);
27008     // this is disabled as the removal is done by other filters;
27009    // this.walk(cfg.node);
27010     this.replaceImageTable(cfg.node);
27011     
27012 }
27013
27014 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27015 {
27016     tag: true,
27017      
27018     
27019     /**
27020      * Clean up MS wordisms...
27021      */
27022     replaceTag : function(node)
27023     {
27024          
27025         // no idea what this does - span with text, replaceds with just text.
27026         if(
27027                 node.nodeName == 'SPAN' &&
27028                 !node.hasAttributes() &&
27029                 node.childNodes.length == 1 &&
27030                 node.firstChild.nodeName == "#text"  
27031         ) {
27032             var textNode = node.firstChild;
27033             node.removeChild(textNode);
27034             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27035                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27036             }
27037             node.parentNode.insertBefore(textNode, node);
27038             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27039                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27040             }
27041             
27042             node.parentNode.removeChild(node);
27043             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27044         }
27045         
27046    
27047         
27048         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27049             node.parentNode.removeChild(node);
27050             return false; // dont do chidlren
27051         }
27052         //Roo.log(node.tagName);
27053         // remove - but keep children..
27054         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27055             //Roo.log('-- removed');
27056             while (node.childNodes.length) {
27057                 var cn = node.childNodes[0];
27058                 node.removeChild(cn);
27059                 node.parentNode.insertBefore(cn, node);
27060                 // move node to parent - and clean it..
27061                 if (cn.nodeType == 1) {
27062                     this.replaceTag(cn);
27063                 }
27064                 
27065             }
27066             node.parentNode.removeChild(node);
27067             /// no need to iterate chidlren = it's got none..
27068             //this.iterateChildren(node, this.cleanWord);
27069             return false; // no need to iterate children.
27070         }
27071         // clean styles
27072         if (node.className.length) {
27073             
27074             var cn = node.className.split(/\W+/);
27075             var cna = [];
27076             Roo.each(cn, function(cls) {
27077                 if (cls.match(/Mso[a-zA-Z]+/)) {
27078                     return;
27079                 }
27080                 cna.push(cls);
27081             });
27082             node.className = cna.length ? cna.join(' ') : '';
27083             if (!cna.length) {
27084                 node.removeAttribute("class");
27085             }
27086         }
27087         
27088         if (node.hasAttribute("lang")) {
27089             node.removeAttribute("lang");
27090         }
27091         
27092         if (node.hasAttribute("style")) {
27093             
27094             var styles = node.getAttribute("style").split(";");
27095             var nstyle = [];
27096             Roo.each(styles, function(s) {
27097                 if (!s.match(/:/)) {
27098                     return;
27099                 }
27100                 var kv = s.split(":");
27101                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27102                     return;
27103                 }
27104                 // what ever is left... we allow.
27105                 nstyle.push(s);
27106             });
27107             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27108             if (!nstyle.length) {
27109                 node.removeAttribute('style');
27110             }
27111         }
27112         return true; // do children
27113         
27114         
27115         
27116     },
27117     
27118     styleToObject: function(node)
27119     {
27120         var styles = (node.getAttribute("style") || '').split(";");
27121         var ret = {};
27122         Roo.each(styles, function(s) {
27123             if (!s.match(/:/)) {
27124                 return;
27125             }
27126             var kv = s.split(":");
27127              
27128             // what ever is left... we allow.
27129             ret[kv[0].trim()] = kv[1];
27130         });
27131         return ret;
27132     },
27133     
27134     
27135     replaceAname : function (doc)
27136     {
27137         // replace all the a/name without..
27138         var aa = Array.from(doc.getElementsByTagName('a'));
27139         for (var i = 0; i  < aa.length; i++) {
27140             var a = aa[i];
27141             if (a.hasAttribute("name")) {
27142                 a.removeAttribute("name");
27143             }
27144             if (a.hasAttribute("href")) {
27145                 continue;
27146             }
27147             // reparent children.
27148             this.removeNodeKeepChildren(a);
27149             
27150         }
27151         
27152         
27153         
27154     },
27155
27156     
27157     
27158     replaceDocBullets : function(doc)
27159     {
27160         // this is a bit odd - but it appears some indents use ql-indent-1
27161          //Roo.log(doc.innerHTML);
27162         
27163         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27164         for( var i = 0; i < listpara.length; i ++) {
27165             listpara[i].className = "MsoListParagraph";
27166         }
27167         
27168         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27169         for( var i = 0; i < listpara.length; i ++) {
27170             listpara[i].className = "MsoListParagraph";
27171         }
27172         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27173         for( var i = 0; i < listpara.length; i ++) {
27174             listpara[i].className = "MsoListParagraph";
27175         }
27176         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27177         for( var i = 0; i < listpara.length; i ++) {
27178             listpara[i].className = "MsoListParagraph";
27179         }
27180         
27181         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27182         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27183         for( var i = 0; i < htwo.length; i ++) {
27184             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27185                 htwo[i].className = "MsoListParagraph";
27186             }
27187         }
27188         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27189         for( var i = 0; i < listpara.length; i ++) {
27190             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27191                 listpara[i].className = "MsoListParagraph";
27192             } else {
27193                 listpara[i].className = "MsoNormalx";
27194             }
27195         }
27196        
27197         listpara = doc.getElementsByClassName('MsoListParagraph');
27198         // Roo.log(doc.innerHTML);
27199         
27200         
27201         
27202         while(listpara.length) {
27203             
27204             this.replaceDocBullet(listpara.item(0));
27205         }
27206       
27207     },
27208     
27209      
27210     
27211     replaceDocBullet : function(p)
27212     {
27213         // gather all the siblings.
27214         var ns = p,
27215             parent = p.parentNode,
27216             doc = parent.ownerDocument,
27217             items = [];
27218          
27219         //Roo.log("Parsing: " + p.innerText)    ;
27220         var listtype = 'ul';   
27221         while (ns) {
27222             if (ns.nodeType != 1) {
27223                 ns = ns.nextSibling;
27224                 continue;
27225             }
27226             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27227                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27228                 break;
27229             }
27230             var spans = ns.getElementsByTagName('span');
27231             
27232             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27233                 items.push(ns);
27234                 ns = ns.nextSibling;
27235                 has_list = true;
27236                 if (!spans.length) {
27237                     continue;
27238                 }
27239                 var ff = '';
27240                 var se = spans[0];
27241                 for (var i = 0; i < spans.length;i++) {
27242                     se = spans[i];
27243                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27244                         ff = se.style.fontFamily;
27245                         break;
27246                     }
27247                 }
27248                  
27249                     
27250                 //Roo.log("got font family: " + ff);
27251                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27252                     listtype = 'ol';
27253                 }
27254                 
27255                 continue;
27256             }
27257             //Roo.log("no mso-list?");
27258             
27259             var spans = ns.getElementsByTagName('span');
27260             if (!spans.length) {
27261                 break;
27262             }
27263             var has_list  = false;
27264             for(var i = 0; i < spans.length; i++) {
27265                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27266                     has_list = true;
27267                     break;
27268                 }
27269             }
27270             if (!has_list) {
27271                 break;
27272             }
27273             items.push(ns);
27274             ns = ns.nextSibling;
27275             
27276             
27277         }
27278         if (!items.length) {
27279             ns.className = "";
27280             return;
27281         }
27282         
27283         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27284         parent.insertBefore(ul, p);
27285         var lvl = 0;
27286         var stack = [ ul ];
27287         var last_li = false;
27288         
27289         var margin_to_depth = {};
27290         max_margins = -1;
27291         
27292         items.forEach(function(n, ipos) {
27293             //Roo.log("got innertHMLT=" + n.innerHTML);
27294             
27295             var spans = n.getElementsByTagName('span');
27296             if (!spans.length) {
27297                 //Roo.log("No spans found");
27298                  
27299                 parent.removeChild(n);
27300                 
27301                 
27302                 return; // skip it...
27303             }
27304            
27305                 
27306             var num = 1;
27307             var style = {};
27308             for(var i = 0; i < spans.length; i++) {
27309             
27310                 style = this.styleToObject(spans[i]);
27311                 if (typeof(style['mso-list']) == 'undefined') {
27312                     continue;
27313                 }
27314                 if (listtype == 'ol') {
27315                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27316                 }
27317                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27318                 break;
27319             }
27320             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27321             style = this.styleToObject(n); // mo-list is from the parent node.
27322             if (typeof(style['mso-list']) == 'undefined') {
27323                 //Roo.log("parent is missing level");
27324                   
27325                 parent.removeChild(n);
27326                  
27327                 return;
27328             }
27329             
27330             var margin = style['margin-left'];
27331             if (typeof(margin_to_depth[margin]) == 'undefined') {
27332                 max_margins++;
27333                 margin_to_depth[margin] = max_margins;
27334             }
27335             nlvl = margin_to_depth[margin] ;
27336              
27337             if (nlvl > lvl) {
27338                 //new indent
27339                 var nul = doc.createElement(listtype); // what about number lists...
27340                 if (!last_li) {
27341                     last_li = doc.createElement('li');
27342                     stack[lvl].appendChild(last_li);
27343                 }
27344                 last_li.appendChild(nul);
27345                 stack[nlvl] = nul;
27346                 
27347             }
27348             lvl = nlvl;
27349             
27350             // not starting at 1..
27351             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27352                 stack[nlvl].setAttribute("start", num);
27353             }
27354             
27355             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27356             last_li = nli;
27357             nli.innerHTML = n.innerHTML;
27358             //Roo.log("innerHTML = " + n.innerHTML);
27359             parent.removeChild(n);
27360             
27361              
27362              
27363             
27364         },this);
27365         
27366         
27367         
27368         
27369     },
27370     
27371     replaceImageTable : function(doc)
27372     {
27373          /*
27374           <table cellpadding=0 cellspacing=0 align=left>
27375   <tr>
27376    <td width=423 height=0></td>
27377   </tr>
27378   <tr>
27379    <td></td>
27380    <td><img width=601 height=401
27381    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27382    v:shapes="Picture_x0020_2"></td>
27383   </tr>
27384  </table>
27385  */
27386         var imgs = Array.from(doc.getElementsByTagName('img'));
27387         Roo.each(imgs, function(img) {
27388             var td = img.parentNode;
27389             if (td.nodeName !=  'TD') {
27390                 return;
27391             }
27392             var tr = td.parentNode;
27393             if (tr.nodeName !=  'TR') {
27394                 return;
27395             }
27396             var tbody = tr.parentNode;
27397             if (tbody.nodeName !=  'TBODY') {
27398                 return;
27399             }
27400             var table = tbody.parentNode;
27401             if (table.nodeName !=  'TABLE') {
27402                 return;
27403             }
27404             // first row..
27405             
27406             if (table.getElementsByTagName('tr').length != 2) {
27407                 return;
27408             }
27409             if (table.getElementsByTagName('td').length != 3) {
27410                 return;
27411             }
27412             if (table.innerText.trim() != '') {
27413                 return;
27414             }
27415             var p = table.parentNode;
27416             img.parentNode.removeChild(img);
27417             p.insertBefore(img, table);
27418             p.removeChild(table);
27419             
27420             
27421             
27422         });
27423         
27424       
27425     }
27426     
27427 });
27428 /**
27429  * @class Roo.htmleditor.FilterStyleToTag
27430  * part of the word stuff... - certain 'styles' should be converted to tags.
27431  * eg.
27432  *   font-weight: bold -> bold
27433  *   ?? super / subscrit etc..
27434  * 
27435  * @constructor
27436 * Run a new style to tag filter.
27437 * @param {Object} config Configuration options
27438  */
27439 Roo.htmleditor.FilterStyleToTag = function(cfg)
27440 {
27441     
27442     this.tags = {
27443         B  : [ 'fontWeight' , 'bold'],
27444         I :  [ 'fontStyle' , 'italic'],
27445         //pre :  [ 'font-style' , 'italic'],
27446         // h1.. h6 ?? font-size?
27447         SUP : [ 'verticalAlign' , 'super' ],
27448         SUB : [ 'verticalAlign' , 'sub' ]
27449         
27450         
27451     };
27452     
27453     Roo.apply(this, cfg);
27454      
27455     
27456     this.walk(cfg.node);
27457     
27458     
27459     
27460 }
27461
27462
27463 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27464 {
27465     tag: true, // all tags
27466     
27467     tags : false,
27468     
27469     
27470     replaceTag : function(node)
27471     {
27472         
27473         
27474         if (node.getAttribute("style") === null) {
27475             return true;
27476         }
27477         var inject = [];
27478         for (var k in this.tags) {
27479             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27480                 inject.push(k);
27481                 node.style.removeProperty(this.tags[k][0]);
27482             }
27483         }
27484         if (!inject.length) {
27485             return true; 
27486         }
27487         var cn = Array.from(node.childNodes);
27488         var nn = node;
27489         Roo.each(inject, function(t) {
27490             var nc = node.ownerDocument.createElement(t);
27491             nn.appendChild(nc);
27492             nn = nc;
27493         });
27494         for(var i = 0;i < cn.length;cn++) {
27495             node.removeChild(cn[i]);
27496             nn.appendChild(cn[i]);
27497         }
27498         return true /// iterate thru
27499     }
27500     
27501 })/**
27502  * @class Roo.htmleditor.FilterLongBr
27503  * BR/BR/BR - keep a maximum of 2...
27504  * @constructor
27505  * Run a new Long BR Filter
27506  * @param {Object} config Configuration options
27507  */
27508
27509 Roo.htmleditor.FilterLongBr = function(cfg)
27510 {
27511     // no need to apply config.
27512     this.walk(cfg.node);
27513 }
27514
27515 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27516 {
27517     
27518      
27519     tag : 'BR',
27520     
27521      
27522     replaceTag : function(node)
27523     {
27524         
27525         var ps = node.nextSibling;
27526         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27527             ps = ps.nextSibling;
27528         }
27529         
27530         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27531             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27532             return false;
27533         }
27534         
27535         if (!ps || ps.nodeType != 1) {
27536             return false;
27537         }
27538         
27539         if (!ps || ps.tagName != 'BR') {
27540            
27541             return false;
27542         }
27543         
27544         
27545         
27546         
27547         
27548         if (!node.previousSibling) {
27549             return false;
27550         }
27551         var ps = node.previousSibling;
27552         
27553         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27554             ps = ps.previousSibling;
27555         }
27556         if (!ps || ps.nodeType != 1) {
27557             return false;
27558         }
27559         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27560         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27561             return false;
27562         }
27563         
27564         node.parentNode.removeChild(node); // remove me...
27565         
27566         return false; // no need to do children
27567
27568     }
27569     
27570 }); 
27571
27572 /**
27573  * @class Roo.htmleditor.FilterBlock
27574  * removes id / data-block and contenteditable that are associated with blocks
27575  * usage should be done on a cloned copy of the dom
27576  * @constructor
27577 * Run a new Attribute Filter { node : xxxx }}
27578 * @param {Object} config Configuration options
27579  */
27580 Roo.htmleditor.FilterBlock = function(cfg)
27581 {
27582     Roo.apply(this, cfg);
27583     var qa = cfg.node.querySelectorAll;
27584     this.removeAttributes('data-block');
27585     this.removeAttributes('contenteditable');
27586     this.removeAttributes('id');
27587     
27588 }
27589
27590 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27591 {
27592     node: true, // all tags
27593      
27594      
27595     removeAttributes : function(attr)
27596     {
27597         var ar = this.node.querySelectorAll('*[' + attr + ']');
27598         for (var i =0;i<ar.length;i++) {
27599             ar[i].removeAttribute(attr);
27600         }
27601     }
27602         
27603         
27604         
27605     
27606 });
27607 /***
27608  * This is based loosely on tinymce 
27609  * @class Roo.htmleditor.TidySerializer
27610  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27611  * @constructor
27612  * @method Serializer
27613  * @param {Object} settings Name/value settings object.
27614  */
27615
27616
27617 Roo.htmleditor.TidySerializer = function(settings)
27618 {
27619     Roo.apply(this, settings);
27620     
27621     this.writer = new Roo.htmleditor.TidyWriter(settings);
27622     
27623     
27624
27625 };
27626 Roo.htmleditor.TidySerializer.prototype = {
27627     
27628     /**
27629      * @param {boolean} inner do the inner of the node.
27630      */
27631     inner : false,
27632     
27633     writer : false,
27634     
27635     /**
27636     * Serializes the specified node into a string.
27637     *
27638     * @example
27639     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27640     * @method serialize
27641     * @param {DomElement} node Node instance to serialize.
27642     * @return {String} String with HTML based on DOM tree.
27643     */
27644     serialize : function(node) {
27645         
27646         // = settings.validate;
27647         var writer = this.writer;
27648         var self  = this;
27649         this.handlers = {
27650             // #text
27651             3: function(node) {
27652                 
27653                 writer.text(node.nodeValue, node);
27654             },
27655             // #comment
27656             8: function(node) {
27657                 writer.comment(node.nodeValue);
27658             },
27659             // Processing instruction
27660             7: function(node) {
27661                 writer.pi(node.name, node.nodeValue);
27662             },
27663             // Doctype
27664             10: function(node) {
27665                 writer.doctype(node.nodeValue);
27666             },
27667             // CDATA
27668             4: function(node) {
27669                 writer.cdata(node.nodeValue);
27670             },
27671             // Document fragment
27672             11: function(node) {
27673                 node = node.firstChild;
27674                 if (!node) {
27675                     return;
27676                 }
27677                 while(node) {
27678                     self.walk(node);
27679                     node = node.nextSibling
27680                 }
27681             }
27682         };
27683         writer.reset();
27684         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27685         return writer.getContent();
27686     },
27687
27688     walk: function(node)
27689     {
27690         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27691             handler = this.handlers[node.nodeType];
27692             
27693         if (handler) {
27694             handler(node);
27695             return;
27696         }
27697     
27698         var name = node.nodeName;
27699         var isEmpty = node.childNodes.length < 1;
27700       
27701         var writer = this.writer;
27702         var attrs = node.attributes;
27703         // Sort attributes
27704         
27705         writer.start(node.nodeName, attrs, isEmpty, node);
27706         if (isEmpty) {
27707             return;
27708         }
27709         node = node.firstChild;
27710         if (!node) {
27711             writer.end(name);
27712             return;
27713         }
27714         while (node) {
27715             this.walk(node);
27716             node = node.nextSibling;
27717         }
27718         writer.end(name);
27719         
27720     
27721     }
27722     // Serialize element and treat all non elements as fragments
27723    
27724 }; 
27725
27726 /***
27727  * This is based loosely on tinymce 
27728  * @class Roo.htmleditor.TidyWriter
27729  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27730  *
27731  * Known issues?
27732  * - not tested much with 'PRE' formated elements.
27733  * 
27734  *
27735  *
27736  */
27737
27738 Roo.htmleditor.TidyWriter = function(settings)
27739 {
27740     
27741     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27742     Roo.apply(this, settings);
27743     this.html = [];
27744     this.state = [];
27745      
27746     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27747   
27748 }
27749 Roo.htmleditor.TidyWriter.prototype = {
27750
27751  
27752     state : false,
27753     
27754     indent :  '  ',
27755     
27756     // part of state...
27757     indentstr : '',
27758     in_pre: false,
27759     in_inline : false,
27760     last_inline : false,
27761     encode : false,
27762      
27763     
27764             /**
27765     * Writes the a start element such as <p id="a">.
27766     *
27767     * @method start
27768     * @param {String} name Name of the element.
27769     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27770     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27771     */
27772     start: function(name, attrs, empty, node)
27773     {
27774         var i, l, attr, value;
27775         
27776         // there are some situations where adding line break && indentation will not work. will not work.
27777         // <span / b / i ... formating?
27778         
27779         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27780         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27781         
27782         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27783         
27784         var add_lb = name == 'BR' ? false : in_inline;
27785         
27786         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27787             i_inline = false;
27788         }
27789
27790         var indentstr =  this.indentstr;
27791         
27792         // e_inline = elements that can be inline, but still allow \n before and after?
27793         // only 'BR' ??? any others?
27794         
27795         // ADD LINE BEFORE tage
27796         if (!this.in_pre) {
27797             if (in_inline) {
27798                 //code
27799                 if (name == 'BR') {
27800                     this.addLine();
27801                 } else if (this.lastElementEndsWS()) {
27802                     this.addLine();
27803                 } else{
27804                     // otherwise - no new line. (and dont indent.)
27805                     indentstr = '';
27806                 }
27807                 
27808             } else {
27809                 this.addLine();
27810             }
27811         } else {
27812             indentstr = '';
27813         }
27814         
27815         this.html.push(indentstr + '<', name.toLowerCase());
27816         
27817         if (attrs) {
27818             for (i = 0, l = attrs.length; i < l; i++) {
27819                 attr = attrs[i];
27820                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27821             }
27822         }
27823      
27824         if (empty) {
27825             if (is_short) {
27826                 this.html[this.html.length] = '/>';
27827             } else {
27828                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27829             }
27830             var e_inline = name == 'BR' ? false : this.in_inline;
27831             
27832             if (!e_inline && !this.in_pre) {
27833                 this.addLine();
27834             }
27835             return;
27836         
27837         }
27838         // not empty..
27839         this.html[this.html.length] = '>';
27840         
27841         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27842         /*
27843         if (!in_inline && !in_pre) {
27844             var cn = node.firstChild;
27845             while(cn) {
27846                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27847                     in_inline = true
27848                     break;
27849                 }
27850                 cn = cn.nextSibling;
27851             }
27852              
27853         }
27854         */
27855         
27856         
27857         this.pushState({
27858             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27859             in_pre : in_pre,
27860             in_inline :  in_inline
27861         });
27862         // add a line after if we are not in a
27863         
27864         if (!in_inline && !in_pre) {
27865             this.addLine();
27866         }
27867         
27868             
27869          
27870         
27871     },
27872     
27873     lastElementEndsWS : function()
27874     {
27875         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27876         if (value === false) {
27877             return true;
27878         }
27879         return value.match(/\s+$/);
27880         
27881     },
27882     
27883     /**
27884      * Writes the a end element such as </p>.
27885      *
27886      * @method end
27887      * @param {String} name Name of the element.
27888      */
27889     end: function(name) {
27890         var value;
27891         this.popState();
27892         var indentstr = '';
27893         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27894         
27895         if (!this.in_pre && !in_inline) {
27896             this.addLine();
27897             indentstr  = this.indentstr;
27898         }
27899         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27900         this.last_inline = in_inline;
27901         
27902         // pop the indent state..
27903     },
27904     /**
27905      * Writes a text node.
27906      *
27907      * In pre - we should not mess with the contents.
27908      * 
27909      *
27910      * @method text
27911      * @param {String} text String to write out.
27912      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27913      */
27914     text: function(in_text, node)
27915     {
27916         // if not in whitespace critical
27917         if (in_text.length < 1) {
27918             return;
27919         }
27920         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27921         
27922         if (this.in_pre) {
27923             this.html[this.html.length] =  text;
27924             return;   
27925         }
27926         
27927         if (this.in_inline) {
27928             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27929             if (text != ' ') {
27930                 text = text.replace(/\s+/,' ');  // all white space to single white space
27931                 
27932                     
27933                 // if next tag is '<BR>', then we can trim right..
27934                 if (node.nextSibling &&
27935                     node.nextSibling.nodeType == 1 &&
27936                     node.nextSibling.nodeName == 'BR' )
27937                 {
27938                     text = text.replace(/\s+$/g,'');
27939                 }
27940                 // if previous tag was a BR, we can also trim..
27941                 if (node.previousSibling &&
27942                     node.previousSibling.nodeType == 1 &&
27943                     node.previousSibling.nodeName == 'BR' )
27944                 {
27945                     text = this.indentstr +  text.replace(/^\s+/g,'');
27946                 }
27947                 if (text.match(/\n/)) {
27948                     text = text.replace(
27949                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27950                     );
27951                     // remoeve the last whitespace / line break.
27952                     text = text.replace(/\n\s+$/,'');
27953                 }
27954                 // repace long lines
27955                 
27956             }
27957              
27958             this.html[this.html.length] =  text;
27959             return;   
27960         }
27961         // see if previous element was a inline element.
27962         var indentstr = this.indentstr;
27963    
27964         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27965         
27966         // should trim left?
27967         if (node.previousSibling &&
27968             node.previousSibling.nodeType == 1 &&
27969             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27970         {
27971             indentstr = '';
27972             
27973         } else {
27974             this.addLine();
27975             text = text.replace(/^\s+/,''); // trim left
27976           
27977         }
27978         // should trim right?
27979         if (node.nextSibling &&
27980             node.nextSibling.nodeType == 1 &&
27981             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27982         {
27983           // noop
27984             
27985         }  else {
27986             text = text.replace(/\s+$/,''); // trim right
27987         }
27988          
27989               
27990         
27991         
27992         
27993         if (text.length < 1) {
27994             return;
27995         }
27996         if (!text.match(/\n/)) {
27997             this.html.push(indentstr + text);
27998             return;
27999         }
28000         
28001         text = this.indentstr + text.replace(
28002             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28003         );
28004         // remoeve the last whitespace / line break.
28005         text = text.replace(/\s+$/,''); 
28006         
28007         this.html.push(text);
28008         
28009         // split and indent..
28010         
28011         
28012     },
28013     /**
28014      * Writes a cdata node such as <![CDATA[data]]>.
28015      *
28016      * @method cdata
28017      * @param {String} text String to write out inside the cdata.
28018      */
28019     cdata: function(text) {
28020         this.html.push('<![CDATA[', text, ']]>');
28021     },
28022     /**
28023     * Writes a comment node such as <!-- Comment -->.
28024     *
28025     * @method cdata
28026     * @param {String} text String to write out inside the comment.
28027     */
28028    comment: function(text) {
28029        this.html.push('<!--', text, '-->');
28030    },
28031     /**
28032      * Writes a PI node such as <?xml attr="value" ?>.
28033      *
28034      * @method pi
28035      * @param {String} name Name of the pi.
28036      * @param {String} text String to write out inside the pi.
28037      */
28038     pi: function(name, text) {
28039         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28040         this.indent != '' && this.html.push('\n');
28041     },
28042     /**
28043      * Writes a doctype node such as <!DOCTYPE data>.
28044      *
28045      * @method doctype
28046      * @param {String} text String to write out inside the doctype.
28047      */
28048     doctype: function(text) {
28049         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28050     },
28051     /**
28052      * Resets the internal buffer if one wants to reuse the writer.
28053      *
28054      * @method reset
28055      */
28056     reset: function() {
28057         this.html.length = 0;
28058         this.state = [];
28059         this.pushState({
28060             indentstr : '',
28061             in_pre : false, 
28062             in_inline : false
28063         })
28064     },
28065     /**
28066      * Returns the contents that got serialized.
28067      *
28068      * @method getContent
28069      * @return {String} HTML contents that got written down.
28070      */
28071     getContent: function() {
28072         return this.html.join('').replace(/\n$/, '');
28073     },
28074     
28075     pushState : function(cfg)
28076     {
28077         this.state.push(cfg);
28078         Roo.apply(this, cfg);
28079     },
28080     
28081     popState : function()
28082     {
28083         if (this.state.length < 1) {
28084             return; // nothing to push
28085         }
28086         var cfg = {
28087             in_pre: false,
28088             indentstr : ''
28089         };
28090         this.state.pop();
28091         if (this.state.length > 0) {
28092             cfg = this.state[this.state.length-1]; 
28093         }
28094         Roo.apply(this, cfg);
28095     },
28096     
28097     addLine: function()
28098     {
28099         if (this.html.length < 1) {
28100             return;
28101         }
28102         
28103         
28104         var value = this.html[this.html.length - 1];
28105         if (value.length > 0 && '\n' !== value) {
28106             this.html.push('\n');
28107         }
28108     }
28109     
28110     
28111 //'pre script noscript style textarea video audio iframe object code'
28112 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28113 // inline 
28114 };
28115
28116 Roo.htmleditor.TidyWriter.inline_elements = [
28117         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28118         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28119 ];
28120 Roo.htmleditor.TidyWriter.shortend_elements = [
28121     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28122     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28123 ];
28124
28125 Roo.htmleditor.TidyWriter.whitespace_elements = [
28126     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28127 ];/***
28128  * This is based loosely on tinymce 
28129  * @class Roo.htmleditor.TidyEntities
28130  * @static
28131  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28132  *
28133  * Not 100% sure this is actually used or needed.
28134  */
28135
28136 Roo.htmleditor.TidyEntities = {
28137     
28138     /**
28139      * initialize data..
28140      */
28141     init : function (){
28142      
28143         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28144        
28145     },
28146
28147
28148     buildEntitiesLookup: function(items, radix) {
28149         var i, chr, entity, lookup = {};
28150         if (!items) {
28151             return {};
28152         }
28153         items = typeof(items) == 'string' ? items.split(',') : items;
28154         radix = radix || 10;
28155         // Build entities lookup table
28156         for (i = 0; i < items.length; i += 2) {
28157             chr = String.fromCharCode(parseInt(items[i], radix));
28158             // Only add non base entities
28159             if (!this.baseEntities[chr]) {
28160                 entity = '&' + items[i + 1] + ';';
28161                 lookup[chr] = entity;
28162                 lookup[entity] = chr;
28163             }
28164         }
28165         return lookup;
28166         
28167     },
28168     
28169     asciiMap : {
28170             128: '€',
28171             130: '‚',
28172             131: 'ƒ',
28173             132: '„',
28174             133: '…',
28175             134: '†',
28176             135: '‡',
28177             136: 'ˆ',
28178             137: '‰',
28179             138: 'Š',
28180             139: '‹',
28181             140: 'Œ',
28182             142: 'Ž',
28183             145: '‘',
28184             146: '’',
28185             147: '“',
28186             148: '”',
28187             149: '•',
28188             150: '–',
28189             151: '—',
28190             152: '˜',
28191             153: '™',
28192             154: 'š',
28193             155: '›',
28194             156: 'œ',
28195             158: 'ž',
28196             159: 'Ÿ'
28197     },
28198     // Raw entities
28199     baseEntities : {
28200         '"': '&quot;',
28201         // Needs to be escaped since the YUI compressor would otherwise break the code
28202         '\'': '&#39;',
28203         '<': '&lt;',
28204         '>': '&gt;',
28205         '&': '&amp;',
28206         '`': '&#96;'
28207     },
28208     // Reverse lookup table for raw entities
28209     reverseEntities : {
28210         '&lt;': '<',
28211         '&gt;': '>',
28212         '&amp;': '&',
28213         '&quot;': '"',
28214         '&apos;': '\''
28215     },
28216     
28217     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28218     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28219     rawCharsRegExp : /[<>&\"\']/g,
28220     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28221     namedEntities  : false,
28222     namedEntitiesData : [ 
28223         '50',
28224         'nbsp',
28225         '51',
28226         'iexcl',
28227         '52',
28228         'cent',
28229         '53',
28230         'pound',
28231         '54',
28232         'curren',
28233         '55',
28234         'yen',
28235         '56',
28236         'brvbar',
28237         '57',
28238         'sect',
28239         '58',
28240         'uml',
28241         '59',
28242         'copy',
28243         '5a',
28244         'ordf',
28245         '5b',
28246         'laquo',
28247         '5c',
28248         'not',
28249         '5d',
28250         'shy',
28251         '5e',
28252         'reg',
28253         '5f',
28254         'macr',
28255         '5g',
28256         'deg',
28257         '5h',
28258         'plusmn',
28259         '5i',
28260         'sup2',
28261         '5j',
28262         'sup3',
28263         '5k',
28264         'acute',
28265         '5l',
28266         'micro',
28267         '5m',
28268         'para',
28269         '5n',
28270         'middot',
28271         '5o',
28272         'cedil',
28273         '5p',
28274         'sup1',
28275         '5q',
28276         'ordm',
28277         '5r',
28278         'raquo',
28279         '5s',
28280         'frac14',
28281         '5t',
28282         'frac12',
28283         '5u',
28284         'frac34',
28285         '5v',
28286         'iquest',
28287         '60',
28288         'Agrave',
28289         '61',
28290         'Aacute',
28291         '62',
28292         'Acirc',
28293         '63',
28294         'Atilde',
28295         '64',
28296         'Auml',
28297         '65',
28298         'Aring',
28299         '66',
28300         'AElig',
28301         '67',
28302         'Ccedil',
28303         '68',
28304         'Egrave',
28305         '69',
28306         'Eacute',
28307         '6a',
28308         'Ecirc',
28309         '6b',
28310         'Euml',
28311         '6c',
28312         'Igrave',
28313         '6d',
28314         'Iacute',
28315         '6e',
28316         'Icirc',
28317         '6f',
28318         'Iuml',
28319         '6g',
28320         'ETH',
28321         '6h',
28322         'Ntilde',
28323         '6i',
28324         'Ograve',
28325         '6j',
28326         'Oacute',
28327         '6k',
28328         'Ocirc',
28329         '6l',
28330         'Otilde',
28331         '6m',
28332         'Ouml',
28333         '6n',
28334         'times',
28335         '6o',
28336         'Oslash',
28337         '6p',
28338         'Ugrave',
28339         '6q',
28340         'Uacute',
28341         '6r',
28342         'Ucirc',
28343         '6s',
28344         'Uuml',
28345         '6t',
28346         'Yacute',
28347         '6u',
28348         'THORN',
28349         '6v',
28350         'szlig',
28351         '70',
28352         'agrave',
28353         '71',
28354         'aacute',
28355         '72',
28356         'acirc',
28357         '73',
28358         'atilde',
28359         '74',
28360         'auml',
28361         '75',
28362         'aring',
28363         '76',
28364         'aelig',
28365         '77',
28366         'ccedil',
28367         '78',
28368         'egrave',
28369         '79',
28370         'eacute',
28371         '7a',
28372         'ecirc',
28373         '7b',
28374         'euml',
28375         '7c',
28376         'igrave',
28377         '7d',
28378         'iacute',
28379         '7e',
28380         'icirc',
28381         '7f',
28382         'iuml',
28383         '7g',
28384         'eth',
28385         '7h',
28386         'ntilde',
28387         '7i',
28388         'ograve',
28389         '7j',
28390         'oacute',
28391         '7k',
28392         'ocirc',
28393         '7l',
28394         'otilde',
28395         '7m',
28396         'ouml',
28397         '7n',
28398         'divide',
28399         '7o',
28400         'oslash',
28401         '7p',
28402         'ugrave',
28403         '7q',
28404         'uacute',
28405         '7r',
28406         'ucirc',
28407         '7s',
28408         'uuml',
28409         '7t',
28410         'yacute',
28411         '7u',
28412         'thorn',
28413         '7v',
28414         'yuml',
28415         'ci',
28416         'fnof',
28417         'sh',
28418         'Alpha',
28419         'si',
28420         'Beta',
28421         'sj',
28422         'Gamma',
28423         'sk',
28424         'Delta',
28425         'sl',
28426         'Epsilon',
28427         'sm',
28428         'Zeta',
28429         'sn',
28430         'Eta',
28431         'so',
28432         'Theta',
28433         'sp',
28434         'Iota',
28435         'sq',
28436         'Kappa',
28437         'sr',
28438         'Lambda',
28439         'ss',
28440         'Mu',
28441         'st',
28442         'Nu',
28443         'su',
28444         'Xi',
28445         'sv',
28446         'Omicron',
28447         't0',
28448         'Pi',
28449         't1',
28450         'Rho',
28451         't3',
28452         'Sigma',
28453         't4',
28454         'Tau',
28455         't5',
28456         'Upsilon',
28457         't6',
28458         'Phi',
28459         't7',
28460         'Chi',
28461         't8',
28462         'Psi',
28463         't9',
28464         'Omega',
28465         'th',
28466         'alpha',
28467         'ti',
28468         'beta',
28469         'tj',
28470         'gamma',
28471         'tk',
28472         'delta',
28473         'tl',
28474         'epsilon',
28475         'tm',
28476         'zeta',
28477         'tn',
28478         'eta',
28479         'to',
28480         'theta',
28481         'tp',
28482         'iota',
28483         'tq',
28484         'kappa',
28485         'tr',
28486         'lambda',
28487         'ts',
28488         'mu',
28489         'tt',
28490         'nu',
28491         'tu',
28492         'xi',
28493         'tv',
28494         'omicron',
28495         'u0',
28496         'pi',
28497         'u1',
28498         'rho',
28499         'u2',
28500         'sigmaf',
28501         'u3',
28502         'sigma',
28503         'u4',
28504         'tau',
28505         'u5',
28506         'upsilon',
28507         'u6',
28508         'phi',
28509         'u7',
28510         'chi',
28511         'u8',
28512         'psi',
28513         'u9',
28514         'omega',
28515         'uh',
28516         'thetasym',
28517         'ui',
28518         'upsih',
28519         'um',
28520         'piv',
28521         '812',
28522         'bull',
28523         '816',
28524         'hellip',
28525         '81i',
28526         'prime',
28527         '81j',
28528         'Prime',
28529         '81u',
28530         'oline',
28531         '824',
28532         'frasl',
28533         '88o',
28534         'weierp',
28535         '88h',
28536         'image',
28537         '88s',
28538         'real',
28539         '892',
28540         'trade',
28541         '89l',
28542         'alefsym',
28543         '8cg',
28544         'larr',
28545         '8ch',
28546         'uarr',
28547         '8ci',
28548         'rarr',
28549         '8cj',
28550         'darr',
28551         '8ck',
28552         'harr',
28553         '8dl',
28554         'crarr',
28555         '8eg',
28556         'lArr',
28557         '8eh',
28558         'uArr',
28559         '8ei',
28560         'rArr',
28561         '8ej',
28562         'dArr',
28563         '8ek',
28564         'hArr',
28565         '8g0',
28566         'forall',
28567         '8g2',
28568         'part',
28569         '8g3',
28570         'exist',
28571         '8g5',
28572         'empty',
28573         '8g7',
28574         'nabla',
28575         '8g8',
28576         'isin',
28577         '8g9',
28578         'notin',
28579         '8gb',
28580         'ni',
28581         '8gf',
28582         'prod',
28583         '8gh',
28584         'sum',
28585         '8gi',
28586         'minus',
28587         '8gn',
28588         'lowast',
28589         '8gq',
28590         'radic',
28591         '8gt',
28592         'prop',
28593         '8gu',
28594         'infin',
28595         '8h0',
28596         'ang',
28597         '8h7',
28598         'and',
28599         '8h8',
28600         'or',
28601         '8h9',
28602         'cap',
28603         '8ha',
28604         'cup',
28605         '8hb',
28606         'int',
28607         '8hk',
28608         'there4',
28609         '8hs',
28610         'sim',
28611         '8i5',
28612         'cong',
28613         '8i8',
28614         'asymp',
28615         '8j0',
28616         'ne',
28617         '8j1',
28618         'equiv',
28619         '8j4',
28620         'le',
28621         '8j5',
28622         'ge',
28623         '8k2',
28624         'sub',
28625         '8k3',
28626         'sup',
28627         '8k4',
28628         'nsub',
28629         '8k6',
28630         'sube',
28631         '8k7',
28632         'supe',
28633         '8kl',
28634         'oplus',
28635         '8kn',
28636         'otimes',
28637         '8l5',
28638         'perp',
28639         '8m5',
28640         'sdot',
28641         '8o8',
28642         'lceil',
28643         '8o9',
28644         'rceil',
28645         '8oa',
28646         'lfloor',
28647         '8ob',
28648         'rfloor',
28649         '8p9',
28650         'lang',
28651         '8pa',
28652         'rang',
28653         '9ea',
28654         'loz',
28655         '9j0',
28656         'spades',
28657         '9j3',
28658         'clubs',
28659         '9j5',
28660         'hearts',
28661         '9j6',
28662         'diams',
28663         'ai',
28664         'OElig',
28665         'aj',
28666         'oelig',
28667         'b0',
28668         'Scaron',
28669         'b1',
28670         'scaron',
28671         'bo',
28672         'Yuml',
28673         'm6',
28674         'circ',
28675         'ms',
28676         'tilde',
28677         '802',
28678         'ensp',
28679         '803',
28680         'emsp',
28681         '809',
28682         'thinsp',
28683         '80c',
28684         'zwnj',
28685         '80d',
28686         'zwj',
28687         '80e',
28688         'lrm',
28689         '80f',
28690         'rlm',
28691         '80j',
28692         'ndash',
28693         '80k',
28694         'mdash',
28695         '80o',
28696         'lsquo',
28697         '80p',
28698         'rsquo',
28699         '80q',
28700         'sbquo',
28701         '80s',
28702         'ldquo',
28703         '80t',
28704         'rdquo',
28705         '80u',
28706         'bdquo',
28707         '810',
28708         'dagger',
28709         '811',
28710         'Dagger',
28711         '81g',
28712         'permil',
28713         '81p',
28714         'lsaquo',
28715         '81q',
28716         'rsaquo',
28717         '85c',
28718         'euro'
28719     ],
28720
28721          
28722     /**
28723      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28724      *
28725      * @method encodeRaw
28726      * @param {String} text Text to encode.
28727      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28728      * @return {String} Entity encoded text.
28729      */
28730     encodeRaw: function(text, attr)
28731     {
28732         var t = this;
28733         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28734             return t.baseEntities[chr] || chr;
28735         });
28736     },
28737     /**
28738      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28739      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28740      * and is exposed as the DOMUtils.encode function.
28741      *
28742      * @method encodeAllRaw
28743      * @param {String} text Text to encode.
28744      * @return {String} Entity encoded text.
28745      */
28746     encodeAllRaw: function(text) {
28747         var t = this;
28748         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28749             return t.baseEntities[chr] || chr;
28750         });
28751     },
28752     /**
28753      * Encodes the specified string using numeric entities. The core entities will be
28754      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28755      *
28756      * @method encodeNumeric
28757      * @param {String} text Text to encode.
28758      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28759      * @return {String} Entity encoded text.
28760      */
28761     encodeNumeric: function(text, attr) {
28762         var t = this;
28763         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28764             // Multi byte sequence convert it to a single entity
28765             if (chr.length > 1) {
28766                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28767             }
28768             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28769         });
28770     },
28771     /**
28772      * Encodes the specified string using named entities. The core entities will be encoded
28773      * as named ones but all non lower ascii characters will be encoded into named entities.
28774      *
28775      * @method encodeNamed
28776      * @param {String} text Text to encode.
28777      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28778      * @param {Object} entities Optional parameter with entities to use.
28779      * @return {String} Entity encoded text.
28780      */
28781     encodeNamed: function(text, attr, entities) {
28782         var t = this;
28783         entities = entities || this.namedEntities;
28784         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28785             return t.baseEntities[chr] || entities[chr] || chr;
28786         });
28787     },
28788     /**
28789      * Returns an encode function based on the name(s) and it's optional entities.
28790      *
28791      * @method getEncodeFunc
28792      * @param {String} name Comma separated list of encoders for example named,numeric.
28793      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28794      * @return {function} Encode function to be used.
28795      */
28796     getEncodeFunc: function(name, entities) {
28797         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28798         var t = this;
28799         function encodeNamedAndNumeric(text, attr) {
28800             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28801                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28802             });
28803         }
28804
28805         function encodeCustomNamed(text, attr) {
28806             return t.encodeNamed(text, attr, entities);
28807         }
28808         // Replace + with , to be compatible with previous TinyMCE versions
28809         name = this.makeMap(name.replace(/\+/g, ','));
28810         // Named and numeric encoder
28811         if (name.named && name.numeric) {
28812             return this.encodeNamedAndNumeric;
28813         }
28814         // Named encoder
28815         if (name.named) {
28816             // Custom names
28817             if (entities) {
28818                 return encodeCustomNamed;
28819             }
28820             return this.encodeNamed;
28821         }
28822         // Numeric
28823         if (name.numeric) {
28824             return this.encodeNumeric;
28825         }
28826         // Raw encoder
28827         return this.encodeRaw;
28828     },
28829     /**
28830      * Decodes the specified string, this will replace entities with raw UTF characters.
28831      *
28832      * @method decode
28833      * @param {String} text Text to entity decode.
28834      * @return {String} Entity decoded string.
28835      */
28836     decode: function(text)
28837     {
28838         var  t = this;
28839         return text.replace(this.entityRegExp, function(all, numeric) {
28840             if (numeric) {
28841                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28842                 // Support upper UTF
28843                 if (numeric > 65535) {
28844                     numeric -= 65536;
28845                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28846                 }
28847                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28848             }
28849             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28850         });
28851     },
28852     nativeDecode : function (text) {
28853         return text;
28854     },
28855     makeMap : function (items, delim, map) {
28856                 var i;
28857                 items = items || [];
28858                 delim = delim || ',';
28859                 if (typeof items == "string") {
28860                         items = items.split(delim);
28861                 }
28862                 map = map || {};
28863                 i = items.length;
28864                 while (i--) {
28865                         map[items[i]] = {};
28866                 }
28867                 return map;
28868         }
28869 };
28870     
28871     
28872     
28873 Roo.htmleditor.TidyEntities.init();
28874 /**
28875  * @class Roo.htmleditor.KeyEnter
28876  * Handle Enter press..
28877  * @cfg {Roo.HtmlEditorCore} core the editor.
28878  * @constructor
28879  * Create a new Filter.
28880  * @param {Object} config Configuration options
28881  */
28882
28883
28884
28885
28886
28887 Roo.htmleditor.KeyEnter = function(cfg) {
28888     Roo.apply(this, cfg);
28889     // this does not actually call walk as it's really just a abstract class
28890  
28891     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28892 }
28893
28894 //Roo.htmleditor.KeyEnter.i = 0;
28895
28896
28897 Roo.htmleditor.KeyEnter.prototype = {
28898     
28899     core : false,
28900     
28901     keypress : function(e)
28902     {
28903         if (e.charCode != 13 && e.charCode != 10) {
28904             Roo.log([e.charCode,e]);
28905             return true;
28906         }
28907         e.preventDefault();
28908         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28909         var doc = this.core.doc;
28910           //add a new line
28911        
28912     
28913         var sel = this.core.getSelection();
28914         var range = sel.getRangeAt(0);
28915         var n = range.commonAncestorContainer;
28916         var pc = range.closest([ 'ol', 'ul']);
28917         var pli = range.closest('li');
28918         if (!pc || e.ctrlKey) {
28919             // on it list, or ctrl pressed.
28920             if (!e.ctrlKey) {
28921                 sel.insertNode('br', 'after'); 
28922             } else {
28923                 // only do this if we have ctrl key..
28924                 var br = doc.createElement('br');
28925                 br.className = 'clear';
28926                 br.setAttribute('style', 'clear: both');
28927                 sel.insertNode(br, 'after'); 
28928             }
28929             
28930          
28931             this.core.undoManager.addEvent();
28932             this.core.fireEditorEvent(e);
28933             return false;
28934         }
28935         
28936         // deal with <li> insetion
28937         if (pli.innerText.trim() == '' &&
28938             pli.previousSibling &&
28939             pli.previousSibling.nodeName == 'LI' &&
28940             pli.previousSibling.innerText.trim() ==  '') {
28941             pli.parentNode.removeChild(pli.previousSibling);
28942             sel.cursorAfter(pc);
28943             this.core.undoManager.addEvent();
28944             this.core.fireEditorEvent(e);
28945             return false;
28946         }
28947     
28948         var li = doc.createElement('LI');
28949         li.innerHTML = '&nbsp;';
28950         if (!pli || !pli.firstSibling) {
28951             pc.appendChild(li);
28952         } else {
28953             pli.parentNode.insertBefore(li, pli.firstSibling);
28954         }
28955         sel.cursorText (li.firstChild);
28956       
28957         this.core.undoManager.addEvent();
28958         this.core.fireEditorEvent(e);
28959
28960         return false;
28961         
28962     
28963         
28964         
28965          
28966     }
28967 };
28968      
28969 /**
28970  * @class Roo.htmleditor.Block
28971  * Base class for html editor blocks - do not use it directly .. extend it..
28972  * @cfg {DomElement} node The node to apply stuff to.
28973  * @cfg {String} friendly_name the name that appears in the context bar about this block
28974  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28975  
28976  * @constructor
28977  * Create a new Filter.
28978  * @param {Object} config Configuration options
28979  */
28980
28981 Roo.htmleditor.Block  = function(cfg)
28982 {
28983     // do nothing .. should not be called really.
28984 }
28985 /**
28986  * factory method to get the block from an element (using cache if necessary)
28987  * @static
28988  * @param {HtmlElement} the dom element
28989  */
28990 Roo.htmleditor.Block.factory = function(node)
28991 {
28992     var cc = Roo.htmleditor.Block.cache;
28993     var id = Roo.get(node).id;
28994     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28995         Roo.htmleditor.Block.cache[id].readElement(node);
28996         return Roo.htmleditor.Block.cache[id];
28997     }
28998     var db  = node.getAttribute('data-block');
28999     if (!db) {
29000         db = node.nodeName.toLowerCase().toUpperCaseFirst();
29001     }
29002     var cls = Roo.htmleditor['Block' + db];
29003     if (typeof(cls) == 'undefined') {
29004         //Roo.log(node.getAttribute('data-block'));
29005         Roo.log("OOps missing block : " + 'Block' + db);
29006         return false;
29007     }
29008     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29009     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29010 };
29011
29012 /**
29013  * initalize all Elements from content that are 'blockable'
29014  * @static
29015  * @param the body element
29016  */
29017 Roo.htmleditor.Block.initAll = function(body, type)
29018 {
29019     if (typeof(type) == 'undefined') {
29020         var ia = Roo.htmleditor.Block.initAll;
29021         ia(body,'table');
29022         ia(body,'td');
29023         ia(body,'figure');
29024         return;
29025     }
29026     Roo.each(Roo.get(body).query(type), function(e) {
29027         Roo.htmleditor.Block.factory(e);    
29028     },this);
29029 };
29030 // question goes here... do we need to clear out this cache sometimes?
29031 // or show we make it relivant to the htmleditor.
29032 Roo.htmleditor.Block.cache = {};
29033
29034 Roo.htmleditor.Block.prototype = {
29035     
29036     node : false,
29037     
29038      // used by context menu
29039     friendly_name : 'Based Block',
29040     
29041     // text for button to delete this element
29042     deleteTitle : false,
29043     
29044     context : false,
29045     /**
29046      * Update a node with values from this object
29047      * @param {DomElement} node
29048      */
29049     updateElement : function(node)
29050     {
29051         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29052     },
29053      /**
29054      * convert to plain HTML for calling insertAtCursor..
29055      */
29056     toHTML : function()
29057     {
29058         return Roo.DomHelper.markup(this.toObject());
29059     },
29060     /**
29061      * used by readEleemnt to extract data from a node
29062      * may need improving as it's pretty basic
29063      
29064      * @param {DomElement} node
29065      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29066      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29067      * @param {String} style the style property - eg. text-align
29068      */
29069     getVal : function(node, tag, attr, style)
29070     {
29071         var n = node;
29072         if (tag !== true && n.tagName != tag.toUpperCase()) {
29073             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29074             // but kiss for now.
29075             n = node.getElementsByTagName(tag).item(0);
29076         }
29077         if (!n) {
29078             return '';
29079         }
29080         if (attr === false) {
29081             return n;
29082         }
29083         if (attr == 'html') {
29084             return n.innerHTML;
29085         }
29086         if (attr == 'style') {
29087             return n.style[style]; 
29088         }
29089         
29090         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29091             
29092     },
29093     /**
29094      * create a DomHelper friendly object - for use with 
29095      * Roo.DomHelper.markup / overwrite / etc..
29096      * (override this)
29097      */
29098     toObject : function()
29099     {
29100         return {};
29101     },
29102       /**
29103      * Read a node that has a 'data-block' property - and extract the values from it.
29104      * @param {DomElement} node - the node
29105      */
29106     readElement : function(node)
29107     {
29108         
29109     } 
29110     
29111     
29112 };
29113
29114  
29115
29116 /**
29117  * @class Roo.htmleditor.BlockFigure
29118  * Block that has an image and a figcaption
29119  * @cfg {String} image_src the url for the image
29120  * @cfg {String} align (left|right) alignment for the block default left
29121  * @cfg {String} caption the text to appear below  (and in the alt tag)
29122  * @cfg {String} caption_display (block|none) display or not the caption
29123  * @cfg {String|number} image_width the width of the image number or %?
29124  * @cfg {String|number} image_height the height of the image number or %?
29125  * 
29126  * @constructor
29127  * Create a new Filter.
29128  * @param {Object} config Configuration options
29129  */
29130
29131 Roo.htmleditor.BlockFigure = function(cfg)
29132 {
29133     if (cfg.node) {
29134         this.readElement(cfg.node);
29135         this.updateElement(cfg.node);
29136     }
29137     Roo.apply(this, cfg);
29138 }
29139 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29140  
29141     
29142     // setable values.
29143     image_src: '',
29144     align: 'center',
29145     caption : '',
29146     caption_display : 'block',
29147     width : '100%',
29148     cls : '',
29149     href: '',
29150     video_url : '',
29151     
29152     // margin: '2%', not used
29153     
29154     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29155
29156     
29157     // used by context menu
29158     friendly_name : 'Image with caption',
29159     deleteTitle : "Delete Image and Caption",
29160     
29161     contextMenu : function(toolbar)
29162     {
29163         
29164         var block = function() {
29165             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29166         };
29167         
29168         
29169         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29170         
29171         var syncValue = toolbar.editorcore.syncValue;
29172         
29173         var fields = {};
29174         
29175         return [
29176              {
29177                 xtype : 'TextItem',
29178                 text : "Source: ",
29179                 xns : rooui.Toolbar  //Boostrap?
29180             },
29181             {
29182                 xtype : 'Button',
29183                 text: 'Change Image URL',
29184                  
29185                 listeners : {
29186                     click: function (btn, state)
29187                     {
29188                         var b = block();
29189                         
29190                         Roo.MessageBox.show({
29191                             title : "Image Source URL",
29192                             msg : "Enter the url for the image",
29193                             buttons: Roo.MessageBox.OKCANCEL,
29194                             fn: function(btn, val){
29195                                 if (btn != 'ok') {
29196                                     return;
29197                                 }
29198                                 b.image_src = val;
29199                                 b.updateElement();
29200                                 syncValue();
29201                                 toolbar.editorcore.onEditorEvent();
29202                             },
29203                             minWidth:250,
29204                             prompt:true,
29205                             //multiline: multiline,
29206                             modal : true,
29207                             value : b.image_src
29208                         });
29209                     }
29210                 },
29211                 xns : rooui.Toolbar
29212             },
29213          
29214             {
29215                 xtype : 'Button',
29216                 text: 'Change Link URL',
29217                  
29218                 listeners : {
29219                     click: function (btn, state)
29220                     {
29221                         var b = block();
29222                         
29223                         Roo.MessageBox.show({
29224                             title : "Link URL",
29225                             msg : "Enter the url for the link - leave blank to have no link",
29226                             buttons: Roo.MessageBox.OKCANCEL,
29227                             fn: function(btn, val){
29228                                 if (btn != 'ok') {
29229                                     return;
29230                                 }
29231                                 b.href = val;
29232                                 b.updateElement();
29233                                 syncValue();
29234                                 toolbar.editorcore.onEditorEvent();
29235                             },
29236                             minWidth:250,
29237                             prompt:true,
29238                             //multiline: multiline,
29239                             modal : true,
29240                             value : b.href
29241                         });
29242                     }
29243                 },
29244                 xns : rooui.Toolbar
29245             },
29246             {
29247                 xtype : 'Button',
29248                 text: 'Show Video URL',
29249                  
29250                 listeners : {
29251                     click: function (btn, state)
29252                     {
29253                         Roo.MessageBox.alert("Video URL",
29254                             block().video_url == '' ? 'This image is not linked ot a video' :
29255                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29256                     }
29257                 },
29258                 xns : rooui.Toolbar
29259             },
29260             
29261             
29262             {
29263                 xtype : 'TextItem',
29264                 text : "Width: ",
29265                 xns : rooui.Toolbar  //Boostrap?
29266             },
29267             {
29268                 xtype : 'ComboBox',
29269                 allowBlank : false,
29270                 displayField : 'val',
29271                 editable : true,
29272                 listWidth : 100,
29273                 triggerAction : 'all',
29274                 typeAhead : true,
29275                 valueField : 'val',
29276                 width : 70,
29277                 name : 'width',
29278                 listeners : {
29279                     select : function (combo, r, index)
29280                     {
29281                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29282                         var b = block();
29283                         b.width = r.get('val');
29284                         b.updateElement();
29285                         syncValue();
29286                         toolbar.editorcore.onEditorEvent();
29287                     }
29288                 },
29289                 xns : rooui.form,
29290                 store : {
29291                     xtype : 'SimpleStore',
29292                     data : [
29293                         ['100%'],
29294                         ['80%'],
29295                         ['50%'],
29296                         ['20%'],
29297                         ['10%']
29298                     ],
29299                     fields : [ 'val'],
29300                     xns : Roo.data
29301                 }
29302             },
29303             {
29304                 xtype : 'TextItem',
29305                 text : "Align: ",
29306                 xns : rooui.Toolbar  //Boostrap?
29307             },
29308             {
29309                 xtype : 'ComboBox',
29310                 allowBlank : false,
29311                 displayField : 'val',
29312                 editable : true,
29313                 listWidth : 100,
29314                 triggerAction : 'all',
29315                 typeAhead : true,
29316                 valueField : 'val',
29317                 width : 70,
29318                 name : 'align',
29319                 listeners : {
29320                     select : function (combo, r, index)
29321                     {
29322                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29323                         var b = block();
29324                         b.align = r.get('val');
29325                         b.updateElement();
29326                         syncValue();
29327                         toolbar.editorcore.onEditorEvent();
29328                     }
29329                 },
29330                 xns : rooui.form,
29331                 store : {
29332                     xtype : 'SimpleStore',
29333                     data : [
29334                         ['left'],
29335                         ['right'],
29336                         ['center']
29337                     ],
29338                     fields : [ 'val'],
29339                     xns : Roo.data
29340                 }
29341             },
29342             
29343               
29344             {
29345                 xtype : 'Button',
29346                 text: 'Hide Caption',
29347                 name : 'caption_display',
29348                 pressed : false,
29349                 enableToggle : true,
29350                 setValue : function(v) {
29351                     // this trigger toggle.
29352                      
29353                     this.setText(v ? "Hide Caption" : "Show Caption");
29354                     this.setPressed(v != 'block');
29355                 },
29356                 listeners : {
29357                     toggle: function (btn, state)
29358                     {
29359                         var b  = block();
29360                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29361                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29362                         b.updateElement();
29363                         syncValue();
29364                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29365                         toolbar.editorcore.onEditorEvent();
29366                     }
29367                 },
29368                 xns : rooui.Toolbar
29369             }
29370         ];
29371         
29372     },
29373     /**
29374      * create a DomHelper friendly object - for use with
29375      * Roo.DomHelper.markup / overwrite / etc..
29376      */
29377     toObject : function()
29378     {
29379         var d = document.createElement('div');
29380         d.innerHTML = this.caption;
29381         
29382         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29383         
29384         var iw = this.align == 'center' ? this.width : '100%';
29385         var img =   {
29386             tag : 'img',
29387             contenteditable : 'false',
29388             src : this.image_src,
29389             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29390             style: {
29391                 width : iw,
29392                 maxWidth : iw + ' !important', // this is not getting rendered?
29393                 margin : m  
29394                 
29395             }
29396         };
29397         /*
29398         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29399                     '<a href="{2}">' + 
29400                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29401                     '</a>' + 
29402                 '</div>',
29403         */
29404                 
29405         if (this.href.length > 0) {
29406             img = {
29407                 tag : 'a',
29408                 href: this.href,
29409                 contenteditable : 'true',
29410                 cn : [
29411                     img
29412                 ]
29413             };
29414         }
29415         
29416         
29417         if (this.video_url.length > 0) {
29418             img = {
29419                 tag : 'div',
29420                 cls : this.cls,
29421                 frameborder : 0,
29422                 allowfullscreen : true,
29423                 width : 420,  // these are for video tricks - that we replace the outer
29424                 height : 315,
29425                 src : this.video_url,
29426                 cn : [
29427                     img
29428                 ]
29429             };
29430         }
29431         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29432         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29433         
29434   
29435         var ret =   {
29436             tag: 'figure',
29437             'data-block' : 'Figure',
29438             'data-width' : this.width,
29439             'data-caption' : this.caption, 
29440             contenteditable : 'false',
29441             
29442             style : {
29443                 display: 'block',
29444                 float :  this.align ,
29445                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29446                 width : this.align == 'center' ? '100%' : this.width,
29447                 margin:  '0px',
29448                 padding: this.align == 'center' ? '0' : '0 10px' ,
29449                 textAlign : this.align   // seems to work for email..
29450                 
29451             },
29452            
29453             
29454             align : this.align,
29455             cn : [
29456                 img,
29457               
29458                 {
29459                     tag: 'figcaption',
29460                     'data-display' : this.caption_display,
29461                     style : {
29462                         textAlign : 'left',
29463                         fontSize : '16px',
29464                         lineHeight : '24px',
29465                         display : this.caption_display,
29466                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29467                         margin: m,
29468                         width: this.align == 'center' ?  this.width : '100%' 
29469                     
29470                          
29471                     },
29472                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29473                     cn : [
29474                         {
29475                             tag: 'div',
29476                             style  : {
29477                                 marginTop : '16px',
29478                                 textAlign : 'left'
29479                             },
29480                             align: 'left',
29481                             cn : [
29482                                 {
29483                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29484                                     tag : 'i',
29485                                     contenteditable : true,
29486                                     html : captionhtml
29487                                 }
29488                                 
29489                             ]
29490                         }
29491                         
29492                     ]
29493                     
29494                 }
29495             ]
29496         };
29497         return ret;
29498          
29499     },
29500     
29501     readElement : function(node)
29502     {
29503         // this should not really come from the link...
29504         this.video_url = this.getVal(node, 'div', 'src');
29505         this.cls = this.getVal(node, 'div', 'class');
29506         this.href = this.getVal(node, 'a', 'href');
29507         
29508         
29509         this.image_src = this.getVal(node, 'img', 'src');
29510          
29511         this.align = this.getVal(node, 'figure', 'align');
29512         
29513         /// not really used - as hidden captions do not store the content here..
29514         var figcaption = this.getVal(node, 'figcaption', false);
29515         if (figcaption !== '') {
29516             this.caption = this.getVal(figcaption, 'i', 'html');
29517         }
29518         
29519
29520         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29521         var dc = this.getVal(node, true, 'data-caption');
29522         if (dc && dc.length) {
29523             this.caption = dc;
29524         }
29525         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29526         this.width = this.getVal(node, true, 'data-width');
29527         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29528         
29529     },
29530     removeNode : function()
29531     {
29532         return this.node;
29533     }
29534     
29535   
29536    
29537      
29538     
29539     
29540     
29541     
29542 })
29543
29544  
29545
29546 /**
29547  * @class Roo.htmleditor.BlockTable
29548  * Block that manages a table
29549  * 
29550  * @constructor
29551  * Create a new Filter.
29552  * @param {Object} config Configuration options
29553  */
29554
29555 Roo.htmleditor.BlockTable = function(cfg)
29556 {
29557     if (cfg.node) {
29558         this.readElement(cfg.node);
29559         this.updateElement(cfg.node);
29560     }
29561     Roo.apply(this, cfg);
29562     if (!cfg.node) {
29563         this.rows = [];
29564         for(var r = 0; r < this.no_row; r++) {
29565             this.rows[r] = [];
29566             for(var c = 0; c < this.no_col; c++) {
29567                 this.rows[r][c] = this.emptyCell();
29568             }
29569         }
29570     }
29571     
29572     
29573 }
29574 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29575  
29576     rows : false,
29577     no_col : 1,
29578     no_row : 1,
29579     
29580     
29581     width: '100%',
29582     
29583     // used by context menu
29584     friendly_name : 'Table',
29585     deleteTitle : 'Delete Table',
29586     // context menu is drawn once..
29587     
29588     contextMenu : function(toolbar)
29589     {
29590         
29591         var block = function() {
29592             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29593         };
29594         
29595         
29596         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29597         
29598         var syncValue = toolbar.editorcore.syncValue;
29599         
29600         var fields = {};
29601         
29602         return [
29603             {
29604                 xtype : 'TextItem',
29605                 text : "Width: ",
29606                 xns : rooui.Toolbar  //Boostrap?
29607             },
29608             {
29609                 xtype : 'ComboBox',
29610                 allowBlank : false,
29611                 displayField : 'val',
29612                 editable : true,
29613                 listWidth : 100,
29614                 triggerAction : 'all',
29615                 typeAhead : true,
29616                 valueField : 'val',
29617                 width : 100,
29618                 name : 'width',
29619                 listeners : {
29620                     select : function (combo, r, index)
29621                     {
29622                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29623                         var b = block();
29624                         b.width = r.get('val');
29625                         b.updateElement();
29626                         syncValue();
29627                         toolbar.editorcore.onEditorEvent();
29628                     }
29629                 },
29630                 xns : rooui.form,
29631                 store : {
29632                     xtype : 'SimpleStore',
29633                     data : [
29634                         ['100%'],
29635                         ['auto']
29636                     ],
29637                     fields : [ 'val'],
29638                     xns : Roo.data
29639                 }
29640             },
29641             // -------- Cols
29642             
29643             {
29644                 xtype : 'TextItem',
29645                 text : "Columns: ",
29646                 xns : rooui.Toolbar  //Boostrap?
29647             },
29648          
29649             {
29650                 xtype : 'Button',
29651                 text: '-',
29652                 listeners : {
29653                     click : function (_self, e)
29654                     {
29655                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29656                         block().removeColumn();
29657                         syncValue();
29658                         toolbar.editorcore.onEditorEvent();
29659                     }
29660                 },
29661                 xns : rooui.Toolbar
29662             },
29663             {
29664                 xtype : 'Button',
29665                 text: '+',
29666                 listeners : {
29667                     click : function (_self, e)
29668                     {
29669                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29670                         block().addColumn();
29671                         syncValue();
29672                         toolbar.editorcore.onEditorEvent();
29673                     }
29674                 },
29675                 xns : rooui.Toolbar
29676             },
29677             // -------- ROWS
29678             {
29679                 xtype : 'TextItem',
29680                 text : "Rows: ",
29681                 xns : rooui.Toolbar  //Boostrap?
29682             },
29683          
29684             {
29685                 xtype : 'Button',
29686                 text: '-',
29687                 listeners : {
29688                     click : function (_self, e)
29689                     {
29690                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29691                         block().removeRow();
29692                         syncValue();
29693                         toolbar.editorcore.onEditorEvent();
29694                     }
29695                 },
29696                 xns : rooui.Toolbar
29697             },
29698             {
29699                 xtype : 'Button',
29700                 text: '+',
29701                 listeners : {
29702                     click : function (_self, e)
29703                     {
29704                         block().addRow();
29705                         syncValue();
29706                         toolbar.editorcore.onEditorEvent();
29707                     }
29708                 },
29709                 xns : rooui.Toolbar
29710             },
29711             // -------- ROWS
29712             {
29713                 xtype : 'Button',
29714                 text: 'Reset Column Widths',
29715                 listeners : {
29716                     
29717                     click : function (_self, e)
29718                     {
29719                         block().resetWidths();
29720                         syncValue();
29721                         toolbar.editorcore.onEditorEvent();
29722                     }
29723                 },
29724                 xns : rooui.Toolbar
29725             } 
29726             
29727             
29728             
29729         ];
29730         
29731     },
29732     
29733     
29734   /**
29735      * create a DomHelper friendly object - for use with
29736      * Roo.DomHelper.markup / overwrite / etc..
29737      * ?? should it be called with option to hide all editing features?
29738      */
29739     toObject : function()
29740     {
29741         
29742         var ret = {
29743             tag : 'table',
29744             contenteditable : 'false', // this stops cell selection from picking the table.
29745             'data-block' : 'Table',
29746             style : {
29747                 width:  this.width,
29748                 border : 'solid 1px #000', // ??? hard coded?
29749                 'border-collapse' : 'collapse' 
29750             },
29751             cn : [
29752                 { tag : 'tbody' , cn : [] }
29753             ]
29754         };
29755         
29756         // do we have a head = not really 
29757         var ncols = 0;
29758         Roo.each(this.rows, function( row ) {
29759             var tr = {
29760                 tag: 'tr',
29761                 style : {
29762                     margin: '6px',
29763                     border : 'solid 1px #000',
29764                     textAlign : 'left' 
29765                 },
29766                 cn : [ ]
29767             };
29768             
29769             ret.cn[0].cn.push(tr);
29770             // does the row have any properties? ?? height?
29771             var nc = 0;
29772             Roo.each(row, function( cell ) {
29773                 
29774                 var td = {
29775                     tag : 'td',
29776                     contenteditable :  'true',
29777                     'data-block' : 'Td',
29778                     html : cell.html,
29779                     style : cell.style
29780                 };
29781                 if (cell.colspan > 1) {
29782                     td.colspan = cell.colspan ;
29783                     nc += cell.colspan;
29784                 } else {
29785                     nc++;
29786                 }
29787                 if (cell.rowspan > 1) {
29788                     td.rowspan = cell.rowspan ;
29789                 }
29790                 
29791                 
29792                 // widths ?
29793                 tr.cn.push(td);
29794                     
29795                 
29796             }, this);
29797             ncols = Math.max(nc, ncols);
29798             
29799             
29800         }, this);
29801         // add the header row..
29802         
29803         ncols++;
29804          
29805         
29806         return ret;
29807          
29808     },
29809     
29810     readElement : function(node)
29811     {
29812         node  = node ? node : this.node ;
29813         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29814         
29815         this.rows = [];
29816         this.no_row = 0;
29817         var trs = Array.from(node.rows);
29818         trs.forEach(function(tr) {
29819             var row =  [];
29820             this.rows.push(row);
29821             
29822             this.no_row++;
29823             var no_column = 0;
29824             Array.from(tr.cells).forEach(function(td) {
29825                 
29826                 var add = {
29827                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29828                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29829                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29830                     html : td.innerHTML
29831                 };
29832                 no_column += add.colspan;
29833                      
29834                 
29835                 row.push(add);
29836                 
29837                 
29838             },this);
29839             this.no_col = Math.max(this.no_col, no_column);
29840             
29841             
29842         },this);
29843         
29844         
29845     },
29846     normalizeRows: function()
29847     {
29848         var ret= [];
29849         var rid = -1;
29850         this.rows.forEach(function(row) {
29851             rid++;
29852             ret[rid] = [];
29853             row = this.normalizeRow(row);
29854             var cid = 0;
29855             row.forEach(function(c) {
29856                 while (typeof(ret[rid][cid]) != 'undefined') {
29857                     cid++;
29858                 }
29859                 if (typeof(ret[rid]) == 'undefined') {
29860                     ret[rid] = [];
29861                 }
29862                 ret[rid][cid] = c;
29863                 c.row = rid;
29864                 c.col = cid;
29865                 if (c.rowspan < 2) {
29866                     return;
29867                 }
29868                 
29869                 for(var i = 1 ;i < c.rowspan; i++) {
29870                     if (typeof(ret[rid+i]) == 'undefined') {
29871                         ret[rid+i] = [];
29872                     }
29873                     ret[rid+i][cid] = c;
29874                 }
29875             });
29876         }, this);
29877         return ret;
29878     
29879     },
29880     
29881     normalizeRow: function(row)
29882     {
29883         var ret= [];
29884         row.forEach(function(c) {
29885             if (c.colspan < 2) {
29886                 ret.push(c);
29887                 return;
29888             }
29889             for(var i =0 ;i < c.colspan; i++) {
29890                 ret.push(c);
29891             }
29892         });
29893         return ret;
29894     
29895     },
29896     
29897     deleteColumn : function(sel)
29898     {
29899         if (!sel || sel.type != 'col') {
29900             return;
29901         }
29902         if (this.no_col < 2) {
29903             return;
29904         }
29905         
29906         this.rows.forEach(function(row) {
29907             var cols = this.normalizeRow(row);
29908             var col = cols[sel.col];
29909             if (col.colspan > 1) {
29910                 col.colspan --;
29911             } else {
29912                 row.remove(col);
29913             }
29914             
29915         }, this);
29916         this.no_col--;
29917         
29918     },
29919     removeColumn : function()
29920     {
29921         this.deleteColumn({
29922             type: 'col',
29923             col : this.no_col-1
29924         });
29925         this.updateElement();
29926     },
29927     
29928      
29929     addColumn : function()
29930     {
29931         
29932         this.rows.forEach(function(row) {
29933             row.push(this.emptyCell());
29934            
29935         }, this);
29936         this.updateElement();
29937     },
29938     
29939     deleteRow : function(sel)
29940     {
29941         if (!sel || sel.type != 'row') {
29942             return;
29943         }
29944         
29945         if (this.no_row < 2) {
29946             return;
29947         }
29948         
29949         var rows = this.normalizeRows();
29950         
29951         
29952         rows[sel.row].forEach(function(col) {
29953             if (col.rowspan > 1) {
29954                 col.rowspan--;
29955             } else {
29956                 col.remove = 1; // flage it as removed.
29957             }
29958             
29959         }, this);
29960         var newrows = [];
29961         this.rows.forEach(function(row) {
29962             newrow = [];
29963             row.forEach(function(c) {
29964                 if (typeof(c.remove) == 'undefined') {
29965                     newrow.push(c);
29966                 }
29967                 
29968             });
29969             if (newrow.length > 0) {
29970                 newrows.push(row);
29971             }
29972         });
29973         this.rows =  newrows;
29974         
29975         
29976         
29977         this.no_row--;
29978         this.updateElement();
29979         
29980     },
29981     removeRow : function()
29982     {
29983         this.deleteRow({
29984             type: 'row',
29985             row : this.no_row-1
29986         });
29987         
29988     },
29989     
29990      
29991     addRow : function()
29992     {
29993         
29994         var row = [];
29995         for (var i = 0; i < this.no_col; i++ ) {
29996             
29997             row.push(this.emptyCell());
29998            
29999         }
30000         this.rows.push(row);
30001         this.updateElement();
30002         
30003     },
30004      
30005     // the default cell object... at present...
30006     emptyCell : function() {
30007         return (new Roo.htmleditor.BlockTd({})).toObject();
30008         
30009      
30010     },
30011     
30012     removeNode : function()
30013     {
30014         return this.node;
30015     },
30016     
30017     
30018     
30019     resetWidths : function()
30020     {
30021         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30022             var nn = Roo.htmleditor.Block.factory(n);
30023             nn.width = '';
30024             nn.updateElement(n);
30025         });
30026     }
30027     
30028     
30029     
30030     
30031 })
30032
30033 /**
30034  *
30035  * editing a TD?
30036  *
30037  * since selections really work on the table cell, then editing really should work from there
30038  *
30039  * The original plan was to support merging etc... - but that may not be needed yet..
30040  *
30041  * So this simple version will support:
30042  *   add/remove cols
30043  *   adjust the width +/-
30044  *   reset the width...
30045  *   
30046  *
30047  */
30048
30049
30050  
30051
30052 /**
30053  * @class Roo.htmleditor.BlockTable
30054  * Block that manages a table
30055  * 
30056  * @constructor
30057  * Create a new Filter.
30058  * @param {Object} config Configuration options
30059  */
30060
30061 Roo.htmleditor.BlockTd = function(cfg)
30062 {
30063     if (cfg.node) {
30064         this.readElement(cfg.node);
30065         this.updateElement(cfg.node);
30066     }
30067     Roo.apply(this, cfg);
30068      
30069     
30070     
30071 }
30072 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30073  
30074     node : false,
30075     
30076     width: '',
30077     textAlign : 'left',
30078     valign : 'top',
30079     
30080     colspan : 1,
30081     rowspan : 1,
30082     
30083     
30084     // used by context menu
30085     friendly_name : 'Table Cell',
30086     deleteTitle : false, // use our customer delete
30087     
30088     // context menu is drawn once..
30089     
30090     contextMenu : function(toolbar)
30091     {
30092         
30093         var cell = function() {
30094             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30095         };
30096         
30097         var table = function() {
30098             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30099         };
30100         
30101         var lr = false;
30102         var saveSel = function()
30103         {
30104             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30105         }
30106         var restoreSel = function()
30107         {
30108             if (lr) {
30109                 (function() {
30110                     toolbar.editorcore.focus();
30111                     var cr = toolbar.editorcore.getSelection();
30112                     cr.removeAllRanges();
30113                     cr.addRange(lr);
30114                     toolbar.editorcore.onEditorEvent();
30115                 }).defer(10, this);
30116                 
30117                 
30118             }
30119         }
30120         
30121         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30122         
30123         var syncValue = toolbar.editorcore.syncValue;
30124         
30125         var fields = {};
30126         
30127         return [
30128             {
30129                 xtype : 'Button',
30130                 text : 'Edit Table',
30131                 listeners : {
30132                     click : function() {
30133                         var t = toolbar.tb.selectedNode.closest('table');
30134                         toolbar.editorcore.selectNode(t);
30135                         toolbar.editorcore.onEditorEvent();                        
30136                     }
30137                 }
30138                 
30139             },
30140               
30141            
30142              
30143             {
30144                 xtype : 'TextItem',
30145                 text : "Column Width: ",
30146                  xns : rooui.Toolbar 
30147                
30148             },
30149             {
30150                 xtype : 'Button',
30151                 text: '-',
30152                 listeners : {
30153                     click : function (_self, e)
30154                     {
30155                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30156                         cell().shrinkColumn();
30157                         syncValue();
30158                          toolbar.editorcore.onEditorEvent();
30159                     }
30160                 },
30161                 xns : rooui.Toolbar
30162             },
30163             {
30164                 xtype : 'Button',
30165                 text: '+',
30166                 listeners : {
30167                     click : function (_self, e)
30168                     {
30169                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30170                         cell().growColumn();
30171                         syncValue();
30172                         toolbar.editorcore.onEditorEvent();
30173                     }
30174                 },
30175                 xns : rooui.Toolbar
30176             },
30177             
30178             {
30179                 xtype : 'TextItem',
30180                 text : "Vertical Align: ",
30181                 xns : rooui.Toolbar  //Boostrap?
30182             },
30183             {
30184                 xtype : 'ComboBox',
30185                 allowBlank : false,
30186                 displayField : 'val',
30187                 editable : true,
30188                 listWidth : 100,
30189                 triggerAction : 'all',
30190                 typeAhead : true,
30191                 valueField : 'val',
30192                 width : 100,
30193                 name : 'valign',
30194                 listeners : {
30195                     select : function (combo, r, index)
30196                     {
30197                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30198                         var b = cell();
30199                         b.valign = r.get('val');
30200                         b.updateElement();
30201                         syncValue();
30202                         toolbar.editorcore.onEditorEvent();
30203                     }
30204                 },
30205                 xns : rooui.form,
30206                 store : {
30207                     xtype : 'SimpleStore',
30208                     data : [
30209                         ['top'],
30210                         ['middle'],
30211                         ['bottom'] // there are afew more... 
30212                     ],
30213                     fields : [ 'val'],
30214                     xns : Roo.data
30215                 }
30216             },
30217             
30218             {
30219                 xtype : 'TextItem',
30220                 text : "Merge Cells: ",
30221                  xns : rooui.Toolbar 
30222                
30223             },
30224             
30225             
30226             {
30227                 xtype : 'Button',
30228                 text: 'Right',
30229                 listeners : {
30230                     click : function (_self, e)
30231                     {
30232                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30233                         cell().mergeRight();
30234                         //block().growColumn();
30235                         syncValue();
30236                         toolbar.editorcore.onEditorEvent();
30237                     }
30238                 },
30239                 xns : rooui.Toolbar
30240             },
30241              
30242             {
30243                 xtype : 'Button',
30244                 text: 'Below',
30245                 listeners : {
30246                     click : function (_self, e)
30247                     {
30248                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30249                         cell().mergeBelow();
30250                         //block().growColumn();
30251                         syncValue();
30252                         toolbar.editorcore.onEditorEvent();
30253                     }
30254                 },
30255                 xns : rooui.Toolbar
30256             },
30257             {
30258                 xtype : 'TextItem',
30259                 text : "| ",
30260                  xns : rooui.Toolbar 
30261                
30262             },
30263             
30264             {
30265                 xtype : 'Button',
30266                 text: 'Split',
30267                 listeners : {
30268                     click : function (_self, e)
30269                     {
30270                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30271                         cell().split();
30272                         syncValue();
30273                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30274                         toolbar.editorcore.onEditorEvent();
30275                                              
30276                     }
30277                 },
30278                 xns : rooui.Toolbar
30279             },
30280             {
30281                 xtype : 'Fill',
30282                 xns : rooui.Toolbar 
30283                
30284             },
30285         
30286           
30287             {
30288                 xtype : 'Button',
30289                 text: 'Delete',
30290                  
30291                 xns : rooui.Toolbar,
30292                 menu : {
30293                     xtype : 'Menu',
30294                     xns : rooui.menu,
30295                     items : [
30296                         {
30297                             xtype : 'Item',
30298                             html: 'Column',
30299                             listeners : {
30300                                 click : function (_self, e)
30301                                 {
30302                                     var t = table();
30303                                     
30304                                     cell().deleteColumn();
30305                                     syncValue();
30306                                     toolbar.editorcore.selectNode(t.node);
30307                                     toolbar.editorcore.onEditorEvent();   
30308                                 }
30309                             },
30310                             xns : rooui.menu
30311                         },
30312                         {
30313                             xtype : 'Item',
30314                             html: 'Row',
30315                             listeners : {
30316                                 click : function (_self, e)
30317                                 {
30318                                     var t = table();
30319                                     cell().deleteRow();
30320                                     syncValue();
30321                                     
30322                                     toolbar.editorcore.selectNode(t.node);
30323                                     toolbar.editorcore.onEditorEvent();   
30324                                                          
30325                                 }
30326                             },
30327                             xns : rooui.menu
30328                         },
30329                        {
30330                             xtype : 'Separator',
30331                             xns : rooui.menu
30332                         },
30333                         {
30334                             xtype : 'Item',
30335                             html: 'Table',
30336                             listeners : {
30337                                 click : function (_self, e)
30338                                 {
30339                                     var t = table();
30340                                     var nn = t.node.nextSibling || t.node.previousSibling;
30341                                     t.node.parentNode.removeChild(t.node);
30342                                     if (nn) { 
30343                                         toolbar.editorcore.selectNode(nn, true);
30344                                     }
30345                                     toolbar.editorcore.onEditorEvent();   
30346                                                          
30347                                 }
30348                             },
30349                             xns : rooui.menu
30350                         }
30351                     ]
30352                 }
30353             }
30354             
30355             // align... << fixme
30356             
30357         ];
30358         
30359     },
30360     
30361     
30362   /**
30363      * create a DomHelper friendly object - for use with
30364      * Roo.DomHelper.markup / overwrite / etc..
30365      * ?? should it be called with option to hide all editing features?
30366      */
30367  /**
30368      * create a DomHelper friendly object - for use with
30369      * Roo.DomHelper.markup / overwrite / etc..
30370      * ?? should it be called with option to hide all editing features?
30371      */
30372     toObject : function()
30373     {
30374         var ret = {
30375             tag : 'td',
30376             contenteditable : 'true', // this stops cell selection from picking the table.
30377             'data-block' : 'Td',
30378             valign : this.valign,
30379             style : {  
30380                 'text-align' :  this.textAlign,
30381                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30382                 'border-collapse' : 'collapse',
30383                 padding : '6px', // 8 for desktop / 4 for mobile
30384                 'vertical-align': this.valign
30385             },
30386             html : this.html
30387         };
30388         if (this.width != '') {
30389             ret.width = this.width;
30390             ret.style.width = this.width;
30391         }
30392         
30393         
30394         if (this.colspan > 1) {
30395             ret.colspan = this.colspan ;
30396         } 
30397         if (this.rowspan > 1) {
30398             ret.rowspan = this.rowspan ;
30399         }
30400         
30401            
30402         
30403         return ret;
30404          
30405     },
30406     
30407     readElement : function(node)
30408     {
30409         node  = node ? node : this.node ;
30410         this.width = node.style.width;
30411         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30412         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30413         this.html = node.innerHTML;
30414         if (node.style.textAlign != '') {
30415             this.textAlign = node.style.textAlign;
30416         }
30417         
30418         
30419     },
30420      
30421     // the default cell object... at present...
30422     emptyCell : function() {
30423         return {
30424             colspan :  1,
30425             rowspan :  1,
30426             textAlign : 'left',
30427             html : "&nbsp;" // is this going to be editable now?
30428         };
30429      
30430     },
30431     
30432     removeNode : function()
30433     {
30434         return this.node.closest('table');
30435          
30436     },
30437     
30438     cellData : false,
30439     
30440     colWidths : false,
30441     
30442     toTableArray  : function()
30443     {
30444         var ret = [];
30445         var tab = this.node.closest('tr').closest('table');
30446         Array.from(tab.rows).forEach(function(r, ri){
30447             ret[ri] = [];
30448         });
30449         var rn = 0;
30450         this.colWidths = [];
30451         var all_auto = true;
30452         Array.from(tab.rows).forEach(function(r, ri){
30453             
30454             var cn = 0;
30455             Array.from(r.cells).forEach(function(ce, ci){
30456                 var c =  {
30457                     cell : ce,
30458                     row : rn,
30459                     col: cn,
30460                     colspan : ce.colSpan,
30461                     rowspan : ce.rowSpan
30462                 };
30463                 if (ce.isEqualNode(this.node)) {
30464                     this.cellData = c;
30465                 }
30466                 // if we have been filled up by a row?
30467                 if (typeof(ret[rn][cn]) != 'undefined') {
30468                     while(typeof(ret[rn][cn]) != 'undefined') {
30469                         cn++;
30470                     }
30471                     c.col = cn;
30472                 }
30473                 
30474                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30475                     this.colWidths[cn] =   ce.style.width;
30476                     if (this.colWidths[cn] != '') {
30477                         all_auto = false;
30478                     }
30479                 }
30480                 
30481                 
30482                 if (c.colspan < 2 && c.rowspan < 2 ) {
30483                     ret[rn][cn] = c;
30484                     cn++;
30485                     return;
30486                 }
30487                 for(var j = 0; j < c.rowspan; j++) {
30488                     if (typeof(ret[rn+j]) == 'undefined') {
30489                         continue; // we have a problem..
30490                     }
30491                     ret[rn+j][cn] = c;
30492                     for(var i = 0; i < c.colspan; i++) {
30493                         ret[rn+j][cn+i] = c;
30494                     }
30495                 }
30496                 
30497                 cn += c.colspan;
30498             }, this);
30499             rn++;
30500         }, this);
30501         
30502         // initalize widths.?
30503         // either all widths or no widths..
30504         if (all_auto) {
30505             this.colWidths[0] = false; // no widths flag.
30506         }
30507         
30508         
30509         return ret;
30510         
30511     },
30512     
30513     
30514     
30515     
30516     mergeRight: function()
30517     {
30518          
30519         // get the contents of the next cell along..
30520         var tr = this.node.closest('tr');
30521         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30522         if (i >= tr.childNodes.length - 1) {
30523             return; // no cells on right to merge with.
30524         }
30525         var table = this.toTableArray();
30526         
30527         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30528             return; // nothing right?
30529         }
30530         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30531         // right cell - must be same rowspan and on the same row.
30532         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30533             return; // right hand side is not same rowspan.
30534         }
30535         
30536         
30537         
30538         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30539         tr.removeChild(rc.cell);
30540         this.colspan += rc.colspan;
30541         this.node.setAttribute('colspan', this.colspan);
30542
30543         var table = this.toTableArray();
30544         this.normalizeWidths(table);
30545         this.updateWidths(table);
30546     },
30547     
30548     
30549     mergeBelow : function()
30550     {
30551         var table = this.toTableArray();
30552         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30553             return; // no row below
30554         }
30555         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30556             return; // nothing right?
30557         }
30558         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30559         
30560         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30561             return; // right hand side is not same rowspan.
30562         }
30563         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30564         rc.cell.parentNode.removeChild(rc.cell);
30565         this.rowspan += rc.rowspan;
30566         this.node.setAttribute('rowspan', this.rowspan);
30567     },
30568     
30569     split: function()
30570     {
30571         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30572             return;
30573         }
30574         var table = this.toTableArray();
30575         var cd = this.cellData;
30576         this.rowspan = 1;
30577         this.colspan = 1;
30578         
30579         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30580              
30581             
30582             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30583                 if (r == cd.row && c == cd.col) {
30584                     this.node.removeAttribute('rowspan');
30585                     this.node.removeAttribute('colspan');
30586                 }
30587                  
30588                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30589                 ntd.removeAttribute('id'); 
30590                 ntd.style.width  = this.colWidths[c];
30591                 ntd.innerHTML = '';
30592                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30593             }
30594             
30595         }
30596         this.redrawAllCells(table);
30597         
30598     },
30599     
30600     
30601     
30602     redrawAllCells: function(table)
30603     {
30604         
30605          
30606         var tab = this.node.closest('tr').closest('table');
30607         var ctr = tab.rows[0].parentNode;
30608         Array.from(tab.rows).forEach(function(r, ri){
30609             
30610             Array.from(r.cells).forEach(function(ce, ci){
30611                 ce.parentNode.removeChild(ce);
30612             });
30613             r.parentNode.removeChild(r);
30614         });
30615         for(var r = 0 ; r < table.length; r++) {
30616             var re = tab.rows[r];
30617             
30618             var re = tab.ownerDocument.createElement('tr');
30619             ctr.appendChild(re);
30620             for(var c = 0 ; c < table[r].length; c++) {
30621                 if (table[r][c].cell === false) {
30622                     continue;
30623                 }
30624                 
30625                 re.appendChild(table[r][c].cell);
30626                  
30627                 table[r][c].cell = false;
30628             }
30629         }
30630         
30631     },
30632     updateWidths : function(table)
30633     {
30634         for(var r = 0 ; r < table.length; r++) {
30635            
30636             for(var c = 0 ; c < table[r].length; c++) {
30637                 if (table[r][c].cell === false) {
30638                     continue;
30639                 }
30640                 
30641                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30642                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30643                     el.width = Math.floor(this.colWidths[c])  +'%';
30644                     el.updateElement(el.node);
30645                 }
30646                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30647                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30648                     var width = 0;
30649                     for(var i = 0; i < table[r][c].colspan; i ++) {
30650                         width += Math.floor(this.colWidths[c + i]);
30651                     }
30652                     el.width = width  +'%';
30653                     el.updateElement(el.node);
30654                 }
30655                 table[r][c].cell = false; // done
30656             }
30657         }
30658     },
30659     normalizeWidths : function(table)
30660     {
30661         if (this.colWidths[0] === false) {
30662             var nw = 100.0 / this.colWidths.length;
30663             this.colWidths.forEach(function(w,i) {
30664                 this.colWidths[i] = nw;
30665             },this);
30666             return;
30667         }
30668     
30669         var t = 0, missing = [];
30670         
30671         this.colWidths.forEach(function(w,i) {
30672             //if you mix % and
30673             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30674             var add =  this.colWidths[i];
30675             if (add > 0) {
30676                 t+=add;
30677                 return;
30678             }
30679             missing.push(i);
30680             
30681             
30682         },this);
30683         var nc = this.colWidths.length;
30684         if (missing.length) {
30685             var mult = (nc - missing.length) / (1.0 * nc);
30686             var t = mult * t;
30687             var ew = (100 -t) / (1.0 * missing.length);
30688             this.colWidths.forEach(function(w,i) {
30689                 if (w > 0) {
30690                     this.colWidths[i] = w * mult;
30691                     return;
30692                 }
30693                 
30694                 this.colWidths[i] = ew;
30695             }, this);
30696             // have to make up numbers..
30697              
30698         }
30699         // now we should have all the widths..
30700         
30701     
30702     },
30703     
30704     shrinkColumn : function()
30705     {
30706         var table = this.toTableArray();
30707         this.normalizeWidths(table);
30708         var col = this.cellData.col;
30709         var nw = this.colWidths[col] * 0.8;
30710         if (nw < 5) {
30711             return;
30712         }
30713         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30714         this.colWidths.forEach(function(w,i) {
30715             if (i == col) {
30716                  this.colWidths[i] = nw;
30717                 return;
30718             }
30719             this.colWidths[i] += otherAdd
30720         }, this);
30721         this.updateWidths(table);
30722          
30723     },
30724     growColumn : function()
30725     {
30726         var table = this.toTableArray();
30727         this.normalizeWidths(table);
30728         var col = this.cellData.col;
30729         var nw = this.colWidths[col] * 1.2;
30730         if (nw > 90) {
30731             return;
30732         }
30733         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30734         this.colWidths.forEach(function(w,i) {
30735             if (i == col) {
30736                 this.colWidths[i] = nw;
30737                 return;
30738             }
30739             this.colWidths[i] -= otherSub
30740         }, this);
30741         this.updateWidths(table);
30742          
30743     },
30744     deleteRow : function()
30745     {
30746         // delete this rows 'tr'
30747         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30748         // then reduce the rowspan.
30749         var table = this.toTableArray();
30750         // this.cellData.row;
30751         for (var i =0;i< table[this.cellData.row].length ; i++) {
30752             var c = table[this.cellData.row][i];
30753             if (c.row != this.cellData.row) {
30754                 
30755                 c.rowspan--;
30756                 c.cell.setAttribute('rowspan', c.rowspan);
30757                 continue;
30758             }
30759             if (c.rowspan > 1) {
30760                 c.rowspan--;
30761                 c.cell.setAttribute('rowspan', c.rowspan);
30762             }
30763         }
30764         table.splice(this.cellData.row,1);
30765         this.redrawAllCells(table);
30766         
30767     },
30768     deleteColumn : function()
30769     {
30770         var table = this.toTableArray();
30771         
30772         for (var i =0;i< table.length ; i++) {
30773             var c = table[i][this.cellData.col];
30774             if (c.col != this.cellData.col) {
30775                 table[i][this.cellData.col].colspan--;
30776             } else if (c.colspan > 1) {
30777                 c.colspan--;
30778                 c.cell.setAttribute('colspan', c.colspan);
30779             }
30780             table[i].splice(this.cellData.col,1);
30781         }
30782         
30783         this.redrawAllCells(table);
30784     }
30785     
30786     
30787     
30788     
30789 })
30790
30791 //<script type="text/javascript">
30792
30793 /*
30794  * Based  Ext JS Library 1.1.1
30795  * Copyright(c) 2006-2007, Ext JS, LLC.
30796  * LGPL
30797  *
30798  */
30799  
30800 /**
30801  * @class Roo.HtmlEditorCore
30802  * @extends Roo.Component
30803  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30804  *
30805  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30806  */
30807
30808 Roo.HtmlEditorCore = function(config){
30809     
30810     
30811     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30812     
30813     
30814     this.addEvents({
30815         /**
30816          * @event initialize
30817          * Fires when the editor is fully initialized (including the iframe)
30818          * @param {Roo.HtmlEditorCore} this
30819          */
30820         initialize: true,
30821         /**
30822          * @event activate
30823          * Fires when the editor is first receives the focus. Any insertion must wait
30824          * until after this event.
30825          * @param {Roo.HtmlEditorCore} this
30826          */
30827         activate: true,
30828          /**
30829          * @event beforesync
30830          * Fires before the textarea is updated with content from the editor iframe. Return false
30831          * to cancel the sync.
30832          * @param {Roo.HtmlEditorCore} this
30833          * @param {String} html
30834          */
30835         beforesync: true,
30836          /**
30837          * @event beforepush
30838          * Fires before the iframe editor is updated with content from the textarea. Return false
30839          * to cancel the push.
30840          * @param {Roo.HtmlEditorCore} this
30841          * @param {String} html
30842          */
30843         beforepush: true,
30844          /**
30845          * @event sync
30846          * Fires when the textarea is updated with content from the editor iframe.
30847          * @param {Roo.HtmlEditorCore} this
30848          * @param {String} html
30849          */
30850         sync: true,
30851          /**
30852          * @event push
30853          * Fires when the iframe editor is updated with content from the textarea.
30854          * @param {Roo.HtmlEditorCore} this
30855          * @param {String} html
30856          */
30857         push: true,
30858         
30859         /**
30860          * @event editorevent
30861          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30862          * @param {Roo.HtmlEditorCore} this
30863          */
30864         editorevent: true 
30865         
30866         
30867     });
30868     
30869     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30870     
30871     // defaults : white / black...
30872     this.applyBlacklists();
30873     
30874     
30875     
30876 };
30877
30878
30879 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30880
30881
30882      /**
30883      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30884      */
30885     
30886     owner : false,
30887     
30888      /**
30889      * @cfg {String} css styling for resizing. (used on bootstrap only)
30890      */
30891     resize : false,
30892      /**
30893      * @cfg {Number} height (in pixels)
30894      */   
30895     height: 300,
30896    /**
30897      * @cfg {Number} width (in pixels)
30898      */   
30899     width: 500,
30900      /**
30901      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30902      *         if you are doing an email editor, this probably needs disabling, it's designed
30903      */
30904     autoClean: true,
30905     
30906     /**
30907      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30908      */
30909     enableBlocks : true,
30910     /**
30911      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30912      * 
30913      */
30914     stylesheets: false,
30915      /**
30916      * @cfg {String} language default en - language of text (usefull for rtl languages)
30917      * 
30918      */
30919     language: 'en',
30920     
30921     /**
30922      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30923      *          - by default they are stripped - if you are editing email you may need this.
30924      */
30925     allowComments: false,
30926     // id of frame..
30927     frameId: false,
30928     
30929     // private properties
30930     validationEvent : false,
30931     deferHeight: true,
30932     initialized : false,
30933     activated : false,
30934     sourceEditMode : false,
30935     onFocus : Roo.emptyFn,
30936     iframePad:3,
30937     hideMode:'offsets',
30938     
30939     clearUp: true,
30940     
30941     // blacklist + whitelisted elements..
30942     black: false,
30943     white: false,
30944      
30945     bodyCls : '',
30946
30947     
30948     undoManager : false,
30949     /**
30950      * Protected method that will not generally be called directly. It
30951      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30952      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30953      */
30954     getDocMarkup : function(){
30955         // body styles..
30956         var st = '';
30957         
30958         // inherit styels from page...?? 
30959         if (this.stylesheets === false) {
30960             
30961             Roo.get(document.head).select('style').each(function(node) {
30962                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30963             });
30964             
30965             Roo.get(document.head).select('link').each(function(node) { 
30966                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30967             });
30968             
30969         } else if (!this.stylesheets.length) {
30970                 // simple..
30971                 st = '<style type="text/css">' +
30972                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30973                    '</style>';
30974         } else {
30975             for (var i in this.stylesheets) {
30976                 if (typeof(this.stylesheets[i]) != 'string') {
30977                     continue;
30978                 }
30979                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30980             }
30981             
30982         }
30983         
30984         st +=  '<style type="text/css">' +
30985             'IMG { cursor: pointer } ' +
30986         '</style>';
30987         
30988         st += '<meta name="google" content="notranslate">';
30989         
30990         var cls = 'notranslate roo-htmleditor-body';
30991         
30992         if(this.bodyCls.length){
30993             cls += ' ' + this.bodyCls;
30994         }
30995         
30996         return '<html  class="notranslate" translate="no"><head>' + st  +
30997             //<style type="text/css">' +
30998             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30999             //'</style>' +
31000             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
31001     },
31002
31003     // private
31004     onRender : function(ct, position)
31005     {
31006         var _t = this;
31007         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31008         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31009         
31010         
31011         this.el.dom.style.border = '0 none';
31012         this.el.dom.setAttribute('tabIndex', -1);
31013         this.el.addClass('x-hidden hide');
31014         
31015         
31016         
31017         if(Roo.isIE){ // fix IE 1px bogus margin
31018             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31019         }
31020        
31021         
31022         this.frameId = Roo.id();
31023         
31024         var ifcfg = {
31025             tag: 'iframe',
31026             cls: 'form-control', // bootstrap..
31027             id: this.frameId,
31028             name: this.frameId,
31029             frameBorder : 'no',
31030             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31031         };
31032         if (this.resize) {
31033             ifcfg.style = { resize : this.resize };
31034         }
31035         
31036         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31037         
31038         
31039         this.iframe = iframe.dom;
31040
31041         this.assignDocWin();
31042         
31043         this.doc.designMode = 'on';
31044        
31045         this.doc.open();
31046         this.doc.write(this.getDocMarkup());
31047         this.doc.close();
31048
31049         
31050         var task = { // must defer to wait for browser to be ready
31051             run : function(){
31052                 //console.log("run task?" + this.doc.readyState);
31053                 this.assignDocWin();
31054                 if(this.doc.body || this.doc.readyState == 'complete'){
31055                     try {
31056                         this.doc.designMode="on";
31057                         
31058                     } catch (e) {
31059                         return;
31060                     }
31061                     Roo.TaskMgr.stop(task);
31062                     this.initEditor.defer(10, this);
31063                 }
31064             },
31065             interval : 10,
31066             duration: 10000,
31067             scope: this
31068         };
31069         Roo.TaskMgr.start(task);
31070
31071     },
31072
31073     // private
31074     onResize : function(w, h)
31075     {
31076          Roo.log('resize: ' +w + ',' + h );
31077         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31078         if(!this.iframe){
31079             return;
31080         }
31081         if(typeof w == 'number'){
31082             
31083             this.iframe.style.width = w + 'px';
31084         }
31085         if(typeof h == 'number'){
31086             
31087             this.iframe.style.height = h + 'px';
31088             if(this.doc){
31089                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31090             }
31091         }
31092         
31093     },
31094
31095     /**
31096      * Toggles the editor between standard and source edit mode.
31097      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31098      */
31099     toggleSourceEdit : function(sourceEditMode){
31100         
31101         this.sourceEditMode = sourceEditMode === true;
31102         
31103         if(this.sourceEditMode){
31104  
31105             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31106             
31107         }else{
31108             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31109             //this.iframe.className = '';
31110             this.deferFocus();
31111         }
31112         //this.setSize(this.owner.wrap.getSize());
31113         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31114     },
31115
31116     
31117   
31118
31119     /**
31120      * Protected method that will not generally be called directly. If you need/want
31121      * custom HTML cleanup, this is the method you should override.
31122      * @param {String} html The HTML to be cleaned
31123      * return {String} The cleaned HTML
31124      */
31125     cleanHtml : function(html)
31126     {
31127         html = String(html);
31128         if(html.length > 5){
31129             if(Roo.isSafari){ // strip safari nonsense
31130                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31131             }
31132         }
31133         if(html == '&nbsp;'){
31134             html = '';
31135         }
31136         return html;
31137     },
31138
31139     /**
31140      * HTML Editor -> Textarea
31141      * Protected method that will not generally be called directly. Syncs the contents
31142      * of the editor iframe with the textarea.
31143      */
31144     syncValue : function()
31145     {
31146         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31147         if(this.initialized){
31148             
31149             if (this.undoManager) {
31150                 this.undoManager.addEvent();
31151             }
31152
31153             
31154             var bd = (this.doc.body || this.doc.documentElement);
31155            
31156             
31157             var sel = this.win.getSelection();
31158             
31159             var div = document.createElement('div');
31160             div.innerHTML = bd.innerHTML;
31161             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31162             if (gtx.length > 0) {
31163                 var rm = gtx.item(0).parentNode;
31164                 rm.parentNode.removeChild(rm);
31165             }
31166             
31167            
31168             if (this.enableBlocks) {
31169                 new Roo.htmleditor.FilterBlock({ node : div });
31170             }
31171             
31172             var html = div.innerHTML;
31173             
31174             //?? tidy?
31175             if (this.autoClean) {
31176                 
31177                 new Roo.htmleditor.FilterAttributes({
31178                     node : div,
31179                     attrib_white : [
31180                             'href',
31181                             'src',
31182                             'name',
31183                             'align',
31184                             'colspan',
31185                             'rowspan',
31186                             'data-display',
31187                             'data-width',
31188                             'data-caption',
31189                             'start' ,
31190                             'style',
31191                             // youtube embed.
31192                             'class',
31193                             'allowfullscreen',
31194                             'frameborder',
31195                             'width',
31196                             'height',
31197                             'alt'
31198                             ],
31199                     attrib_clean : ['href', 'src' ] 
31200                 });
31201                 
31202                 var tidy = new Roo.htmleditor.TidySerializer({
31203                     inner:  true
31204                 });
31205                 html  = tidy.serialize(div);
31206                 
31207             }
31208             
31209             
31210             if(Roo.isSafari){
31211                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31212                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31213                 if(m && m[1]){
31214                     html = '<div style="'+m[0]+'">' + html + '</div>';
31215                 }
31216             }
31217             html = this.cleanHtml(html);
31218             // fix up the special chars.. normaly like back quotes in word...
31219             // however we do not want to do this with chinese..
31220             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31221                 
31222                 var cc = match.charCodeAt();
31223
31224                 // Get the character value, handling surrogate pairs
31225                 if (match.length == 2) {
31226                     // It's a surrogate pair, calculate the Unicode code point
31227                     var high = match.charCodeAt(0) - 0xD800;
31228                     var low  = match.charCodeAt(1) - 0xDC00;
31229                     cc = (high * 0x400) + low + 0x10000;
31230                 }  else if (
31231                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31232                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31233                     (cc >= 0xf900 && cc < 0xfb00 )
31234                 ) {
31235                         return match;
31236                 }  
31237          
31238                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31239                 return "&#" + cc + ";";
31240                 
31241                 
31242             });
31243             
31244             
31245              
31246             if(this.owner.fireEvent('beforesync', this, html) !== false){
31247                 this.el.dom.value = html;
31248                 this.owner.fireEvent('sync', this, html);
31249             }
31250         }
31251     },
31252
31253     /**
31254      * TEXTAREA -> EDITABLE
31255      * Protected method that will not generally be called directly. Pushes the value of the textarea
31256      * into the iframe editor.
31257      */
31258     pushValue : function()
31259     {
31260         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31261         if(this.initialized){
31262             var v = this.el.dom.value.trim();
31263             
31264             
31265             if(this.owner.fireEvent('beforepush', this, v) !== false){
31266                 var d = (this.doc.body || this.doc.documentElement);
31267                 d.innerHTML = v;
31268                  
31269                 this.el.dom.value = d.innerHTML;
31270                 this.owner.fireEvent('push', this, v);
31271             }
31272             if (this.autoClean) {
31273                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31274                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31275             }
31276             if (this.enableBlocks) {
31277                 Roo.htmleditor.Block.initAll(this.doc.body);
31278             }
31279             
31280             this.updateLanguage();
31281             
31282             var lc = this.doc.body.lastChild;
31283             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31284                 // add an extra line at the end.
31285                 this.doc.body.appendChild(this.doc.createElement('br'));
31286             }
31287             
31288             
31289         }
31290     },
31291
31292     // private
31293     deferFocus : function(){
31294         this.focus.defer(10, this);
31295     },
31296
31297     // doc'ed in Field
31298     focus : function(){
31299         if(this.win && !this.sourceEditMode){
31300             this.win.focus();
31301         }else{
31302             this.el.focus();
31303         }
31304     },
31305     
31306     assignDocWin: function()
31307     {
31308         var iframe = this.iframe;
31309         
31310          if(Roo.isIE){
31311             this.doc = iframe.contentWindow.document;
31312             this.win = iframe.contentWindow;
31313         } else {
31314 //            if (!Roo.get(this.frameId)) {
31315 //                return;
31316 //            }
31317 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31318 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31319             
31320             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31321                 return;
31322             }
31323             
31324             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31325             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31326         }
31327     },
31328     
31329     // private
31330     initEditor : function(){
31331         //console.log("INIT EDITOR");
31332         this.assignDocWin();
31333         
31334         
31335         
31336         this.doc.designMode="on";
31337         this.doc.open();
31338         this.doc.write(this.getDocMarkup());
31339         this.doc.close();
31340         
31341         var dbody = (this.doc.body || this.doc.documentElement);
31342         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31343         // this copies styles from the containing element into thsi one..
31344         // not sure why we need all of this..
31345         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31346         
31347         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31348         //ss['background-attachment'] = 'fixed'; // w3c
31349         dbody.bgProperties = 'fixed'; // ie
31350         dbody.setAttribute("translate", "no");
31351         
31352         //Roo.DomHelper.applyStyles(dbody, ss);
31353         Roo.EventManager.on(this.doc, {
31354              
31355             'mouseup': this.onEditorEvent,
31356             'dblclick': this.onEditorEvent,
31357             'click': this.onEditorEvent,
31358             'keyup': this.onEditorEvent,
31359             
31360             buffer:100,
31361             scope: this
31362         });
31363         Roo.EventManager.on(this.doc, {
31364             'paste': this.onPasteEvent,
31365             scope : this
31366         });
31367         if(Roo.isGecko){
31368             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31369         }
31370         //??? needed???
31371         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31372             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31373         }
31374         this.initialized = true;
31375
31376         
31377         // initialize special key events - enter
31378         new Roo.htmleditor.KeyEnter({core : this});
31379         
31380          
31381         
31382         this.owner.fireEvent('initialize', this);
31383         this.pushValue();
31384     },
31385     // this is to prevent a href clicks resulting in a redirect?
31386    
31387     onPasteEvent : function(e,v)
31388     {
31389         // I think we better assume paste is going to be a dirty load of rubish from word..
31390         
31391         // even pasting into a 'email version' of this widget will have to clean up that mess.
31392         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31393         
31394         // check what type of paste - if it's an image, then handle it differently.
31395         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31396             // pasting images? 
31397             var urlAPI = (window.createObjectURL && window) || 
31398                 (window.URL && URL.revokeObjectURL && URL) || 
31399                 (window.webkitURL && webkitURL);
31400             
31401             var r = new FileReader();
31402             var t = this;
31403             r.addEventListener('load',function()
31404             {
31405                 
31406                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31407                 // is insert asycn?
31408                 if (t.enableBlocks) {
31409                     
31410                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31411                         if (img.closest('figure')) { // assume!! that it's aready
31412                             return;
31413                         }
31414                         var fig  = new Roo.htmleditor.BlockFigure({
31415                             image_src  : img.src
31416                         });
31417                         fig.updateElement(img); // replace it..
31418                         
31419                     });
31420                 }
31421                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31422                 t.owner.fireEvent('paste', this);
31423             });
31424             r.readAsDataURL(cd.files[0]);
31425             
31426             e.preventDefault();
31427             
31428             return false;
31429         }
31430         if (cd.types.indexOf('text/html') < 0 ) {
31431             return false;
31432         }
31433         var images = [];
31434         var html = cd.getData('text/html'); // clipboard event
31435         if (cd.types.indexOf('text/rtf') > -1) {
31436             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31437             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31438         }
31439         //Roo.log(images);
31440         //Roo.log(imgs);
31441         // fixme..
31442         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31443                        .map(function(g) { return g.toDataURL(); })
31444                        .filter(function(g) { return g != 'about:blank'; });
31445         
31446         //Roo.log(html);
31447         html = this.cleanWordChars(html);
31448         
31449         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31450         
31451         
31452         var sn = this.getParentElement();
31453         // check if d contains a table, and prevent nesting??
31454         //Roo.log(d.getElementsByTagName('table'));
31455         //Roo.log(sn);
31456         //Roo.log(sn.closest('table'));
31457         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31458             e.preventDefault();
31459             this.insertAtCursor("You can not nest tables");
31460             //Roo.log("prevent?"); // fixme - 
31461             return false;
31462         }
31463         
31464         
31465         
31466         if (images.length > 0) {
31467             // replace all v:imagedata - with img.
31468             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31469             Roo.each(ar, function(node) {
31470                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31471                 node.parentNode.removeChild(node);
31472             });
31473             
31474             
31475             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31476                 img.setAttribute('src', images[i]);
31477             });
31478         }
31479         if (this.autoClean) {
31480             new Roo.htmleditor.FilterWord({ node : d });
31481             
31482             new Roo.htmleditor.FilterStyleToTag({ node : d });
31483             new Roo.htmleditor.FilterAttributes({
31484                 node : d,
31485                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31486                 attrib_clean : ['href', 'src' ] 
31487             });
31488             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31489             // should be fonts..
31490             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31491             new Roo.htmleditor.FilterParagraph({ node : d });
31492             new Roo.htmleditor.FilterSpan({ node : d });
31493             new Roo.htmleditor.FilterLongBr({ node : d });
31494             new Roo.htmleditor.FilterComment({ node : d });
31495             
31496             
31497         }
31498         if (this.enableBlocks) {
31499                 
31500             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31501                 if (img.closest('figure')) { // assume!! that it's aready
31502                     return;
31503                 }
31504                 var fig  = new Roo.htmleditor.BlockFigure({
31505                     image_src  : img.src
31506                 });
31507                 fig.updateElement(img); // replace it..
31508                 
31509             });
31510         }
31511         
31512         
31513         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31514         if (this.enableBlocks) {
31515             Roo.htmleditor.Block.initAll(this.doc.body);
31516         }
31517          
31518         
31519         e.preventDefault();
31520         this.owner.fireEvent('paste', this);
31521         return false;
31522         // default behaveiour should be our local cleanup paste? (optional?)
31523         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31524         //this.owner.fireEvent('paste', e, v);
31525     },
31526     // private
31527     onDestroy : function(){
31528         
31529         
31530         
31531         if(this.rendered){
31532             
31533             //for (var i =0; i < this.toolbars.length;i++) {
31534             //    // fixme - ask toolbars for heights?
31535             //    this.toolbars[i].onDestroy();
31536            // }
31537             
31538             //this.wrap.dom.innerHTML = '';
31539             //this.wrap.remove();
31540         }
31541     },
31542
31543     // private
31544     onFirstFocus : function(){
31545         
31546         this.assignDocWin();
31547         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31548         
31549         this.activated = true;
31550          
31551     
31552         if(Roo.isGecko){ // prevent silly gecko errors
31553             this.win.focus();
31554             var s = this.win.getSelection();
31555             if(!s.focusNode || s.focusNode.nodeType != 3){
31556                 var r = s.getRangeAt(0);
31557                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31558                 r.collapse(true);
31559                 this.deferFocus();
31560             }
31561             try{
31562                 this.execCmd('useCSS', true);
31563                 this.execCmd('styleWithCSS', false);
31564             }catch(e){}
31565         }
31566         this.owner.fireEvent('activate', this);
31567     },
31568
31569     // private
31570     adjustFont: function(btn){
31571         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31572         //if(Roo.isSafari){ // safari
31573         //    adjust *= 2;
31574        // }
31575         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31576         if(Roo.isSafari){ // safari
31577             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31578             v =  (v < 10) ? 10 : v;
31579             v =  (v > 48) ? 48 : v;
31580             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31581             
31582         }
31583         
31584         
31585         v = Math.max(1, v+adjust);
31586         
31587         this.execCmd('FontSize', v  );
31588     },
31589
31590     onEditorEvent : function(e)
31591     {
31592          
31593         
31594         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31595             return; // we do not handle this.. (undo manager does..)
31596         }
31597         // clicking a 'block'?
31598         
31599         // in theory this detects if the last element is not a br, then we try and do that.
31600         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31601         if (e &&
31602             e.target.nodeName == 'BODY' &&
31603             e.type == "mouseup" &&
31604             this.doc.body.lastChild
31605            ) {
31606             var lc = this.doc.body.lastChild;
31607             // gtx-trans is google translate plugin adding crap.
31608             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31609                 lc = lc.previousSibling;
31610             }
31611             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31612             // if last element is <BR> - then dont do anything.
31613             
31614                 var ns = this.doc.createElement('br');
31615                 this.doc.body.appendChild(ns);
31616                 range = this.doc.createRange();
31617                 range.setStartAfter(ns);
31618                 range.collapse(true);
31619                 var sel = this.win.getSelection();
31620                 sel.removeAllRanges();
31621                 sel.addRange(range);
31622             }
31623         }
31624         
31625         
31626         
31627         this.fireEditorEvent(e);
31628       //  this.updateToolbar();
31629         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31630     },
31631     
31632     fireEditorEvent: function(e)
31633     {
31634         this.owner.fireEvent('editorevent', this, e);
31635     },
31636
31637     insertTag : function(tg)
31638     {
31639         // could be a bit smarter... -> wrap the current selected tRoo..
31640         if (tg.toLowerCase() == 'span' ||
31641             tg.toLowerCase() == 'code' ||
31642             tg.toLowerCase() == 'sup' ||
31643             tg.toLowerCase() == 'sub' 
31644             ) {
31645             
31646             range = this.createRange(this.getSelection());
31647             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31648             wrappingNode.appendChild(range.extractContents());
31649             range.insertNode(wrappingNode);
31650
31651             return;
31652             
31653             
31654             
31655         }
31656         this.execCmd("formatblock",   tg);
31657         this.undoManager.addEvent(); 
31658     },
31659     
31660     insertText : function(txt)
31661     {
31662         
31663         
31664         var range = this.createRange();
31665         range.deleteContents();
31666                //alert(Sender.getAttribute('label'));
31667                
31668         range.insertNode(this.doc.createTextNode(txt));
31669         this.undoManager.addEvent();
31670     } ,
31671     
31672      
31673
31674     /**
31675      * Executes a Midas editor command on the editor document and performs necessary focus and
31676      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31677      * @param {String} cmd The Midas command
31678      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31679      */
31680     relayCmd : function(cmd, value)
31681     {
31682         
31683         switch (cmd) {
31684             case 'justifyleft':
31685             case 'justifyright':
31686             case 'justifycenter':
31687                 // if we are in a cell, then we will adjust the
31688                 var n = this.getParentElement();
31689                 var td = n.closest('td');
31690                 if (td) {
31691                     var bl = Roo.htmleditor.Block.factory(td);
31692                     bl.textAlign = cmd.replace('justify','');
31693                     bl.updateElement();
31694                     this.owner.fireEvent('editorevent', this);
31695                     return;
31696                 }
31697                 this.execCmd('styleWithCSS', true); // 
31698                 break;
31699             case 'bold':
31700             case 'italic':
31701             case 'underline':                
31702                 // if there is no selection, then we insert, and set the curson inside it..
31703                 this.execCmd('styleWithCSS', false); 
31704                 break;
31705                 
31706         
31707             default:
31708                 break;
31709         }
31710         
31711         
31712         this.win.focus();
31713         this.execCmd(cmd, value);
31714         this.owner.fireEvent('editorevent', this);
31715         //this.updateToolbar();
31716         this.owner.deferFocus();
31717     },
31718
31719     /**
31720      * Executes a Midas editor command directly on the editor document.
31721      * For visual commands, you should use {@link #relayCmd} instead.
31722      * <b>This should only be called after the editor is initialized.</b>
31723      * @param {String} cmd The Midas command
31724      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31725      */
31726     execCmd : function(cmd, value){
31727         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31728         this.syncValue();
31729     },
31730  
31731  
31732    
31733     /**
31734      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31735      * to insert tRoo.
31736      * @param {String} text | dom node.. 
31737      */
31738     insertAtCursor : function(text)
31739     {
31740         
31741         if(!this.activated){
31742             return;
31743         }
31744          
31745         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31746             this.win.focus();
31747             
31748             
31749             // from jquery ui (MIT licenced)
31750             var range, node;
31751             var win = this.win;
31752             
31753             if (win.getSelection && win.getSelection().getRangeAt) {
31754                 
31755                 // delete the existing?
31756                 
31757                 this.createRange(this.getSelection()).deleteContents();
31758                 range = win.getSelection().getRangeAt(0);
31759                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31760                 range.insertNode(node);
31761                 range = range.cloneRange();
31762                 range.collapse(false);
31763                  
31764                 win.getSelection().removeAllRanges();
31765                 win.getSelection().addRange(range);
31766                 
31767                 
31768                 
31769             } else if (win.document.selection && win.document.selection.createRange) {
31770                 // no firefox support
31771                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31772                 win.document.selection.createRange().pasteHTML(txt);
31773             
31774             } else {
31775                 // no firefox support
31776                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31777                 this.execCmd('InsertHTML', txt);
31778             } 
31779             this.syncValue();
31780             
31781             this.deferFocus();
31782         }
31783     },
31784  // private
31785     mozKeyPress : function(e){
31786         if(e.ctrlKey){
31787             var c = e.getCharCode(), cmd;
31788           
31789             if(c > 0){
31790                 c = String.fromCharCode(c).toLowerCase();
31791                 switch(c){
31792                     case 'b':
31793                         cmd = 'bold';
31794                         break;
31795                     case 'i':
31796                         cmd = 'italic';
31797                         break;
31798                     
31799                     case 'u':
31800                         cmd = 'underline';
31801                         break;
31802                     
31803                     //case 'v':
31804                       //  this.cleanUpPaste.defer(100, this);
31805                       //  return;
31806                         
31807                 }
31808                 if(cmd){
31809                     
31810                     this.relayCmd(cmd);
31811                     //this.win.focus();
31812                     //this.execCmd(cmd);
31813                     //this.deferFocus();
31814                     e.preventDefault();
31815                 }
31816                 
31817             }
31818         }
31819     },
31820
31821     // private
31822     fixKeys : function(){ // load time branching for fastest keydown performance
31823         
31824         
31825         if(Roo.isIE){
31826             return function(e){
31827                 var k = e.getKey(), r;
31828                 if(k == e.TAB){
31829                     e.stopEvent();
31830                     r = this.doc.selection.createRange();
31831                     if(r){
31832                         r.collapse(true);
31833                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31834                         this.deferFocus();
31835                     }
31836                     return;
31837                 }
31838                 /// this is handled by Roo.htmleditor.KeyEnter
31839                  /*
31840                 if(k == e.ENTER){
31841                     r = this.doc.selection.createRange();
31842                     if(r){
31843                         var target = r.parentElement();
31844                         if(!target || target.tagName.toLowerCase() != 'li'){
31845                             e.stopEvent();
31846                             r.pasteHTML('<br/>');
31847                             r.collapse(false);
31848                             r.select();
31849                         }
31850                     }
31851                 }
31852                 */
31853                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31854                 //    this.cleanUpPaste.defer(100, this);
31855                 //    return;
31856                 //}
31857                 
31858                 
31859             };
31860         }else if(Roo.isOpera){
31861             return function(e){
31862                 var k = e.getKey();
31863                 if(k == e.TAB){
31864                     e.stopEvent();
31865                     this.win.focus();
31866                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31867                     this.deferFocus();
31868                 }
31869                
31870                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31871                 //    this.cleanUpPaste.defer(100, this);
31872                  //   return;
31873                 //}
31874                 
31875             };
31876         }else if(Roo.isSafari){
31877             return function(e){
31878                 var k = e.getKey();
31879                 
31880                 if(k == e.TAB){
31881                     e.stopEvent();
31882                     this.execCmd('InsertText','\t');
31883                     this.deferFocus();
31884                     return;
31885                 }
31886                  this.mozKeyPress(e);
31887                 
31888                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31889                  //   this.cleanUpPaste.defer(100, this);
31890                  //   return;
31891                // }
31892                 
31893              };
31894         }
31895     }(),
31896     
31897     getAllAncestors: function()
31898     {
31899         var p = this.getSelectedNode();
31900         var a = [];
31901         if (!p) {
31902             a.push(p); // push blank onto stack..
31903             p = this.getParentElement();
31904         }
31905         
31906         
31907         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31908             a.push(p);
31909             p = p.parentNode;
31910         }
31911         a.push(this.doc.body);
31912         return a;
31913     },
31914     lastSel : false,
31915     lastSelNode : false,
31916     
31917     
31918     getSelection : function() 
31919     {
31920         this.assignDocWin();
31921         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31922     },
31923     /**
31924      * Select a dom node
31925      * @param {DomElement} node the node to select
31926      */
31927     selectNode : function(node, collapse)
31928     {
31929         var nodeRange = node.ownerDocument.createRange();
31930         try {
31931             nodeRange.selectNode(node);
31932         } catch (e) {
31933             nodeRange.selectNodeContents(node);
31934         }
31935         if (collapse === true) {
31936             nodeRange.collapse(true);
31937         }
31938         //
31939         var s = this.win.getSelection();
31940         s.removeAllRanges();
31941         s.addRange(nodeRange);
31942     },
31943     
31944     getSelectedNode: function() 
31945     {
31946         // this may only work on Gecko!!!
31947         
31948         // should we cache this!!!!
31949         
31950          
31951          
31952         var range = this.createRange(this.getSelection()).cloneRange();
31953         
31954         if (Roo.isIE) {
31955             var parent = range.parentElement();
31956             while (true) {
31957                 var testRange = range.duplicate();
31958                 testRange.moveToElementText(parent);
31959                 if (testRange.inRange(range)) {
31960                     break;
31961                 }
31962                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31963                     break;
31964                 }
31965                 parent = parent.parentElement;
31966             }
31967             return parent;
31968         }
31969         
31970         // is ancestor a text element.
31971         var ac =  range.commonAncestorContainer;
31972         if (ac.nodeType == 3) {
31973             ac = ac.parentNode;
31974         }
31975         
31976         var ar = ac.childNodes;
31977          
31978         var nodes = [];
31979         var other_nodes = [];
31980         var has_other_nodes = false;
31981         for (var i=0;i<ar.length;i++) {
31982             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31983                 continue;
31984             }
31985             // fullly contained node.
31986             
31987             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31988                 nodes.push(ar[i]);
31989                 continue;
31990             }
31991             
31992             // probably selected..
31993             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31994                 other_nodes.push(ar[i]);
31995                 continue;
31996             }
31997             // outer..
31998             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31999                 continue;
32000             }
32001             
32002             
32003             has_other_nodes = true;
32004         }
32005         if (!nodes.length && other_nodes.length) {
32006             nodes= other_nodes;
32007         }
32008         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32009             return false;
32010         }
32011         
32012         return nodes[0];
32013     },
32014     
32015     
32016     createRange: function(sel)
32017     {
32018         // this has strange effects when using with 
32019         // top toolbar - not sure if it's a great idea.
32020         //this.editor.contentWindow.focus();
32021         if (typeof sel != "undefined") {
32022             try {
32023                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32024             } catch(e) {
32025                 return this.doc.createRange();
32026             }
32027         } else {
32028             return this.doc.createRange();
32029         }
32030     },
32031     getParentElement: function()
32032     {
32033         
32034         this.assignDocWin();
32035         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32036         
32037         var range = this.createRange(sel);
32038          
32039         try {
32040             var p = range.commonAncestorContainer;
32041             while (p.nodeType == 3) { // text node
32042                 p = p.parentNode;
32043             }
32044             return p;
32045         } catch (e) {
32046             return null;
32047         }
32048     
32049     },
32050     /***
32051      *
32052      * Range intersection.. the hard stuff...
32053      *  '-1' = before
32054      *  '0' = hits..
32055      *  '1' = after.
32056      *         [ -- selected range --- ]
32057      *   [fail]                        [fail]
32058      *
32059      *    basically..
32060      *      if end is before start or  hits it. fail.
32061      *      if start is after end or hits it fail.
32062      *
32063      *   if either hits (but other is outside. - then it's not 
32064      *   
32065      *    
32066      **/
32067     
32068     
32069     // @see http://www.thismuchiknow.co.uk/?p=64.
32070     rangeIntersectsNode : function(range, node)
32071     {
32072         var nodeRange = node.ownerDocument.createRange();
32073         try {
32074             nodeRange.selectNode(node);
32075         } catch (e) {
32076             nodeRange.selectNodeContents(node);
32077         }
32078     
32079         var rangeStartRange = range.cloneRange();
32080         rangeStartRange.collapse(true);
32081     
32082         var rangeEndRange = range.cloneRange();
32083         rangeEndRange.collapse(false);
32084     
32085         var nodeStartRange = nodeRange.cloneRange();
32086         nodeStartRange.collapse(true);
32087     
32088         var nodeEndRange = nodeRange.cloneRange();
32089         nodeEndRange.collapse(false);
32090     
32091         return rangeStartRange.compareBoundaryPoints(
32092                  Range.START_TO_START, nodeEndRange) == -1 &&
32093                rangeEndRange.compareBoundaryPoints(
32094                  Range.START_TO_START, nodeStartRange) == 1;
32095         
32096          
32097     },
32098     rangeCompareNode : function(range, node)
32099     {
32100         var nodeRange = node.ownerDocument.createRange();
32101         try {
32102             nodeRange.selectNode(node);
32103         } catch (e) {
32104             nodeRange.selectNodeContents(node);
32105         }
32106         
32107         
32108         range.collapse(true);
32109     
32110         nodeRange.collapse(true);
32111      
32112         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32113         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32114          
32115         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32116         
32117         var nodeIsBefore   =  ss == 1;
32118         var nodeIsAfter    = ee == -1;
32119         
32120         if (nodeIsBefore && nodeIsAfter) {
32121             return 0; // outer
32122         }
32123         if (!nodeIsBefore && nodeIsAfter) {
32124             return 1; //right trailed.
32125         }
32126         
32127         if (nodeIsBefore && !nodeIsAfter) {
32128             return 2;  // left trailed.
32129         }
32130         // fully contined.
32131         return 3;
32132     },
32133  
32134     cleanWordChars : function(input) {// change the chars to hex code
32135         
32136        var swapCodes  = [ 
32137             [    8211, "&#8211;" ], 
32138             [    8212, "&#8212;" ], 
32139             [    8216,  "'" ],  
32140             [    8217, "'" ],  
32141             [    8220, '"' ],  
32142             [    8221, '"' ],  
32143             [    8226, "*" ],  
32144             [    8230, "..." ]
32145         ]; 
32146         var output = input;
32147         Roo.each(swapCodes, function(sw) { 
32148             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32149             
32150             output = output.replace(swapper, sw[1]);
32151         });
32152         
32153         return output;
32154     },
32155     
32156      
32157     
32158         
32159     
32160     cleanUpChild : function (node)
32161     {
32162         
32163         new Roo.htmleditor.FilterComment({node : node});
32164         new Roo.htmleditor.FilterAttributes({
32165                 node : node,
32166                 attrib_black : this.ablack,
32167                 attrib_clean : this.aclean,
32168                 style_white : this.cwhite,
32169                 style_black : this.cblack
32170         });
32171         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32172         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32173          
32174         
32175     },
32176     
32177     /**
32178      * Clean up MS wordisms...
32179      * @deprecated - use filter directly
32180      */
32181     cleanWord : function(node)
32182     {
32183         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32184         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32185         
32186     },
32187    
32188     
32189     /**
32190
32191      * @deprecated - use filters
32192      */
32193     cleanTableWidths : function(node)
32194     {
32195         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32196         
32197  
32198     },
32199     
32200      
32201         
32202     applyBlacklists : function()
32203     {
32204         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32205         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32206         
32207         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32208         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32209         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32210         
32211         this.white = [];
32212         this.black = [];
32213         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32214             if (b.indexOf(tag) > -1) {
32215                 return;
32216             }
32217             this.white.push(tag);
32218             
32219         }, this);
32220         
32221         Roo.each(w, function(tag) {
32222             if (b.indexOf(tag) > -1) {
32223                 return;
32224             }
32225             if (this.white.indexOf(tag) > -1) {
32226                 return;
32227             }
32228             this.white.push(tag);
32229             
32230         }, this);
32231         
32232         
32233         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32234             if (w.indexOf(tag) > -1) {
32235                 return;
32236             }
32237             this.black.push(tag);
32238             
32239         }, this);
32240         
32241         Roo.each(b, function(tag) {
32242             if (w.indexOf(tag) > -1) {
32243                 return;
32244             }
32245             if (this.black.indexOf(tag) > -1) {
32246                 return;
32247             }
32248             this.black.push(tag);
32249             
32250         }, this);
32251         
32252         
32253         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32254         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32255         
32256         this.cwhite = [];
32257         this.cblack = [];
32258         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32259             if (b.indexOf(tag) > -1) {
32260                 return;
32261             }
32262             this.cwhite.push(tag);
32263             
32264         }, this);
32265         
32266         Roo.each(w, function(tag) {
32267             if (b.indexOf(tag) > -1) {
32268                 return;
32269             }
32270             if (this.cwhite.indexOf(tag) > -1) {
32271                 return;
32272             }
32273             this.cwhite.push(tag);
32274             
32275         }, this);
32276         
32277         
32278         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32279             if (w.indexOf(tag) > -1) {
32280                 return;
32281             }
32282             this.cblack.push(tag);
32283             
32284         }, this);
32285         
32286         Roo.each(b, function(tag) {
32287             if (w.indexOf(tag) > -1) {
32288                 return;
32289             }
32290             if (this.cblack.indexOf(tag) > -1) {
32291                 return;
32292             }
32293             this.cblack.push(tag);
32294             
32295         }, this);
32296     },
32297     
32298     setStylesheets : function(stylesheets)
32299     {
32300         if(typeof(stylesheets) == 'string'){
32301             Roo.get(this.iframe.contentDocument.head).createChild({
32302                 tag : 'link',
32303                 rel : 'stylesheet',
32304                 type : 'text/css',
32305                 href : stylesheets
32306             });
32307             
32308             return;
32309         }
32310         var _this = this;
32311      
32312         Roo.each(stylesheets, function(s) {
32313             if(!s.length){
32314                 return;
32315             }
32316             
32317             Roo.get(_this.iframe.contentDocument.head).createChild({
32318                 tag : 'link',
32319                 rel : 'stylesheet',
32320                 type : 'text/css',
32321                 href : s
32322             });
32323         });
32324
32325         
32326     },
32327     
32328     
32329     updateLanguage : function()
32330     {
32331         if (!this.iframe || !this.iframe.contentDocument) {
32332             return;
32333         }
32334         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32335     },
32336     
32337     
32338     removeStylesheets : function()
32339     {
32340         var _this = this;
32341         
32342         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32343             s.remove();
32344         });
32345     },
32346     
32347     setStyle : function(style)
32348     {
32349         Roo.get(this.iframe.contentDocument.head).createChild({
32350             tag : 'style',
32351             type : 'text/css',
32352             html : style
32353         });
32354
32355         return;
32356     }
32357     
32358     // hide stuff that is not compatible
32359     /**
32360      * @event blur
32361      * @hide
32362      */
32363     /**
32364      * @event change
32365      * @hide
32366      */
32367     /**
32368      * @event focus
32369      * @hide
32370      */
32371     /**
32372      * @event specialkey
32373      * @hide
32374      */
32375     /**
32376      * @cfg {String} fieldClass @hide
32377      */
32378     /**
32379      * @cfg {String} focusClass @hide
32380      */
32381     /**
32382      * @cfg {String} autoCreate @hide
32383      */
32384     /**
32385      * @cfg {String} inputType @hide
32386      */
32387     /**
32388      * @cfg {String} invalidClass @hide
32389      */
32390     /**
32391      * @cfg {String} invalidText @hide
32392      */
32393     /**
32394      * @cfg {String} msgFx @hide
32395      */
32396     /**
32397      * @cfg {String} validateOnBlur @hide
32398      */
32399 });
32400
32401 Roo.HtmlEditorCore.white = [
32402         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32403         
32404        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32405        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32406        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32407        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32408        'TABLE',   'UL',         'XMP', 
32409        
32410        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32411       'THEAD',   'TR', 
32412      
32413       'DIR', 'MENU', 'OL', 'UL', 'DL',
32414        
32415       'EMBED',  'OBJECT'
32416 ];
32417
32418
32419 Roo.HtmlEditorCore.black = [
32420     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32421         'APPLET', // 
32422         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32423         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32424         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32425         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32426         //'FONT' // CLEAN LATER..
32427         'COLGROUP', 'COL'   // messy tables.
32428         
32429         
32430 ];
32431 Roo.HtmlEditorCore.clean = [ // ?? needed???
32432      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32433 ];
32434 Roo.HtmlEditorCore.tag_remove = [
32435     'FONT', 'TBODY'  
32436 ];
32437 // attributes..
32438
32439 Roo.HtmlEditorCore.ablack = [
32440     'on'
32441 ];
32442     
32443 Roo.HtmlEditorCore.aclean = [ 
32444     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32445 ];
32446
32447 // protocols..
32448 Roo.HtmlEditorCore.pwhite= [
32449         'http',  'https',  'mailto'
32450 ];
32451
32452 // white listed style attributes.
32453 Roo.HtmlEditorCore.cwhite= [
32454       //  'text-align', /// default is to allow most things..
32455       
32456          
32457 //        'font-size'//??
32458 ];
32459
32460 // black listed style attributes.
32461 Roo.HtmlEditorCore.cblack= [
32462       //  'font-size' -- this can be set by the project 
32463 ];
32464
32465
32466
32467
32468     /*
32469  * - LGPL
32470  *
32471  * HtmlEditor
32472  * 
32473  */
32474
32475 /**
32476  * @class Roo.bootstrap.form.HtmlEditor
32477  * @extends Roo.bootstrap.form.TextArea
32478  * Bootstrap HtmlEditor class
32479
32480  * @constructor
32481  * Create a new HtmlEditor
32482  * @param {Object} config The config object
32483  */
32484
32485 Roo.bootstrap.form.HtmlEditor = function(config){
32486
32487     this.addEvents({
32488             /**
32489              * @event initialize
32490              * Fires when the editor is fully initialized (including the iframe)
32491              * @param {Roo.bootstrap.form.HtmlEditor} this
32492              */
32493             initialize: true,
32494             /**
32495              * @event activate
32496              * Fires when the editor is first receives the focus. Any insertion must wait
32497              * until after this event.
32498              * @param {Roo.bootstrap.form.HtmlEditor} this
32499              */
32500             activate: true,
32501              /**
32502              * @event beforesync
32503              * Fires before the textarea is updated with content from the editor iframe. Return false
32504              * to cancel the sync.
32505              * @param {Roo.bootstrap.form.HtmlEditor} this
32506              * @param {String} html
32507              */
32508             beforesync: true,
32509              /**
32510              * @event beforepush
32511              * Fires before the iframe editor is updated with content from the textarea. Return false
32512              * to cancel the push.
32513              * @param {Roo.bootstrap.form.HtmlEditor} this
32514              * @param {String} html
32515              */
32516             beforepush: true,
32517              /**
32518              * @event sync
32519              * Fires when the textarea is updated with content from the editor iframe.
32520              * @param {Roo.bootstrap.form.HtmlEditor} this
32521              * @param {String} html
32522              */
32523             sync: true,
32524              /**
32525              * @event push
32526              * Fires when the iframe editor is updated with content from the textarea.
32527              * @param {Roo.bootstrap.form.HtmlEditor} this
32528              * @param {String} html
32529              */
32530             push: true,
32531              /**
32532              * @event editmodechange
32533              * Fires when the editor switches edit modes
32534              * @param {Roo.bootstrap.form.HtmlEditor} this
32535              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32536              */
32537             editmodechange: true,
32538             /**
32539              * @event editorevent
32540              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32541              * @param {Roo.bootstrap.form.HtmlEditor} this
32542              */
32543             editorevent: true,
32544             /**
32545              * @event firstfocus
32546              * Fires when on first focus - needed by toolbars..
32547              * @param {Roo.bootstrap.form.HtmlEditor} this
32548              */
32549             firstfocus: true,
32550             /**
32551              * @event autosave
32552              * Auto save the htmlEditor value as a file into Events
32553              * @param {Roo.bootstrap.form.HtmlEditor} this
32554              */
32555             autosave: true,
32556             /**
32557              * @event savedpreview
32558              * preview the saved version of htmlEditor
32559              * @param {Roo.bootstrap.form.HtmlEditor} this
32560              */
32561             savedpreview: true,
32562              /**
32563             * @event stylesheetsclick
32564             * Fires when press the Sytlesheets button
32565             * @param {Roo.HtmlEditorCore} this
32566             */
32567             stylesheetsclick: true,
32568             /**
32569             * @event paste
32570             * Fires when press user pastes into the editor
32571             * @param {Roo.HtmlEditorCore} this
32572             */
32573             paste: true,
32574             /**
32575             * @event imageadd
32576             * Fires when on any editor when an image is added (excluding paste)
32577             * @param {Roo.bootstrap.form.HtmlEditor} this
32578             */
32579            imageadd: true ,
32580             /**
32581             * @event imageupdated
32582             * Fires when on any editor when an image is changed (excluding paste)
32583             * @param {Roo.bootstrap.form.HtmlEditor} this
32584             * @param {HTMLElement} img could also be a figure if blocks are enabled
32585             */
32586            imageupdate: true ,
32587            /**
32588             * @event imagedelete
32589             * Fires when on any editor when an image is deleted
32590             * @param {Roo.bootstrap.form.HtmlEditor} this
32591             * @param {HTMLElement} img could also be a figure if blocks are enabled
32592             */
32593            imagedelete: true  
32594     });
32595     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32596     if (!this.toolbars) {
32597         this.toolbars = [];
32598     }
32599     
32600     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32601     
32602 };
32603
32604
32605 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32606     
32607     
32608       /**
32609      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32610      */
32611     toolbars : true,
32612     
32613      /**
32614     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32615     */
32616     btns : [],
32617    
32618      /**
32619      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32620      */
32621     resize : false,
32622      /**
32623      * @cfg {Number} height (in pixels)
32624      */   
32625     height: 300,
32626    /**
32627      * @cfg {Number} width (in pixels)
32628      */   
32629     width: false,
32630     
32631     /**
32632      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32633      * 
32634      */
32635     stylesheets: false,
32636     
32637     // id of frame..
32638     frameId: false,
32639     
32640     // private properties
32641     validationEvent : false,
32642     deferHeight: true,
32643     initialized : false,
32644     activated : false,
32645     
32646     onFocus : Roo.emptyFn,
32647     iframePad:3,
32648     hideMode:'offsets',
32649     
32650     tbContainer : false,
32651     
32652     bodyCls : '',
32653     
32654     toolbarContainer :function() {
32655         return this.wrap.select('.x-html-editor-tb',true).first();
32656     },
32657
32658     /**
32659      * Protected method that will not generally be called directly. It
32660      * is called when the editor creates its toolbar. Override this method if you need to
32661      * add custom toolbar buttons.
32662      * @param {HtmlEditor} editor
32663      */
32664     createToolbar : function()
32665     {
32666         //Roo.log('renewing');
32667         //Roo.log("create toolbars");
32668         if (this.toolbars === false) {
32669             return;
32670         }
32671         if (this.toolbars === true) {
32672             this.toolbars = [ 'Standard' ];
32673         }
32674         
32675         var ar = Array.from(this.toolbars);
32676         this.toolbars = [];
32677         ar.forEach(function(t,i) {
32678             if (typeof(t) == 'string') {
32679                 t = {
32680                     xtype : t
32681                 };
32682             }
32683             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32684                 t.editor = this;
32685                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32686                 t = Roo.factory(t);
32687             }
32688             this.toolbars[i] = t;
32689             this.toolbars[i].render(this.toolbarContainer());
32690         }, this);
32691         
32692         
32693     },
32694
32695      
32696     // private
32697     onRender : function(ct, position)
32698     {
32699        // Roo.log("Call onRender: " + this.xtype);
32700         var _t = this;
32701         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32702       
32703         this.wrap = this.inputEl().wrap({
32704             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32705         });
32706         
32707         this.editorcore.onRender(ct, position);
32708          
32709          
32710         this.createToolbar(this);
32711        
32712         
32713           
32714         
32715     },
32716
32717     // private
32718     onResize : function(w, h)
32719     {
32720         Roo.log('resize: ' +w + ',' + h );
32721         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32722         var ew = false;
32723         var eh = false;
32724         
32725         if(this.inputEl() ){
32726             if(typeof w == 'number'){
32727                 var aw = w - this.wrap.getFrameWidth('lr');
32728                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32729                 ew = aw;
32730             }
32731             if(typeof h == 'number'){
32732                  var tbh = -11;  // fixme it needs to tool bar size!
32733                 for (var i =0; i < this.toolbars.length;i++) {
32734                     // fixme - ask toolbars for heights?
32735                     tbh += this.toolbars[i].el.getHeight();
32736                     //if (this.toolbars[i].footer) {
32737                     //    tbh += this.toolbars[i].footer.el.getHeight();
32738                     //}
32739                 }
32740               
32741                 
32742                 
32743                 
32744                 
32745                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32746                 ah -= 5; // knock a few pixes off for look..
32747                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32748                 var eh = ah;
32749             }
32750         }
32751         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32752         this.editorcore.onResize(ew,eh);
32753         
32754     },
32755
32756     /**
32757      * Toggles the editor between standard and source edit mode.
32758      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32759      */
32760     toggleSourceEdit : function(sourceEditMode)
32761     {
32762         this.editorcore.toggleSourceEdit(sourceEditMode);
32763         
32764         if(this.editorcore.sourceEditMode){
32765             Roo.log('editor - showing textarea');
32766             
32767 //            Roo.log('in');
32768 //            Roo.log(this.syncValue());
32769             this.syncValue();
32770             this.inputEl().removeClass(['hide', 'x-hidden']);
32771             this.inputEl().dom.removeAttribute('tabIndex');
32772             this.inputEl().focus();
32773         }else{
32774             Roo.log('editor - hiding textarea');
32775 //            Roo.log('out')
32776 //            Roo.log(this.pushValue()); 
32777             this.pushValue();
32778             
32779             this.inputEl().addClass(['hide', 'x-hidden']);
32780             this.inputEl().dom.setAttribute('tabIndex', -1);
32781             //this.deferFocus();
32782         }
32783          
32784         //if(this.resizable){
32785         //    this.setSize(this.wrap.getSize());
32786         //}
32787         
32788         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32789     },
32790  
32791     // private (for BoxComponent)
32792     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32793
32794     // private (for BoxComponent)
32795     getResizeEl : function(){
32796         return this.wrap;
32797     },
32798
32799     // private (for BoxComponent)
32800     getPositionEl : function(){
32801         return this.wrap;
32802     },
32803
32804     // private
32805     initEvents : function(){
32806         this.originalValue = this.getValue();
32807     },
32808
32809 //    /**
32810 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32811 //     * @method
32812 //     */
32813 //    markInvalid : Roo.emptyFn,
32814 //    /**
32815 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32816 //     * @method
32817 //     */
32818 //    clearInvalid : Roo.emptyFn,
32819
32820     setValue : function(v){
32821         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32822         this.editorcore.pushValue();
32823     },
32824
32825      
32826     // private
32827     deferFocus : function(){
32828         this.focus.defer(10, this);
32829     },
32830
32831     // doc'ed in Field
32832     focus : function(){
32833         this.editorcore.focus();
32834         
32835     },
32836       
32837
32838     // private
32839     onDestroy : function(){
32840         
32841         
32842         
32843         if(this.rendered){
32844             
32845             for (var i =0; i < this.toolbars.length;i++) {
32846                 // fixme - ask toolbars for heights?
32847                 this.toolbars[i].onDestroy();
32848             }
32849             
32850             this.wrap.dom.innerHTML = '';
32851             this.wrap.remove();
32852         }
32853     },
32854
32855     // private
32856     onFirstFocus : function(){
32857         //Roo.log("onFirstFocus");
32858         this.editorcore.onFirstFocus();
32859          for (var i =0; i < this.toolbars.length;i++) {
32860             this.toolbars[i].onFirstFocus();
32861         }
32862         
32863     },
32864     
32865     // private
32866     syncValue : function()
32867     {   
32868         this.editorcore.syncValue();
32869     },
32870     
32871     pushValue : function()
32872     {   
32873         this.editorcore.pushValue();
32874     }
32875      
32876     
32877     // hide stuff that is not compatible
32878     /**
32879      * @event blur
32880      * @hide
32881      */
32882     /**
32883      * @event change
32884      * @hide
32885      */
32886     /**
32887      * @event focus
32888      * @hide
32889      */
32890     /**
32891      * @event specialkey
32892      * @hide
32893      */
32894     /**
32895      * @cfg {String} fieldClass @hide
32896      */
32897     /**
32898      * @cfg {String} focusClass @hide
32899      */
32900     /**
32901      * @cfg {String} autoCreate @hide
32902      */
32903     /**
32904      * @cfg {String} inputType @hide
32905      */
32906      
32907     /**
32908      * @cfg {String} invalidText @hide
32909      */
32910     /**
32911      * @cfg {String} msgFx @hide
32912      */
32913     /**
32914      * @cfg {String} validateOnBlur @hide
32915      */
32916 });
32917  
32918     
32919    
32920    
32921    
32922       
32923 /**
32924  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32925  * @parent Roo.bootstrap.form.HtmlEditor
32926  * @extends Roo.bootstrap.nav.Simplebar
32927  * Basic Toolbar
32928  * 
32929  * @example
32930  * Usage:
32931  *
32932  new Roo.bootstrap.form.HtmlEditor({
32933     ....
32934     toolbars : [
32935         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32936             disable : { fonts: 1 , format: 1, ..., ... , ...],
32937             btns : [ .... ]
32938         })
32939     }
32940      
32941  * 
32942  * @cfg {Object} disable List of elements to disable..
32943  * @cfg {Array} btns List of additional buttons.
32944  * 
32945  * 
32946  * NEEDS Extra CSS? 
32947  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32948  */
32949  
32950 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32951 {
32952     
32953     Roo.apply(this, config);
32954     
32955     // default disabled, based on 'good practice'..
32956     this.disable = this.disable || {};
32957     Roo.applyIf(this.disable, {
32958         fontSize : true,
32959         colors : true,
32960         specialElements : true
32961     });
32962     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32963     
32964     this.editor = config.editor;
32965     this.editorcore = config.editor.editorcore;
32966     
32967     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32968     
32969     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32970     // dont call parent... till later.
32971 }
32972 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
32973      
32974     bar : true,
32975     
32976     editor : false,
32977     editorcore : false,
32978     
32979     
32980     formats : [
32981         "p" ,  
32982         "h1","h2","h3","h4","h5","h6", 
32983         "pre", "code", 
32984         "abbr", "acronym", "address", "cite", "samp", "var",
32985         'div','span'
32986     ],
32987     
32988     
32989     deleteBtn: false,
32990     
32991     onRender : function(ct, position)
32992     {
32993        // Roo.log("Call onRender: " + this.xtype);
32994         
32995        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32996        Roo.log(this.el);
32997        this.el.dom.style.marginBottom = '0';
32998        var _this = this;
32999        var editorcore = this.editorcore;
33000        var editor= this.editor;
33001        
33002        var children = [];
33003        var btn = function(id, cmd , toggle, handler, html){
33004        
33005             var  event = toggle ? 'toggle' : 'click';
33006        
33007             var a = {
33008                 size : 'sm',
33009                 xtype: 'Button',
33010                 xns: Roo.bootstrap,
33011                 //glyphicon : id,
33012                 btnid : id,
33013                 fa: id,
33014                 cls : 'roo-html-editor-btn-' + id,
33015                 cmd : cmd, // why id || cmd
33016                 enableToggle: toggle !== false,
33017                 html : html || '',
33018                 pressed : toggle ? false : null,
33019                 listeners : {}
33020             };
33021             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33022                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33023             };
33024             children.push(a);
33025             return a;
33026        }
33027        
33028     //    var cb_box = function...
33029         
33030         var style = {
33031                 xtype: 'Button',
33032                 size : 'sm',
33033                 xns: Roo.bootstrap,
33034                 fa : 'font',
33035                 cls : 'roo-html-editor-font-chooser',
33036                 //html : 'submit'
33037                 menu : {
33038                     xtype: 'Menu',
33039                     xns: Roo.bootstrap,
33040                     items:  []
33041                 }
33042         };
33043         Roo.each(this.formats, function(f) {
33044             style.menu.items.push({
33045                 xtype :'MenuItem',
33046                 xns: Roo.bootstrap,
33047                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33048                 tagname : f,
33049                 listeners : {
33050                     click : function()
33051                     {
33052                         editorcore.insertTag(this.tagname);
33053                         editor.focus();
33054                     }
33055                 }
33056                 
33057             });
33058         });
33059         children.push(style);   
33060         
33061         btn('bold',         'bold',true);
33062         btn('italic',       'italic',true);
33063         btn('underline',     'underline',true);
33064         btn('align-left',   'justifyleft',true);
33065         btn('align-center', 'justifycenter',true);
33066         btn('align-right' , 'justifyright',true);
33067         btn('link', false, true, this.onLinkClick);
33068         
33069         
33070         btn('image', false, true, this.onImageClick);
33071         btn('list','insertunorderedlist',true);
33072         btn('list-ol','insertorderedlist',true);
33073
33074         btn('pencil', false,true, function(btn){
33075                 Roo.log(this);
33076                 this.toggleSourceEdit(btn.pressed);
33077         });
33078         
33079         if (this.editor.btns.length > 0) {
33080             for (var i = 0; i<this.editor.btns.length; i++) {
33081                 children.push(this.editor.btns[i]);
33082             }
33083         }
33084         
33085         
33086          
33087         this.xtype = 'NavSimplebar'; // why?
33088         
33089         for(var i=0;i< children.length;i++) {
33090             
33091             this.buttons.add(this.addxtypeChild(children[i]));
33092             
33093         }
33094         this.buildToolbarDelete();
33095
33096         editor.on('editorevent', this.updateToolbar, this);
33097     },
33098     
33099     buildToolbarDelete : function()
33100     {
33101         
33102        /* this.addxtypeChild({
33103             xtype : 'Element',
33104             xns : Roo.bootstrap,
33105             cls : 'roo-htmleditor-fill'
33106         });
33107         */
33108         this.deleteBtn = this.addxtypeChild({
33109             size : 'sm',
33110             xtype: 'Button',
33111             xns: Roo.bootstrap,
33112             fa: 'trash',
33113             listeners : {
33114                 click : this.onDelete.createDelegate(this)
33115             }
33116         });
33117         this.deleteBtn.hide();     
33118         
33119     },
33120     
33121     onImageClick : function()
33122     {
33123         if (this.input) {
33124             this.input.un('change', this.onFileSelected, this);
33125         }
33126         this.input = Roo.get(document.body).createChild({ 
33127           tag: 'input', 
33128           type : 'file', 
33129           style : 'display:none', 
33130           multiple: 'multiple'
33131        });
33132         this.input.on('change', this.onFileSelected, this);
33133         this.input.dom.click();
33134     },
33135     
33136     onFileSelected : function(e)
33137     {
33138          e.preventDefault();
33139         
33140         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33141             return;
33142         }
33143     
33144          
33145         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33146     },
33147     
33148     addFiles : function(far, fire_add) {
33149
33150          
33151         var editor =  this.editorcore;
33152   
33153         if (!far.length) {
33154             if (fire_add) {
33155                 this.editor.syncValue();
33156                 editor.owner.fireEvent('editorevent', editor.owner, false);
33157                 editor.owner.fireEvent('imageadd', editor.owner, false);
33158             }
33159             return;
33160         }
33161         
33162         var f = far.pop();
33163         
33164         if (!f.type.match(/^image/)) {
33165             this.addFiles(far, fire_add);
33166             return;
33167         }
33168          
33169         var sn = this.selectedNode;
33170         
33171         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33172         
33173         
33174         var reader = new FileReader();
33175         reader.addEventListener('load', (function() {
33176             if (bl) {
33177                 bl.image_src = reader.result;
33178                 //bl.caption = f.name;
33179                 bl.updateElement(sn);
33180                 this.editor.syncValue();
33181                 editor.owner.fireEvent('editorevent', editor.owner, false);
33182                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33183                 // we only do the first file!! and replace.
33184                 return;
33185             }
33186             if (this.editorcore.enableBlocks) {
33187                 var fig = new Roo.htmleditor.BlockFigure({
33188                     image_src :  reader.result,
33189                     caption : '',
33190                     caption_display : 'none'  //default to hide captions..
33191                  });
33192                 editor.insertAtCursor(fig.toHTML());
33193                 this.addFiles(far, true);
33194                 return;
33195             }
33196             // just a standard img..
33197             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33198                 sn.src = reader.result;
33199                 this.editor.syncValue();
33200                 editor.owner.fireEvent('editorevent', editor.owner, false);
33201                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33202                 return;
33203             }
33204             editor.insertAtCursor('<img src="' + reader.result +'">');
33205             this.addFiles(far, true);
33206             
33207         }).createDelegate(this));
33208         reader.readAsDataURL(f);
33209         
33210     
33211      },
33212     
33213     
33214     onBtnClick : function(id)
33215     {
33216        this.editorcore.relayCmd(id);
33217        this.editorcore.focus();
33218     },
33219     
33220     onLinkClick : function(btn) {
33221         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33222                 this.selectedNode.getAttribute('href') : '';
33223             
33224         Roo.bootstrap.MessageBox.show({
33225             title : "Add / Edit Link URL",
33226             msg : "Enter the URL for the link",
33227             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33228             minWidth: 250,
33229             scope : this,
33230             prompt:true,
33231             multiline: false,
33232             modal : true,
33233             value : url,
33234             fn:  function(pressed, newurl) {
33235                 if (pressed != 'ok') {
33236                     this.editorcore.focus();
33237                     return;
33238                 }
33239                 if (url != '') {
33240                     this.selectedNode.setAttribute('href', newurl);
33241                     return;
33242                 }
33243                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33244                     this.editorcore.relayCmd('createlink', newurl);
33245                 }
33246                 this.editorcore.focus();
33247             }
33248         });
33249     },
33250     /**
33251      * Protected method that will not generally be called directly. It triggers
33252      * a toolbar update by reading the markup state of the current selection in the editor.
33253      */
33254     updateToolbar: function(editor ,ev, sel){
33255
33256         if(!this.editorcore.activated){
33257             this.editor.onFirstFocus(); // is this neeed?
33258             return;
33259         }
33260
33261         var btns = this.buttons; 
33262         var doc = this.editorcore.doc;
33263         var hasToggle  = false;
33264         btns.each(function(e) {
33265             if (e.enableToggle && e.cmd) {
33266                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33267                 e.setActive(doc.queryCommandState(e.cmd));
33268             }
33269         }, this);
33270         
33271         
33272         if (ev &&
33273             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33274             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33275             // they have click on an image...
33276             // let's see if we can change the selection...
33277             sel = ev.target;
33278             
33279         }
33280         
33281         var ans = this.editorcore.getAllAncestors();
33282         if (!sel) { 
33283             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33284             sel = sel ? sel : this.editorcore.doc.body;
33285             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33286             
33287         }
33288         
33289         var lastSel = this.selectedNode;
33290         this.selectedNode = sel;
33291          
33292         // ok see if we are editing a block?
33293         
33294         var db = false;
33295         // you are not actually selecting the block.
33296         if (sel && sel.hasAttribute('data-block')) {
33297             db = sel;
33298         } else if (sel && sel.closest('[data-block]')) {
33299             db = sel.closest('[data-block]');
33300         }
33301         
33302         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33303             e.classList.remove('roo-ed-selection');
33304         });
33305         
33306         var block = false;
33307         if (db && this.editorcore.enableBlocks) {
33308             block = Roo.htmleditor.Block.factory(db);
33309             
33310             if (block) {
33311                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33312                     ' roo-ed-selection';
33313                 sel = this.selectedNode = db;
33314             }
33315         }
33316         
33317         // highlight the 'a'..
33318         var tn = sel && sel.tagName.toUpperCase() || '';
33319         if (!block && sel && tn != 'A') {
33320             var asel = sel.closest('A');
33321             if (asel) {
33322                 sel = asel;
33323             }
33324         }
33325        
33326         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33327         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33328         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33329         
33330         Roo.bootstrap.menu.Manager.hideAll();
33331          
33332         
33333         
33334         
33335         
33336         // handle delete button..
33337         if (hasToggle || (tn.length && tn == 'BODY')) {
33338             this.deleteBtn.hide();
33339             return;
33340             
33341         }
33342         this.deleteBtn.show();
33343         
33344         
33345         
33346         //this.editorsyncValue();
33347     },
33348     onFirstFocus: function() {
33349         this.buttons.each(function(item){
33350            item.enable();
33351         });
33352     },
33353     
33354     onDelete : function()
33355     {
33356         var range = this.editorcore.createRange();
33357         var selection = this.editorcore.getSelection();
33358         var sn = this.selectedNode;
33359         range.setStart(sn,0);
33360         range.setEnd(sn,0); 
33361         
33362         
33363         if (sn.hasAttribute('data-block')) {
33364             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33365             if (block) {
33366                 sn = block.removeNode();
33367                 sn.parentNode.removeChild(sn);
33368                 selection.removeAllRanges();
33369                 selection.addRange(range);
33370                 this.updateToolbar(null, null, null);
33371                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33372                     this.editor.syncValue();
33373                     this.editor.fireEvent('imagedelete', this.editor, sn);
33374                 }
33375                 
33376                 this.selectedNode = false;
33377                 this.editorcore.fireEditorEvent(false);
33378                 return;
33379             }   
33380              
33381         }
33382         if (!sn) {
33383             return; // should not really happen..
33384         }
33385         if (sn && sn.tagName == 'BODY') {
33386             return;
33387         }
33388         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33389         
33390         // remove and keep parents.
33391         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33392         a.replaceTag(sn);
33393         
33394         selection.removeAllRanges();
33395         selection.addRange(range);
33396         if (sn.tagName.toUpperCase() == 'IMG"') {
33397             this.editor.syncValue();
33398             this.editor.fireEvent('imagedelete', this.editor, sn);
33399         }
33400         
33401         this.selectedNode = false;
33402         this.editorcore.fireEditorEvent(false);
33403         
33404         
33405     },
33406     
33407     
33408     toggleSourceEdit : function(sourceEditMode){
33409         
33410           
33411         if(sourceEditMode){
33412             Roo.log("disabling buttons");
33413            this.buttons.each( function(item){
33414                 if(item.cmd != 'pencil'){
33415                     item.disable();
33416                 }
33417             });
33418           
33419         }else{
33420             Roo.log("enabling buttons");
33421             if(this.editorcore.initialized){
33422                 this.buttons.each( function(item){
33423                     item.enable();
33424                 });
33425             }
33426             
33427         }
33428         Roo.log("calling toggole on editor");
33429         // tell the editor that it's been pressed..
33430         this.editor.toggleSourceEdit(sourceEditMode);
33431        
33432     }
33433 });
33434
33435
33436
33437
33438  
33439 /*
33440  * - LGPL
33441  */
33442
33443 /**
33444  * @class Roo.bootstrap.form.Markdown
33445  * @extends Roo.bootstrap.form.TextArea
33446  * Bootstrap Showdown editable area
33447  * @cfg {string} content
33448  * 
33449  * @constructor
33450  * Create a new Showdown
33451  */
33452
33453 Roo.bootstrap.form.Markdown = function(config){
33454     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33455    
33456 };
33457
33458 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33459     
33460     editing :false,
33461     
33462     initEvents : function()
33463     {
33464         
33465         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33466         this.markdownEl = this.el.createChild({
33467             cls : 'roo-markdown-area'
33468         });
33469         this.inputEl().addClass('d-none');
33470         if (this.getValue() == '') {
33471             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33472             
33473         } else {
33474             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33475         }
33476         this.markdownEl.on('click', this.toggleTextEdit, this);
33477         this.on('blur', this.toggleTextEdit, this);
33478         this.on('specialkey', this.resizeTextArea, this);
33479     },
33480     
33481     toggleTextEdit : function()
33482     {
33483         var sh = this.markdownEl.getHeight();
33484         this.inputEl().addClass('d-none');
33485         this.markdownEl.addClass('d-none');
33486         if (!this.editing) {
33487             // show editor?
33488             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33489             this.inputEl().removeClass('d-none');
33490             this.inputEl().focus();
33491             this.editing = true;
33492             return;
33493         }
33494         // show showdown...
33495         this.updateMarkdown();
33496         this.markdownEl.removeClass('d-none');
33497         this.editing = false;
33498         return;
33499     },
33500     updateMarkdown : function()
33501     {
33502         if (this.getValue() == '') {
33503             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33504             return;
33505         }
33506  
33507         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33508     },
33509     
33510     resizeTextArea: function () {
33511         
33512         var sh = 100;
33513         Roo.log([sh, this.getValue().split("\n").length * 30]);
33514         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33515     },
33516     setValue : function(val)
33517     {
33518         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33519         if (!this.editing) {
33520             this.updateMarkdown();
33521         }
33522         
33523     },
33524     focus : function()
33525     {
33526         if (!this.editing) {
33527             this.toggleTextEdit();
33528         }
33529         
33530     }
33531
33532
33533 });/*
33534  * Based on:
33535  * Ext JS Library 1.1.1
33536  * Copyright(c) 2006-2007, Ext JS, LLC.
33537  *
33538  * Originally Released Under LGPL - original licence link has changed is not relivant.
33539  *
33540  * Fork - LGPL
33541  * <script type="text/javascript">
33542  */
33543  
33544 /**
33545  * @class Roo.bootstrap.PagingToolbar
33546  * @extends Roo.bootstrap.nav.Simplebar
33547  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33548  * @constructor
33549  * Create a new PagingToolbar
33550  * @param {Object} config The config object
33551  * @param {Roo.data.Store} store
33552  */
33553 Roo.bootstrap.PagingToolbar = function(config)
33554 {
33555     // old args format still supported... - xtype is prefered..
33556         // created from xtype...
33557     
33558     this.ds = config.dataSource;
33559     
33560     if (config.store && !this.ds) {
33561         this.store= Roo.factory(config.store, Roo.data);
33562         this.ds = this.store;
33563         this.ds.xmodule = this.xmodule || false;
33564     }
33565     
33566     this.toolbarItems = [];
33567     if (config.items) {
33568         this.toolbarItems = config.items;
33569     }
33570     
33571     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33572     
33573     this.cursor = 0;
33574     
33575     if (this.ds) { 
33576         this.bind(this.ds);
33577     }
33578     
33579     if (Roo.bootstrap.version == 4) {
33580         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33581     } else {
33582         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33583     }
33584     
33585 };
33586
33587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33588     /**
33589      * @cfg {Roo.bootstrap.Button} buttons[]
33590      * Buttons for the toolbar
33591      */
33592      /**
33593      * @cfg {Roo.data.Store} store
33594      * The underlying data store providing the paged data
33595      */
33596     /**
33597      * @cfg {String/HTMLElement/Element} container
33598      * container The id or element that will contain the toolbar
33599      */
33600     /**
33601      * @cfg {Boolean} displayInfo
33602      * True to display the displayMsg (defaults to false)
33603      */
33604     /**
33605      * @cfg {Number} pageSize
33606      * The number of records to display per page (defaults to 20)
33607      */
33608     pageSize: 20,
33609     /**
33610      * @cfg {String} displayMsg
33611      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33612      */
33613     displayMsg : 'Displaying {0} - {1} of {2}',
33614     /**
33615      * @cfg {String} emptyMsg
33616      * The message to display when no records are found (defaults to "No data to display")
33617      */
33618     emptyMsg : 'No data to display',
33619     /**
33620      * Customizable piece of the default paging text (defaults to "Page")
33621      * @type String
33622      */
33623     beforePageText : "Page",
33624     /**
33625      * Customizable piece of the default paging text (defaults to "of %0")
33626      * @type String
33627      */
33628     afterPageText : "of {0}",
33629     /**
33630      * Customizable piece of the default paging text (defaults to "First Page")
33631      * @type String
33632      */
33633     firstText : "First Page",
33634     /**
33635      * Customizable piece of the default paging text (defaults to "Previous Page")
33636      * @type String
33637      */
33638     prevText : "Previous Page",
33639     /**
33640      * Customizable piece of the default paging text (defaults to "Next Page")
33641      * @type String
33642      */
33643     nextText : "Next Page",
33644     /**
33645      * Customizable piece of the default paging text (defaults to "Last Page")
33646      * @type String
33647      */
33648     lastText : "Last Page",
33649     /**
33650      * Customizable piece of the default paging text (defaults to "Refresh")
33651      * @type String
33652      */
33653     refreshText : "Refresh",
33654
33655     buttons : false,
33656     // private
33657     onRender : function(ct, position) 
33658     {
33659         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33660         this.navgroup.parentId = this.id;
33661         this.navgroup.onRender(this.el, null);
33662         // add the buttons to the navgroup
33663         
33664         if(this.displayInfo){
33665             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33666             this.displayEl = this.el.select('.x-paging-info', true).first();
33667 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33668 //            this.displayEl = navel.el.select('span',true).first();
33669         }
33670         
33671         var _this = this;
33672         
33673         if(this.buttons){
33674             Roo.each(_this.buttons, function(e){ // this might need to use render????
33675                Roo.factory(e).render(_this.el);
33676             });
33677         }
33678             
33679         Roo.each(_this.toolbarItems, function(e) {
33680             _this.navgroup.addItem(e);
33681         });
33682         
33683         
33684         this.first = this.navgroup.addItem({
33685             tooltip: this.firstText,
33686             cls: "prev btn-outline-secondary",
33687             html : ' <i class="fa fa-step-backward"></i>',
33688             disabled: true,
33689             preventDefault: true,
33690             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33691         });
33692         
33693         this.prev =  this.navgroup.addItem({
33694             tooltip: this.prevText,
33695             cls: "prev btn-outline-secondary",
33696             html : ' <i class="fa fa-backward"></i>',
33697             disabled: true,
33698             preventDefault: true,
33699             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33700         });
33701     //this.addSeparator();
33702         
33703         
33704         var field = this.navgroup.addItem( {
33705             tagtype : 'span',
33706             cls : 'x-paging-position  btn-outline-secondary',
33707              disabled: true,
33708             html : this.beforePageText  +
33709                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33710                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33711          } ); //?? escaped?
33712         
33713         this.field = field.el.select('input', true).first();
33714         this.field.on("keydown", this.onPagingKeydown, this);
33715         this.field.on("focus", function(){this.dom.select();});
33716     
33717     
33718         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33719         //this.field.setHeight(18);
33720         //this.addSeparator();
33721         this.next = this.navgroup.addItem({
33722             tooltip: this.nextText,
33723             cls: "next btn-outline-secondary",
33724             html : ' <i class="fa fa-forward"></i>',
33725             disabled: true,
33726             preventDefault: true,
33727             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33728         });
33729         this.last = this.navgroup.addItem({
33730             tooltip: this.lastText,
33731             html : ' <i class="fa fa-step-forward"></i>',
33732             cls: "next btn-outline-secondary",
33733             disabled: true,
33734             preventDefault: true,
33735             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33736         });
33737     //this.addSeparator();
33738         this.loading = this.navgroup.addItem({
33739             tooltip: this.refreshText,
33740             cls: "btn-outline-secondary",
33741             html : ' <i class="fa fa-refresh"></i>',
33742             preventDefault: true,
33743             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33744         });
33745         
33746     },
33747
33748     // private
33749     updateInfo : function(){
33750         if(this.displayEl){
33751             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33752             var msg = count == 0 ?
33753                 this.emptyMsg :
33754                 String.format(
33755                     this.displayMsg,
33756                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33757                 );
33758             this.displayEl.update(msg);
33759         }
33760     },
33761
33762     // private
33763     onLoad : function(ds, r, o)
33764     {
33765         this.cursor = o.params && o.params.start ? o.params.start : 0;
33766         
33767         var d = this.getPageData(),
33768             ap = d.activePage,
33769             ps = d.pages;
33770         
33771         
33772         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33773         this.field.dom.value = ap;
33774         this.first.setDisabled(ap == 1);
33775         this.prev.setDisabled(ap == 1);
33776         this.next.setDisabled(ap == ps);
33777         this.last.setDisabled(ap == ps);
33778         this.loading.enable();
33779         this.updateInfo();
33780     },
33781
33782     // private
33783     getPageData : function(){
33784         var total = this.ds.getTotalCount();
33785         return {
33786             total : total,
33787             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33788             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33789         };
33790     },
33791
33792     // private
33793     onLoadError : function(proxy, o){
33794         this.loading.enable();
33795         if (this.ds.events.loadexception.listeners.length  < 2) {
33796             // nothing has been assigned to loadexception except this...
33797             // so 
33798             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33799
33800         }
33801     },
33802
33803     // private
33804     onPagingKeydown : function(e){
33805         var k = e.getKey();
33806         var d = this.getPageData();
33807         if(k == e.RETURN){
33808             var v = this.field.dom.value, pageNum;
33809             if(!v || isNaN(pageNum = parseInt(v, 10))){
33810                 this.field.dom.value = d.activePage;
33811                 return;
33812             }
33813             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33814             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33815             e.stopEvent();
33816         }
33817         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))
33818         {
33819           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33820           this.field.dom.value = pageNum;
33821           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33822           e.stopEvent();
33823         }
33824         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33825         {
33826           var v = this.field.dom.value, pageNum; 
33827           var increment = (e.shiftKey) ? 10 : 1;
33828           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33829                 increment *= -1;
33830           }
33831           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33832             this.field.dom.value = d.activePage;
33833             return;
33834           }
33835           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33836           {
33837             this.field.dom.value = parseInt(v, 10) + increment;
33838             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33839             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33840           }
33841           e.stopEvent();
33842         }
33843     },
33844
33845     // private
33846     beforeLoad : function(){
33847         if(this.loading){
33848             this.loading.disable();
33849         }
33850     },
33851
33852     // private
33853     onClick : function(which){
33854         
33855         var ds = this.ds;
33856         if (!ds) {
33857             return;
33858         }
33859         
33860         switch(which){
33861             case "first":
33862                 ds.load({params:{start: 0, limit: this.pageSize}});
33863             break;
33864             case "prev":
33865                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33866             break;
33867             case "next":
33868                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33869             break;
33870             case "last":
33871                 var total = ds.getTotalCount();
33872                 var extra = total % this.pageSize;
33873                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33874                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33875             break;
33876             case "refresh":
33877                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33878             break;
33879         }
33880     },
33881
33882     /**
33883      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33884      * @param {Roo.data.Store} store The data store to unbind
33885      */
33886     unbind : function(ds){
33887         ds.un("beforeload", this.beforeLoad, this);
33888         ds.un("load", this.onLoad, this);
33889         ds.un("loadexception", this.onLoadError, this);
33890         ds.un("remove", this.updateInfo, this);
33891         ds.un("add", this.updateInfo, this);
33892         this.ds = undefined;
33893     },
33894
33895     /**
33896      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33897      * @param {Roo.data.Store} store The data store to bind
33898      */
33899     bind : function(ds){
33900         ds.on("beforeload", this.beforeLoad, this);
33901         ds.on("load", this.onLoad, this);
33902         ds.on("loadexception", this.onLoadError, this);
33903         ds.on("remove", this.updateInfo, this);
33904         ds.on("add", this.updateInfo, this);
33905         this.ds = ds;
33906     }
33907 });/*
33908  * - LGPL
33909  *
33910  * element
33911  * 
33912  */
33913
33914 /**
33915  * @class Roo.bootstrap.MessageBar
33916  * @extends Roo.bootstrap.Component
33917  * Bootstrap MessageBar class
33918  * @cfg {String} html contents of the MessageBar
33919  * @cfg {String} weight (info | success | warning | danger) default info
33920  * @cfg {String} beforeClass insert the bar before the given class
33921  * @cfg {Boolean} closable (true | false) default false
33922  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33923  * 
33924  * @constructor
33925  * Create a new Element
33926  * @param {Object} config The config object
33927  */
33928
33929 Roo.bootstrap.MessageBar = function(config){
33930     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33931 };
33932
33933 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33934     
33935     html: '',
33936     weight: 'info',
33937     closable: false,
33938     fixed: false,
33939     beforeClass: 'bootstrap-sticky-wrap',
33940     
33941     getAutoCreate : function(){
33942         
33943         var cfg = {
33944             tag: 'div',
33945             cls: 'alert alert-dismissable alert-' + this.weight,
33946             cn: [
33947                 {
33948                     tag: 'span',
33949                     cls: 'message',
33950                     html: this.html || ''
33951                 }
33952             ]
33953         };
33954         
33955         if(this.fixed){
33956             cfg.cls += ' alert-messages-fixed';
33957         }
33958         
33959         if(this.closable){
33960             cfg.cn.push({
33961                 tag: 'button',
33962                 cls: 'close',
33963                 html: 'x'
33964             });
33965         }
33966         
33967         return cfg;
33968     },
33969     
33970     onRender : function(ct, position)
33971     {
33972         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33973         
33974         if(!this.el){
33975             var cfg = Roo.apply({},  this.getAutoCreate());
33976             cfg.id = Roo.id();
33977             
33978             if (this.cls) {
33979                 cfg.cls += ' ' + this.cls;
33980             }
33981             if (this.style) {
33982                 cfg.style = this.style;
33983             }
33984             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33985             
33986             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33987         }
33988         
33989         this.el.select('>button.close').on('click', this.hide, this);
33990         
33991     },
33992     
33993     show : function()
33994     {
33995         if (!this.rendered) {
33996             this.render();
33997         }
33998         
33999         this.el.show();
34000         
34001         this.fireEvent('show', this);
34002         
34003     },
34004     
34005     hide : function()
34006     {
34007         if (!this.rendered) {
34008             this.render();
34009         }
34010         
34011         this.el.hide();
34012         
34013         this.fireEvent('hide', this);
34014     },
34015     
34016     update : function()
34017     {
34018 //        var e = this.el.dom.firstChild;
34019 //        
34020 //        if(this.closable){
34021 //            e = e.nextSibling;
34022 //        }
34023 //        
34024 //        e.data = this.html || '';
34025
34026         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34027     }
34028    
34029 });
34030
34031  
34032
34033      /*
34034  * - LGPL
34035  *
34036  * Graph
34037  * 
34038  */
34039
34040
34041 /**
34042  * @class Roo.bootstrap.Graph
34043  * @extends Roo.bootstrap.Component
34044  * Bootstrap Graph class
34045 > Prameters
34046  -sm {number} sm 4
34047  -md {number} md 5
34048  @cfg {String} graphtype  bar | vbar | pie
34049  @cfg {number} g_x coodinator | centre x (pie)
34050  @cfg {number} g_y coodinator | centre y (pie)
34051  @cfg {number} g_r radius (pie)
34052  @cfg {number} g_height height of the chart (respected by all elements in the set)
34053  @cfg {number} g_width width of the chart (respected by all elements in the set)
34054  @cfg {Object} title The title of the chart
34055     
34056  -{Array}  values
34057  -opts (object) options for the chart 
34058      o {
34059      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34060      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34061      o vgutter (number)
34062      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.
34063      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34064      o to
34065      o stretch (boolean)
34066      o }
34067  -opts (object) options for the pie
34068      o{
34069      o cut
34070      o startAngle (number)
34071      o endAngle (number)
34072      } 
34073  *
34074  * @constructor
34075  * Create a new Input
34076  * @param {Object} config The config object
34077  */
34078
34079 Roo.bootstrap.Graph = function(config){
34080     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34081     
34082     this.addEvents({
34083         // img events
34084         /**
34085          * @event click
34086          * The img click event for the img.
34087          * @param {Roo.EventObject} e
34088          */
34089         "click" : true
34090     });
34091 };
34092
34093 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34094     
34095     sm: 4,
34096     md: 5,
34097     graphtype: 'bar',
34098     g_height: 250,
34099     g_width: 400,
34100     g_x: 50,
34101     g_y: 50,
34102     g_r: 30,
34103     opts:{
34104         //g_colors: this.colors,
34105         g_type: 'soft',
34106         g_gutter: '20%'
34107
34108     },
34109     title : false,
34110
34111     getAutoCreate : function(){
34112         
34113         var cfg = {
34114             tag: 'div',
34115             html : null
34116         };
34117         
34118         
34119         return  cfg;
34120     },
34121
34122     onRender : function(ct,position){
34123         
34124         
34125         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34126         
34127         if (typeof(Raphael) == 'undefined') {
34128             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34129             return;
34130         }
34131         
34132         this.raphael = Raphael(this.el.dom);
34133         
34134                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34135                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34136                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34137                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34138                 /*
34139                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34140                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34141                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34142                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34143                 
34144                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34145                 r.barchart(330, 10, 300, 220, data1);
34146                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34147                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34148                 */
34149                 
34150                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34151                 // r.barchart(30, 30, 560, 250,  xdata, {
34152                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34153                 //     axis : "0 0 1 1",
34154                 //     axisxlabels :  xdata
34155                 //     //yvalues : cols,
34156                    
34157                 // });
34158 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34159 //        
34160 //        this.load(null,xdata,{
34161 //                axis : "0 0 1 1",
34162 //                axisxlabels :  xdata
34163 //                });
34164
34165     },
34166
34167     load : function(graphtype,xdata,opts)
34168     {
34169         this.raphael.clear();
34170         if(!graphtype) {
34171             graphtype = this.graphtype;
34172         }
34173         if(!opts){
34174             opts = this.opts;
34175         }
34176         var r = this.raphael,
34177             fin = function () {
34178                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34179             },
34180             fout = function () {
34181                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34182             },
34183             pfin = function() {
34184                 this.sector.stop();
34185                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34186
34187                 if (this.label) {
34188                     this.label[0].stop();
34189                     this.label[0].attr({ r: 7.5 });
34190                     this.label[1].attr({ "font-weight": 800 });
34191                 }
34192             },
34193             pfout = function() {
34194                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34195
34196                 if (this.label) {
34197                     this.label[0].animate({ r: 5 }, 500, "bounce");
34198                     this.label[1].attr({ "font-weight": 400 });
34199                 }
34200             };
34201
34202         switch(graphtype){
34203             case 'bar':
34204                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34205                 break;
34206             case 'hbar':
34207                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34208                 break;
34209             case 'pie':
34210 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34211 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34212 //            
34213                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34214                 
34215                 break;
34216
34217         }
34218         
34219         if(this.title){
34220             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34221         }
34222         
34223     },
34224     
34225     setTitle: function(o)
34226     {
34227         this.title = o;
34228     },
34229     
34230     initEvents: function() {
34231         
34232         if(!this.href){
34233             this.el.on('click', this.onClick, this);
34234         }
34235     },
34236     
34237     onClick : function(e)
34238     {
34239         Roo.log('img onclick');
34240         this.fireEvent('click', this, e);
34241     }
34242    
34243 });
34244
34245  
34246 Roo.bootstrap.dash = {};/*
34247  * - LGPL
34248  *
34249  * numberBox
34250  * 
34251  */
34252 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34253
34254 /**
34255  * @class Roo.bootstrap.dash.NumberBox
34256  * @extends Roo.bootstrap.Component
34257  * Bootstrap NumberBox class
34258  * @cfg {String} headline Box headline
34259  * @cfg {String} content Box content
34260  * @cfg {String} icon Box icon
34261  * @cfg {String} footer Footer text
34262  * @cfg {String} fhref Footer href
34263  * 
34264  * @constructor
34265  * Create a new NumberBox
34266  * @param {Object} config The config object
34267  */
34268
34269
34270 Roo.bootstrap.dash.NumberBox = function(config){
34271     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34272     
34273 };
34274
34275 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34276     
34277     headline : '',
34278     content : '',
34279     icon : '',
34280     footer : '',
34281     fhref : '',
34282     ficon : '',
34283     
34284     getAutoCreate : function(){
34285         
34286         var cfg = {
34287             tag : 'div',
34288             cls : 'small-box ',
34289             cn : [
34290                 {
34291                     tag : 'div',
34292                     cls : 'inner',
34293                     cn :[
34294                         {
34295                             tag : 'h3',
34296                             cls : 'roo-headline',
34297                             html : this.headline
34298                         },
34299                         {
34300                             tag : 'p',
34301                             cls : 'roo-content',
34302                             html : this.content
34303                         }
34304                     ]
34305                 }
34306             ]
34307         };
34308         
34309         if(this.icon){
34310             cfg.cn.push({
34311                 tag : 'div',
34312                 cls : 'icon',
34313                 cn :[
34314                     {
34315                         tag : 'i',
34316                         cls : 'ion ' + this.icon
34317                     }
34318                 ]
34319             });
34320         }
34321         
34322         if(this.footer){
34323             var footer = {
34324                 tag : 'a',
34325                 cls : 'small-box-footer',
34326                 href : this.fhref || '#',
34327                 html : this.footer
34328             };
34329             
34330             cfg.cn.push(footer);
34331             
34332         }
34333         
34334         return  cfg;
34335     },
34336
34337     onRender : function(ct,position){
34338         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34339
34340
34341        
34342                 
34343     },
34344
34345     setHeadline: function (value)
34346     {
34347         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34348     },
34349     
34350     setFooter: function (value, href)
34351     {
34352         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34353         
34354         if(href){
34355             this.el.select('a.small-box-footer',true).first().attr('href', href);
34356         }
34357         
34358     },
34359
34360     setContent: function (value)
34361     {
34362         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34363     },
34364
34365     initEvents: function() 
34366     {   
34367         
34368     }
34369     
34370 });
34371
34372  
34373 /*
34374  * - LGPL
34375  *
34376  * TabBox
34377  * 
34378  */
34379 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34380
34381 /**
34382  * @class Roo.bootstrap.dash.TabBox
34383  * @extends Roo.bootstrap.Component
34384  * @children Roo.bootstrap.dash.TabPane
34385  * Bootstrap TabBox class
34386  * @cfg {String} title Title of the TabBox
34387  * @cfg {String} icon Icon of the TabBox
34388  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34389  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34390  * 
34391  * @constructor
34392  * Create a new TabBox
34393  * @param {Object} config The config object
34394  */
34395
34396
34397 Roo.bootstrap.dash.TabBox = function(config){
34398     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34399     this.addEvents({
34400         // raw events
34401         /**
34402          * @event addpane
34403          * When a pane is added
34404          * @param {Roo.bootstrap.dash.TabPane} pane
34405          */
34406         "addpane" : true,
34407         /**
34408          * @event activatepane
34409          * When a pane is activated
34410          * @param {Roo.bootstrap.dash.TabPane} pane
34411          */
34412         "activatepane" : true
34413         
34414          
34415     });
34416     
34417     this.panes = [];
34418 };
34419
34420 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34421
34422     title : '',
34423     icon : false,
34424     showtabs : true,
34425     tabScrollable : false,
34426     
34427     getChildContainer : function()
34428     {
34429         return this.el.select('.tab-content', true).first();
34430     },
34431     
34432     getAutoCreate : function(){
34433         
34434         var header = {
34435             tag: 'li',
34436             cls: 'pull-left header',
34437             html: this.title,
34438             cn : []
34439         };
34440         
34441         if(this.icon){
34442             header.cn.push({
34443                 tag: 'i',
34444                 cls: 'fa ' + this.icon
34445             });
34446         }
34447         
34448         var h = {
34449             tag: 'ul',
34450             cls: 'nav nav-tabs pull-right',
34451             cn: [
34452                 header
34453             ]
34454         };
34455         
34456         if(this.tabScrollable){
34457             h = {
34458                 tag: 'div',
34459                 cls: 'tab-header',
34460                 cn: [
34461                     {
34462                         tag: 'ul',
34463                         cls: 'nav nav-tabs pull-right',
34464                         cn: [
34465                             header
34466                         ]
34467                     }
34468                 ]
34469             };
34470         }
34471         
34472         var cfg = {
34473             tag: 'div',
34474             cls: 'nav-tabs-custom',
34475             cn: [
34476                 h,
34477                 {
34478                     tag: 'div',
34479                     cls: 'tab-content no-padding',
34480                     cn: []
34481                 }
34482             ]
34483         };
34484
34485         return  cfg;
34486     },
34487     initEvents : function()
34488     {
34489         //Roo.log('add add pane handler');
34490         this.on('addpane', this.onAddPane, this);
34491     },
34492      /**
34493      * Updates the box title
34494      * @param {String} html to set the title to.
34495      */
34496     setTitle : function(value)
34497     {
34498         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34499     },
34500     onAddPane : function(pane)
34501     {
34502         this.panes.push(pane);
34503         //Roo.log('addpane');
34504         //Roo.log(pane);
34505         // tabs are rendere left to right..
34506         if(!this.showtabs){
34507             return;
34508         }
34509         
34510         var ctr = this.el.select('.nav-tabs', true).first();
34511          
34512          
34513         var existing = ctr.select('.nav-tab',true);
34514         var qty = existing.getCount();;
34515         
34516         
34517         var tab = ctr.createChild({
34518             tag : 'li',
34519             cls : 'nav-tab' + (qty ? '' : ' active'),
34520             cn : [
34521                 {
34522                     tag : 'a',
34523                     href:'#',
34524                     html : pane.title
34525                 }
34526             ]
34527         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34528         pane.tab = tab;
34529         
34530         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34531         if (!qty) {
34532             pane.el.addClass('active');
34533         }
34534         
34535                 
34536     },
34537     onTabClick : function(ev,un,ob,pane)
34538     {
34539         //Roo.log('tab - prev default');
34540         ev.preventDefault();
34541         
34542         
34543         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34544         pane.tab.addClass('active');
34545         //Roo.log(pane.title);
34546         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34547         // technically we should have a deactivate event.. but maybe add later.
34548         // and it should not de-activate the selected tab...
34549         this.fireEvent('activatepane', pane);
34550         pane.el.addClass('active');
34551         pane.fireEvent('activate');
34552         
34553         
34554     },
34555     
34556     getActivePane : function()
34557     {
34558         var r = false;
34559         Roo.each(this.panes, function(p) {
34560             if(p.el.hasClass('active')){
34561                 r = p;
34562                 return false;
34563             }
34564             
34565             return;
34566         });
34567         
34568         return r;
34569     }
34570     
34571     
34572 });
34573
34574  
34575 /*
34576  * - LGPL
34577  *
34578  * Tab pane
34579  * 
34580  */
34581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34582 /**
34583  * @class Roo.bootstrap.TabPane
34584  * @extends Roo.bootstrap.Component
34585  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34586  * Bootstrap TabPane class
34587  * @cfg {Boolean} active (false | true) Default false
34588  * @cfg {String} title title of panel
34589
34590  * 
34591  * @constructor
34592  * Create a new TabPane
34593  * @param {Object} config The config object
34594  */
34595
34596 Roo.bootstrap.dash.TabPane = function(config){
34597     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34598     
34599     this.addEvents({
34600         // raw events
34601         /**
34602          * @event activate
34603          * When a pane is activated
34604          * @param {Roo.bootstrap.dash.TabPane} pane
34605          */
34606         "activate" : true
34607          
34608     });
34609 };
34610
34611 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34612     
34613     active : false,
34614     title : '',
34615     
34616     // the tabBox that this is attached to.
34617     tab : false,
34618      
34619     getAutoCreate : function() 
34620     {
34621         var cfg = {
34622             tag: 'div',
34623             cls: 'tab-pane'
34624         };
34625         
34626         if(this.active){
34627             cfg.cls += ' active';
34628         }
34629         
34630         return cfg;
34631     },
34632     initEvents  : function()
34633     {
34634         //Roo.log('trigger add pane handler');
34635         this.parent().fireEvent('addpane', this)
34636     },
34637     
34638      /**
34639      * Updates the tab title 
34640      * @param {String} html to set the title to.
34641      */
34642     setTitle: function(str)
34643     {
34644         if (!this.tab) {
34645             return;
34646         }
34647         this.title = str;
34648         this.tab.select('a', true).first().dom.innerHTML = str;
34649         
34650     }
34651     
34652     
34653     
34654 });
34655
34656  
34657
34658
34659  /*
34660  * - LGPL
34661  *
34662  * Tooltip
34663  * 
34664  */
34665
34666 /**
34667  * @class Roo.bootstrap.Tooltip
34668  * Bootstrap Tooltip class
34669  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34670  * to determine which dom element triggers the tooltip.
34671  * 
34672  * It needs to add support for additional attributes like tooltip-position
34673  * 
34674  * @constructor
34675  * Create a new Toolti
34676  * @param {Object} config The config object
34677  */
34678
34679 Roo.bootstrap.Tooltip = function(config){
34680     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34681     
34682     this.alignment = Roo.bootstrap.Tooltip.alignment;
34683     
34684     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34685         this.alignment = config.alignment;
34686     }
34687     
34688 };
34689
34690 Roo.apply(Roo.bootstrap.Tooltip, {
34691     /**
34692      * @function init initialize tooltip monitoring.
34693      * @static
34694      */
34695     currentEl : false,
34696     currentTip : false,
34697     currentRegion : false,
34698     
34699     //  init : delay?
34700     
34701     init : function()
34702     {
34703         Roo.get(document).on('mouseover', this.enter ,this);
34704         Roo.get(document).on('mouseout', this.leave, this);
34705          
34706         
34707         this.currentTip = new Roo.bootstrap.Tooltip();
34708     },
34709     
34710     enter : function(ev)
34711     {
34712         var dom = ev.getTarget();
34713         
34714         //Roo.log(['enter',dom]);
34715         var el = Roo.fly(dom);
34716         if (this.currentEl) {
34717             //Roo.log(dom);
34718             //Roo.log(this.currentEl);
34719             //Roo.log(this.currentEl.contains(dom));
34720             if (this.currentEl == el) {
34721                 return;
34722             }
34723             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34724                 return;
34725             }
34726
34727         }
34728         
34729         if (this.currentTip.el) {
34730             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34731         }    
34732         //Roo.log(ev);
34733         
34734         if(!el || el.dom == document){
34735             return;
34736         }
34737         
34738         var bindEl = el; 
34739         var pel = false;
34740         if (!el.attr('tooltip')) {
34741             pel = el.findParent("[tooltip]");
34742             if (pel) {
34743                 bindEl = Roo.get(pel);
34744             }
34745         }
34746         
34747        
34748         
34749         // you can not look for children, as if el is the body.. then everythign is the child..
34750         if (!pel && !el.attr('tooltip')) { //
34751             if (!el.select("[tooltip]").elements.length) {
34752                 return;
34753             }
34754             // is the mouse over this child...?
34755             bindEl = el.select("[tooltip]").first();
34756             var xy = ev.getXY();
34757             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34758                 //Roo.log("not in region.");
34759                 return;
34760             }
34761             //Roo.log("child element over..");
34762             
34763         }
34764         this.currentEl = el;
34765         this.currentTip.bind(bindEl);
34766         this.currentRegion = Roo.lib.Region.getRegion(dom);
34767         this.currentTip.enter();
34768         
34769     },
34770     leave : function(ev)
34771     {
34772         var dom = ev.getTarget();
34773         //Roo.log(['leave',dom]);
34774         if (!this.currentEl) {
34775             return;
34776         }
34777         
34778         
34779         if (dom != this.currentEl.dom) {
34780             return;
34781         }
34782         var xy = ev.getXY();
34783         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34784             return;
34785         }
34786         // only activate leave if mouse cursor is outside... bounding box..
34787         
34788         
34789         
34790         
34791         if (this.currentTip) {
34792             this.currentTip.leave();
34793         }
34794         //Roo.log('clear currentEl');
34795         this.currentEl = false;
34796         
34797         
34798     },
34799     alignment : {
34800         'left' : ['r-l', [-2,0], 'right'],
34801         'right' : ['l-r', [2,0], 'left'],
34802         'bottom' : ['t-b', [0,2], 'top'],
34803         'top' : [ 'b-t', [0,-2], 'bottom']
34804     }
34805     
34806 });
34807
34808
34809 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34810     
34811     
34812     bindEl : false,
34813     
34814     delay : null, // can be { show : 300 , hide: 500}
34815     
34816     timeout : null,
34817     
34818     hoverState : null, //???
34819     
34820     placement : 'bottom', 
34821     
34822     alignment : false,
34823     
34824     getAutoCreate : function(){
34825     
34826         var cfg = {
34827            cls : 'tooltip',   
34828            role : 'tooltip',
34829            cn : [
34830                 {
34831                     cls : 'tooltip-arrow arrow'
34832                 },
34833                 {
34834                     cls : 'tooltip-inner'
34835                 }
34836            ]
34837         };
34838         
34839         return cfg;
34840     },
34841     bind : function(el)
34842     {
34843         this.bindEl = el;
34844     },
34845     
34846     initEvents : function()
34847     {
34848         this.arrowEl = this.el.select('.arrow', true).first();
34849         this.innerEl = this.el.select('.tooltip-inner', true).first();
34850     },
34851     
34852     enter : function () {
34853        
34854         if (this.timeout != null) {
34855             clearTimeout(this.timeout);
34856         }
34857         
34858         this.hoverState = 'in';
34859          //Roo.log("enter - show");
34860         if (!this.delay || !this.delay.show) {
34861             this.show();
34862             return;
34863         }
34864         var _t = this;
34865         this.timeout = setTimeout(function () {
34866             if (_t.hoverState == 'in') {
34867                 _t.show();
34868             }
34869         }, this.delay.show);
34870     },
34871     leave : function()
34872     {
34873         clearTimeout(this.timeout);
34874     
34875         this.hoverState = 'out';
34876          if (!this.delay || !this.delay.hide) {
34877             this.hide();
34878             return;
34879         }
34880        
34881         var _t = this;
34882         this.timeout = setTimeout(function () {
34883             //Roo.log("leave - timeout");
34884             
34885             if (_t.hoverState == 'out') {
34886                 _t.hide();
34887                 Roo.bootstrap.Tooltip.currentEl = false;
34888             }
34889         }, delay);
34890     },
34891     
34892     show : function (msg)
34893     {
34894         if (!this.el) {
34895             this.render(document.body);
34896         }
34897         // set content.
34898         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34899         
34900         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34901         
34902         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34903         
34904         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34905                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34906
34907         if(this.bindEl.attr('tooltip-class')) {
34908             this.el.addClass(this.bindEl.attr('tooltip-class'));
34909         }
34910         
34911         var placement = typeof this.placement == 'function' ?
34912             this.placement.call(this, this.el, on_el) :
34913             this.placement;
34914         
34915         if(this.bindEl.attr('tooltip-placement')) {
34916             placement = this.bindEl.attr('tooltip-placement');
34917         }
34918             
34919         var autoToken = /\s?auto?\s?/i;
34920         var autoPlace = autoToken.test(placement);
34921         if (autoPlace) {
34922             placement = placement.replace(autoToken, '') || 'top';
34923         }
34924         
34925         //this.el.detach()
34926         //this.el.setXY([0,0]);
34927         this.el.show();
34928         //this.el.dom.style.display='block';
34929         
34930         //this.el.appendTo(on_el);
34931         
34932         var p = this.getPosition();
34933         var box = this.el.getBox();
34934         
34935         if (autoPlace) {
34936             // fixme..
34937         }
34938         
34939         var align = this.alignment[placement];
34940         
34941         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34942         
34943         if(placement == 'top' || placement == 'bottom'){
34944             if(xy[0] < 0){
34945                 placement = 'right';
34946             }
34947             
34948             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34949                 placement = 'left';
34950             }
34951             
34952             var scroll = Roo.select('body', true).first().getScroll();
34953             
34954             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34955                 placement = 'top';
34956             }
34957             
34958             align = this.alignment[placement];
34959             
34960             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34961             
34962         }
34963         
34964         var elems = document.getElementsByTagName('div');
34965         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34966         for (var i = 0; i < elems.length; i++) {
34967           var zindex = Number.parseInt(
34968                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34969                 10
34970           );
34971           if (zindex > highest) {
34972             highest = zindex;
34973           }
34974         }
34975         
34976         
34977         
34978         this.el.dom.style.zIndex = highest;
34979         
34980         this.el.alignTo(this.bindEl, align[0],align[1]);
34981         //var arrow = this.el.select('.arrow',true).first();
34982         //arrow.set(align[2], 
34983         
34984         this.el.addClass(placement);
34985         this.el.addClass("bs-tooltip-"+ placement);
34986         
34987         this.el.addClass('in fade show');
34988         
34989         this.hoverState = null;
34990         
34991         if (this.el.hasClass('fade')) {
34992             // fade it?
34993         }
34994         
34995         
34996         
34997         
34998         
34999     },
35000     hide : function()
35001     {
35002          
35003         if (!this.el) {
35004             return;
35005         }
35006         //this.el.setXY([0,0]);
35007         if(this.bindEl.attr('tooltip-class')) {
35008             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35009         }
35010         this.el.removeClass(['show', 'in']);
35011         //this.el.hide();
35012         
35013     }
35014     
35015 });
35016  
35017
35018  /*
35019  * - LGPL
35020  *
35021  * Location Picker
35022  * 
35023  */
35024
35025 /**
35026  * @class Roo.bootstrap.LocationPicker
35027  * @extends Roo.bootstrap.Component
35028  * Bootstrap LocationPicker class
35029  * @cfg {Number} latitude Position when init default 0
35030  * @cfg {Number} longitude Position when init default 0
35031  * @cfg {Number} zoom default 15
35032  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35033  * @cfg {Boolean} mapTypeControl default false
35034  * @cfg {Boolean} disableDoubleClickZoom default false
35035  * @cfg {Boolean} scrollwheel default true
35036  * @cfg {Boolean} streetViewControl default false
35037  * @cfg {Number} radius default 0
35038  * @cfg {String} locationName
35039  * @cfg {Boolean} draggable default true
35040  * @cfg {Boolean} enableAutocomplete default false
35041  * @cfg {Boolean} enableReverseGeocode default true
35042  * @cfg {String} markerTitle
35043  * 
35044  * @constructor
35045  * Create a new LocationPicker
35046  * @param {Object} config The config object
35047  */
35048
35049
35050 Roo.bootstrap.LocationPicker = function(config){
35051     
35052     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35053     
35054     this.addEvents({
35055         /**
35056          * @event initial
35057          * Fires when the picker initialized.
35058          * @param {Roo.bootstrap.LocationPicker} this
35059          * @param {Google Location} location
35060          */
35061         initial : true,
35062         /**
35063          * @event positionchanged
35064          * Fires when the picker position changed.
35065          * @param {Roo.bootstrap.LocationPicker} this
35066          * @param {Google Location} location
35067          */
35068         positionchanged : true,
35069         /**
35070          * @event resize
35071          * Fires when the map resize.
35072          * @param {Roo.bootstrap.LocationPicker} this
35073          */
35074         resize : true,
35075         /**
35076          * @event show
35077          * Fires when the map show.
35078          * @param {Roo.bootstrap.LocationPicker} this
35079          */
35080         show : true,
35081         /**
35082          * @event hide
35083          * Fires when the map hide.
35084          * @param {Roo.bootstrap.LocationPicker} this
35085          */
35086         hide : true,
35087         /**
35088          * @event mapClick
35089          * Fires when click the map.
35090          * @param {Roo.bootstrap.LocationPicker} this
35091          * @param {Map event} e
35092          */
35093         mapClick : true,
35094         /**
35095          * @event mapRightClick
35096          * Fires when right click the map.
35097          * @param {Roo.bootstrap.LocationPicker} this
35098          * @param {Map event} e
35099          */
35100         mapRightClick : true,
35101         /**
35102          * @event markerClick
35103          * Fires when click the marker.
35104          * @param {Roo.bootstrap.LocationPicker} this
35105          * @param {Map event} e
35106          */
35107         markerClick : true,
35108         /**
35109          * @event markerRightClick
35110          * Fires when right click the marker.
35111          * @param {Roo.bootstrap.LocationPicker} this
35112          * @param {Map event} e
35113          */
35114         markerRightClick : true,
35115         /**
35116          * @event OverlayViewDraw
35117          * Fires when OverlayView Draw
35118          * @param {Roo.bootstrap.LocationPicker} this
35119          */
35120         OverlayViewDraw : true,
35121         /**
35122          * @event OverlayViewOnAdd
35123          * Fires when OverlayView Draw
35124          * @param {Roo.bootstrap.LocationPicker} this
35125          */
35126         OverlayViewOnAdd : true,
35127         /**
35128          * @event OverlayViewOnRemove
35129          * Fires when OverlayView Draw
35130          * @param {Roo.bootstrap.LocationPicker} this
35131          */
35132         OverlayViewOnRemove : true,
35133         /**
35134          * @event OverlayViewShow
35135          * Fires when OverlayView Draw
35136          * @param {Roo.bootstrap.LocationPicker} this
35137          * @param {Pixel} cpx
35138          */
35139         OverlayViewShow : true,
35140         /**
35141          * @event OverlayViewHide
35142          * Fires when OverlayView Draw
35143          * @param {Roo.bootstrap.LocationPicker} this
35144          */
35145         OverlayViewHide : true,
35146         /**
35147          * @event loadexception
35148          * Fires when load google lib failed.
35149          * @param {Roo.bootstrap.LocationPicker} this
35150          */
35151         loadexception : true
35152     });
35153         
35154 };
35155
35156 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35157     
35158     gMapContext: false,
35159     
35160     latitude: 0,
35161     longitude: 0,
35162     zoom: 15,
35163     mapTypeId: false,
35164     mapTypeControl: false,
35165     disableDoubleClickZoom: false,
35166     scrollwheel: true,
35167     streetViewControl: false,
35168     radius: 0,
35169     locationName: '',
35170     draggable: true,
35171     enableAutocomplete: false,
35172     enableReverseGeocode: true,
35173     markerTitle: '',
35174     
35175     getAutoCreate: function()
35176     {
35177
35178         var cfg = {
35179             tag: 'div',
35180             cls: 'roo-location-picker'
35181         };
35182         
35183         return cfg
35184     },
35185     
35186     initEvents: function(ct, position)
35187     {       
35188         if(!this.el.getWidth() || this.isApplied()){
35189             return;
35190         }
35191         
35192         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35193         
35194         this.initial();
35195     },
35196     
35197     initial: function()
35198     {
35199         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35200             this.fireEvent('loadexception', this);
35201             return;
35202         }
35203         
35204         if(!this.mapTypeId){
35205             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35206         }
35207         
35208         this.gMapContext = this.GMapContext();
35209         
35210         this.initOverlayView();
35211         
35212         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35213         
35214         var _this = this;
35215                 
35216         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35217             _this.setPosition(_this.gMapContext.marker.position);
35218         });
35219         
35220         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35221             _this.fireEvent('mapClick', this, event);
35222             
35223         });
35224
35225         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35226             _this.fireEvent('mapRightClick', this, event);
35227             
35228         });
35229         
35230         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35231             _this.fireEvent('markerClick', this, event);
35232             
35233         });
35234
35235         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35236             _this.fireEvent('markerRightClick', this, event);
35237             
35238         });
35239         
35240         this.setPosition(this.gMapContext.location);
35241         
35242         this.fireEvent('initial', this, this.gMapContext.location);
35243     },
35244     
35245     initOverlayView: function()
35246     {
35247         var _this = this;
35248         
35249         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35250             
35251             draw: function()
35252             {
35253                 _this.fireEvent('OverlayViewDraw', _this);
35254             },
35255             
35256             onAdd: function()
35257             {
35258                 _this.fireEvent('OverlayViewOnAdd', _this);
35259             },
35260             
35261             onRemove: function()
35262             {
35263                 _this.fireEvent('OverlayViewOnRemove', _this);
35264             },
35265             
35266             show: function(cpx)
35267             {
35268                 _this.fireEvent('OverlayViewShow', _this, cpx);
35269             },
35270             
35271             hide: function()
35272             {
35273                 _this.fireEvent('OverlayViewHide', _this);
35274             }
35275             
35276         });
35277     },
35278     
35279     fromLatLngToContainerPixel: function(event)
35280     {
35281         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35282     },
35283     
35284     isApplied: function() 
35285     {
35286         return this.getGmapContext() == false ? false : true;
35287     },
35288     
35289     getGmapContext: function() 
35290     {
35291         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35292     },
35293     
35294     GMapContext: function() 
35295     {
35296         var position = new google.maps.LatLng(this.latitude, this.longitude);
35297         
35298         var _map = new google.maps.Map(this.el.dom, {
35299             center: position,
35300             zoom: this.zoom,
35301             mapTypeId: this.mapTypeId,
35302             mapTypeControl: this.mapTypeControl,
35303             disableDoubleClickZoom: this.disableDoubleClickZoom,
35304             scrollwheel: this.scrollwheel,
35305             streetViewControl: this.streetViewControl,
35306             locationName: this.locationName,
35307             draggable: this.draggable,
35308             enableAutocomplete: this.enableAutocomplete,
35309             enableReverseGeocode: this.enableReverseGeocode
35310         });
35311         
35312         var _marker = new google.maps.Marker({
35313             position: position,
35314             map: _map,
35315             title: this.markerTitle,
35316             draggable: this.draggable
35317         });
35318         
35319         return {
35320             map: _map,
35321             marker: _marker,
35322             circle: null,
35323             location: position,
35324             radius: this.radius,
35325             locationName: this.locationName,
35326             addressComponents: {
35327                 formatted_address: null,
35328                 addressLine1: null,
35329                 addressLine2: null,
35330                 streetName: null,
35331                 streetNumber: null,
35332                 city: null,
35333                 district: null,
35334                 state: null,
35335                 stateOrProvince: null
35336             },
35337             settings: this,
35338             domContainer: this.el.dom,
35339             geodecoder: new google.maps.Geocoder()
35340         };
35341     },
35342     
35343     drawCircle: function(center, radius, options) 
35344     {
35345         if (this.gMapContext.circle != null) {
35346             this.gMapContext.circle.setMap(null);
35347         }
35348         if (radius > 0) {
35349             radius *= 1;
35350             options = Roo.apply({}, options, {
35351                 strokeColor: "#0000FF",
35352                 strokeOpacity: .35,
35353                 strokeWeight: 2,
35354                 fillColor: "#0000FF",
35355                 fillOpacity: .2
35356             });
35357             
35358             options.map = this.gMapContext.map;
35359             options.radius = radius;
35360             options.center = center;
35361             this.gMapContext.circle = new google.maps.Circle(options);
35362             return this.gMapContext.circle;
35363         }
35364         
35365         return null;
35366     },
35367     
35368     setPosition: function(location) 
35369     {
35370         this.gMapContext.location = location;
35371         this.gMapContext.marker.setPosition(location);
35372         this.gMapContext.map.panTo(location);
35373         this.drawCircle(location, this.gMapContext.radius, {});
35374         
35375         var _this = this;
35376         
35377         if (this.gMapContext.settings.enableReverseGeocode) {
35378             this.gMapContext.geodecoder.geocode({
35379                 latLng: this.gMapContext.location
35380             }, function(results, status) {
35381                 
35382                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35383                     _this.gMapContext.locationName = results[0].formatted_address;
35384                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35385                     
35386                     _this.fireEvent('positionchanged', this, location);
35387                 }
35388             });
35389             
35390             return;
35391         }
35392         
35393         this.fireEvent('positionchanged', this, location);
35394     },
35395     
35396     resize: function()
35397     {
35398         google.maps.event.trigger(this.gMapContext.map, "resize");
35399         
35400         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35401         
35402         this.fireEvent('resize', this);
35403     },
35404     
35405     setPositionByLatLng: function(latitude, longitude)
35406     {
35407         this.setPosition(new google.maps.LatLng(latitude, longitude));
35408     },
35409     
35410     getCurrentPosition: function() 
35411     {
35412         return {
35413             latitude: this.gMapContext.location.lat(),
35414             longitude: this.gMapContext.location.lng()
35415         };
35416     },
35417     
35418     getAddressName: function() 
35419     {
35420         return this.gMapContext.locationName;
35421     },
35422     
35423     getAddressComponents: function() 
35424     {
35425         return this.gMapContext.addressComponents;
35426     },
35427     
35428     address_component_from_google_geocode: function(address_components) 
35429     {
35430         var result = {};
35431         
35432         for (var i = 0; i < address_components.length; i++) {
35433             var component = address_components[i];
35434             if (component.types.indexOf("postal_code") >= 0) {
35435                 result.postalCode = component.short_name;
35436             } else if (component.types.indexOf("street_number") >= 0) {
35437                 result.streetNumber = component.short_name;
35438             } else if (component.types.indexOf("route") >= 0) {
35439                 result.streetName = component.short_name;
35440             } else if (component.types.indexOf("neighborhood") >= 0) {
35441                 result.city = component.short_name;
35442             } else if (component.types.indexOf("locality") >= 0) {
35443                 result.city = component.short_name;
35444             } else if (component.types.indexOf("sublocality") >= 0) {
35445                 result.district = component.short_name;
35446             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35447                 result.stateOrProvince = component.short_name;
35448             } else if (component.types.indexOf("country") >= 0) {
35449                 result.country = component.short_name;
35450             }
35451         }
35452         
35453         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35454         result.addressLine2 = "";
35455         return result;
35456     },
35457     
35458     setZoomLevel: function(zoom)
35459     {
35460         this.gMapContext.map.setZoom(zoom);
35461     },
35462     
35463     show: function()
35464     {
35465         if(!this.el){
35466             return;
35467         }
35468         
35469         this.el.show();
35470         
35471         this.resize();
35472         
35473         this.fireEvent('show', this);
35474     },
35475     
35476     hide: function()
35477     {
35478         if(!this.el){
35479             return;
35480         }
35481         
35482         this.el.hide();
35483         
35484         this.fireEvent('hide', this);
35485     }
35486     
35487 });
35488
35489 Roo.apply(Roo.bootstrap.LocationPicker, {
35490     
35491     OverlayView : function(map, options)
35492     {
35493         options = options || {};
35494         
35495         this.setMap(map);
35496     }
35497     
35498     
35499 });/**
35500  * @class Roo.bootstrap.Alert
35501  * @extends Roo.bootstrap.Component
35502  * Bootstrap Alert class - shows an alert area box
35503  * eg
35504  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35505   Enter a valid email address
35506 </div>
35507  * @licence LGPL
35508  * @cfg {String} title The title of alert
35509  * @cfg {String} html The content of alert
35510  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35511  * @cfg {String} fa font-awesomeicon
35512  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35513  * @cfg {Boolean} close true to show a x closer
35514  * 
35515  * 
35516  * @constructor
35517  * Create a new alert
35518  * @param {Object} config The config object
35519  */
35520
35521
35522 Roo.bootstrap.Alert = function(config){
35523     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35524     
35525 };
35526
35527 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35528     
35529     title: '',
35530     html: '',
35531     weight: false,
35532     fa: false,
35533     faicon: false, // BC
35534     close : false,
35535     
35536     
35537     getAutoCreate : function()
35538     {
35539         
35540         var cfg = {
35541             tag : 'div',
35542             cls : 'alert',
35543             cn : [
35544                 {
35545                     tag: 'button',
35546                     type :  "button",
35547                     cls: "close",
35548                     html : '×',
35549                     style : this.close ? '' : 'display:none'
35550                 },
35551                 {
35552                     tag : 'i',
35553                     cls : 'roo-alert-icon'
35554                     
35555                 },
35556                 {
35557                     tag : 'b',
35558                     cls : 'roo-alert-title',
35559                     html : this.title
35560                 },
35561                 {
35562                     tag : 'span',
35563                     cls : 'roo-alert-text',
35564                     html : this.html
35565                 }
35566             ]
35567         };
35568         
35569         if(this.faicon){
35570             cfg.cn[0].cls += ' fa ' + this.faicon;
35571         }
35572         if(this.fa){
35573             cfg.cn[0].cls += ' fa ' + this.fa;
35574         }
35575         
35576         if(this.weight){
35577             cfg.cls += ' alert-' + this.weight;
35578         }
35579         
35580         return cfg;
35581     },
35582     
35583     initEvents: function() 
35584     {
35585         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35586         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35587         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35588         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35589         if (this.seconds > 0) {
35590             this.hide.defer(this.seconds, this);
35591         }
35592     },
35593     /**
35594      * Set the Title Message HTML
35595      * @param {String} html
35596      */
35597     setTitle : function(str)
35598     {
35599         this.titleEl.dom.innerHTML = str;
35600     },
35601      
35602      /**
35603      * Set the Body Message HTML
35604      * @param {String} html
35605      */
35606     setHtml : function(str)
35607     {
35608         this.htmlEl.dom.innerHTML = str;
35609     },
35610     /**
35611      * Set the Weight of the alert
35612      * @param {String} (success|info|warning|danger) weight
35613      */
35614     
35615     setWeight : function(weight)
35616     {
35617         if(this.weight){
35618             this.el.removeClass('alert-' + this.weight);
35619         }
35620         
35621         this.weight = weight;
35622         
35623         this.el.addClass('alert-' + this.weight);
35624     },
35625       /**
35626      * Set the Icon of the alert
35627      * @param {String} see fontawsome names (name without the 'fa-' bit)
35628      */
35629     setIcon : function(icon)
35630     {
35631         if(this.faicon){
35632             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35633         }
35634         
35635         this.faicon = icon;
35636         
35637         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35638     },
35639     /**
35640      * Hide the Alert
35641      */
35642     hide: function() 
35643     {
35644         this.el.hide();   
35645     },
35646     /**
35647      * Show the Alert
35648      */
35649     show: function() 
35650     {  
35651         this.el.show();   
35652     }
35653     
35654 });
35655
35656  
35657 /*
35658 * Licence: LGPL
35659 */
35660
35661 /**
35662  * @class Roo.bootstrap.UploadCropbox
35663  * @extends Roo.bootstrap.Component
35664  * Bootstrap UploadCropbox class
35665  * @cfg {String} emptyText show when image has been loaded
35666  * @cfg {String} rotateNotify show when image too small to rotate
35667  * @cfg {Number} errorTimeout default 3000
35668  * @cfg {Number} minWidth default 300
35669  * @cfg {Number} minHeight default 300
35670  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35671  * @cfg {Boolean} isDocument (true|false) default false
35672  * @cfg {String} url action url
35673  * @cfg {String} paramName default 'imageUpload'
35674  * @cfg {String} method default POST
35675  * @cfg {Boolean} loadMask (true|false) default true
35676  * @cfg {Boolean} loadingText default 'Loading...'
35677  * 
35678  * @constructor
35679  * Create a new UploadCropbox
35680  * @param {Object} config The config object
35681  */
35682
35683 Roo.bootstrap.UploadCropbox = function(config){
35684     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35685     
35686     this.addEvents({
35687         /**
35688          * @event beforeselectfile
35689          * Fire before select file
35690          * @param {Roo.bootstrap.UploadCropbox} this
35691          */
35692         "beforeselectfile" : true,
35693         /**
35694          * @event initial
35695          * Fire after initEvent
35696          * @param {Roo.bootstrap.UploadCropbox} this
35697          */
35698         "initial" : true,
35699         /**
35700          * @event crop
35701          * Fire after initEvent
35702          * @param {Roo.bootstrap.UploadCropbox} this
35703          * @param {String} data
35704          */
35705         "crop" : true,
35706         /**
35707          * @event prepare
35708          * Fire when preparing the file data
35709          * @param {Roo.bootstrap.UploadCropbox} this
35710          * @param {Object} file
35711          */
35712         "prepare" : true,
35713         /**
35714          * @event exception
35715          * Fire when get exception
35716          * @param {Roo.bootstrap.UploadCropbox} this
35717          * @param {XMLHttpRequest} xhr
35718          */
35719         "exception" : true,
35720         /**
35721          * @event beforeloadcanvas
35722          * Fire before load the canvas
35723          * @param {Roo.bootstrap.UploadCropbox} this
35724          * @param {String} src
35725          */
35726         "beforeloadcanvas" : true,
35727         /**
35728          * @event trash
35729          * Fire when trash image
35730          * @param {Roo.bootstrap.UploadCropbox} this
35731          */
35732         "trash" : true,
35733         /**
35734          * @event download
35735          * Fire when download the image
35736          * @param {Roo.bootstrap.UploadCropbox} this
35737          */
35738         "download" : true,
35739         /**
35740          * @event footerbuttonclick
35741          * Fire when footerbuttonclick
35742          * @param {Roo.bootstrap.UploadCropbox} this
35743          * @param {String} type
35744          */
35745         "footerbuttonclick" : true,
35746         /**
35747          * @event resize
35748          * Fire when resize
35749          * @param {Roo.bootstrap.UploadCropbox} this
35750          */
35751         "resize" : true,
35752         /**
35753          * @event rotate
35754          * Fire when rotate the image
35755          * @param {Roo.bootstrap.UploadCropbox} this
35756          * @param {String} pos
35757          */
35758         "rotate" : true,
35759         /**
35760          * @event inspect
35761          * Fire when inspect the file
35762          * @param {Roo.bootstrap.UploadCropbox} this
35763          * @param {Object} file
35764          */
35765         "inspect" : true,
35766         /**
35767          * @event upload
35768          * Fire when xhr upload the file
35769          * @param {Roo.bootstrap.UploadCropbox} this
35770          * @param {Object} data
35771          */
35772         "upload" : true,
35773         /**
35774          * @event arrange
35775          * Fire when arrange the file data
35776          * @param {Roo.bootstrap.UploadCropbox} this
35777          * @param {Object} formData
35778          */
35779         "arrange" : true
35780     });
35781     
35782     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35783 };
35784
35785 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35786     
35787     emptyText : 'Click to upload image',
35788     rotateNotify : 'Image is too small to rotate',
35789     errorTimeout : 3000,
35790     scale : 0,
35791     baseScale : 1,
35792     rotate : 0,
35793     dragable : false,
35794     pinching : false,
35795     mouseX : 0,
35796     mouseY : 0,
35797     cropData : false,
35798     minWidth : 300,
35799     minHeight : 300,
35800     file : false,
35801     exif : {},
35802     baseRotate : 1,
35803     cropType : 'image/jpeg',
35804     buttons : false,
35805     canvasLoaded : false,
35806     isDocument : false,
35807     method : 'POST',
35808     paramName : 'imageUpload',
35809     loadMask : true,
35810     loadingText : 'Loading...',
35811     maskEl : false,
35812     
35813     getAutoCreate : function()
35814     {
35815         var cfg = {
35816             tag : 'div',
35817             cls : 'roo-upload-cropbox',
35818             cn : [
35819                 {
35820                     tag : 'input',
35821                     cls : 'roo-upload-cropbox-selector',
35822                     type : 'file'
35823                 },
35824                 {
35825                     tag : 'div',
35826                     cls : 'roo-upload-cropbox-body',
35827                     style : 'cursor:pointer',
35828                     cn : [
35829                         {
35830                             tag : 'div',
35831                             cls : 'roo-upload-cropbox-preview'
35832                         },
35833                         {
35834                             tag : 'div',
35835                             cls : 'roo-upload-cropbox-thumb'
35836                         },
35837                         {
35838                             tag : 'div',
35839                             cls : 'roo-upload-cropbox-empty-notify',
35840                             html : this.emptyText
35841                         },
35842                         {
35843                             tag : 'div',
35844                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35845                             html : this.rotateNotify
35846                         }
35847                     ]
35848                 },
35849                 {
35850                     tag : 'div',
35851                     cls : 'roo-upload-cropbox-footer',
35852                     cn : {
35853                         tag : 'div',
35854                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35855                         cn : []
35856                     }
35857                 }
35858             ]
35859         };
35860         
35861         return cfg;
35862     },
35863     
35864     onRender : function(ct, position)
35865     {
35866         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35867         
35868         if (this.buttons.length) {
35869             
35870             Roo.each(this.buttons, function(bb) {
35871                 
35872                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35873                 
35874                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35875                 
35876             }, this);
35877         }
35878         
35879         if(this.loadMask){
35880             this.maskEl = this.el;
35881         }
35882     },
35883     
35884     initEvents : function()
35885     {
35886         this.urlAPI = (window.createObjectURL && window) || 
35887                                 (window.URL && URL.revokeObjectURL && URL) || 
35888                                 (window.webkitURL && webkitURL);
35889                         
35890         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35891         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35892         
35893         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35894         this.selectorEl.hide();
35895         
35896         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35897         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35898         
35899         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35900         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35901         this.thumbEl.hide();
35902         
35903         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35904         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35905         
35906         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35907         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35908         this.errorEl.hide();
35909         
35910         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35911         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35912         this.footerEl.hide();
35913         
35914         this.setThumbBoxSize();
35915         
35916         this.bind();
35917         
35918         this.resize();
35919         
35920         this.fireEvent('initial', this);
35921     },
35922
35923     bind : function()
35924     {
35925         var _this = this;
35926         
35927         window.addEventListener("resize", function() { _this.resize(); } );
35928         
35929         this.bodyEl.on('click', this.beforeSelectFile, this);
35930         
35931         if(Roo.isTouch){
35932             this.bodyEl.on('touchstart', this.onTouchStart, this);
35933             this.bodyEl.on('touchmove', this.onTouchMove, this);
35934             this.bodyEl.on('touchend', this.onTouchEnd, this);
35935         }
35936         
35937         if(!Roo.isTouch){
35938             this.bodyEl.on('mousedown', this.onMouseDown, this);
35939             this.bodyEl.on('mousemove', this.onMouseMove, this);
35940             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35941             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35942             Roo.get(document).on('mouseup', this.onMouseUp, this);
35943         }
35944         
35945         this.selectorEl.on('change', this.onFileSelected, this);
35946     },
35947     
35948     reset : function()
35949     {    
35950         this.scale = 0;
35951         this.baseScale = 1;
35952         this.rotate = 0;
35953         this.baseRotate = 1;
35954         this.dragable = false;
35955         this.pinching = false;
35956         this.mouseX = 0;
35957         this.mouseY = 0;
35958         this.cropData = false;
35959         this.notifyEl.dom.innerHTML = this.emptyText;
35960         
35961         this.selectorEl.dom.value = '';
35962         
35963     },
35964     
35965     resize : function()
35966     {
35967         if(this.fireEvent('resize', this) != false){
35968             this.setThumbBoxPosition();
35969             this.setCanvasPosition();
35970         }
35971     },
35972     
35973     onFooterButtonClick : function(e, el, o, type)
35974     {
35975         switch (type) {
35976             case 'rotate-left' :
35977                 this.onRotateLeft(e);
35978                 break;
35979             case 'rotate-right' :
35980                 this.onRotateRight(e);
35981                 break;
35982             case 'picture' :
35983                 this.beforeSelectFile(e);
35984                 break;
35985             case 'trash' :
35986                 this.trash(e);
35987                 break;
35988             case 'crop' :
35989                 this.crop(e);
35990                 break;
35991             case 'download' :
35992                 this.download(e);
35993                 break;
35994             default :
35995                 break;
35996         }
35997         
35998         this.fireEvent('footerbuttonclick', this, type);
35999     },
36000     
36001     beforeSelectFile : function(e)
36002     {
36003         e.preventDefault();
36004         
36005         if(this.fireEvent('beforeselectfile', this) != false){
36006             this.selectorEl.dom.click();
36007         }
36008     },
36009     
36010     onFileSelected : function(e)
36011     {
36012         e.preventDefault();
36013         
36014         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36015             return;
36016         }
36017         
36018         var file = this.selectorEl.dom.files[0];
36019         
36020         if(this.fireEvent('inspect', this, file) != false){
36021             this.prepare(file);
36022         }
36023         
36024     },
36025     
36026     trash : function(e)
36027     {
36028         this.fireEvent('trash', this);
36029     },
36030     
36031     download : function(e)
36032     {
36033         this.fireEvent('download', this);
36034     },
36035     
36036     loadCanvas : function(src)
36037     {   
36038         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36039             
36040             this.reset();
36041             
36042             this.imageEl = document.createElement('img');
36043             
36044             var _this = this;
36045             
36046             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36047             
36048             this.imageEl.src = src;
36049         }
36050     },
36051     
36052     onLoadCanvas : function()
36053     {   
36054         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36055         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36056         
36057         this.bodyEl.un('click', this.beforeSelectFile, this);
36058         
36059         this.notifyEl.hide();
36060         this.thumbEl.show();
36061         this.footerEl.show();
36062         
36063         this.baseRotateLevel();
36064         
36065         if(this.isDocument){
36066             this.setThumbBoxSize();
36067         }
36068         
36069         this.setThumbBoxPosition();
36070         
36071         this.baseScaleLevel();
36072         
36073         this.draw();
36074         
36075         this.resize();
36076         
36077         this.canvasLoaded = true;
36078         
36079         if(this.loadMask){
36080             this.maskEl.unmask();
36081         }
36082         
36083     },
36084     
36085     setCanvasPosition : function()
36086     {   
36087         if(!this.canvasEl){
36088             return;
36089         }
36090         
36091         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36092         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36093         
36094         this.previewEl.setLeft(pw);
36095         this.previewEl.setTop(ph);
36096         
36097     },
36098     
36099     onMouseDown : function(e)
36100     {   
36101         e.stopEvent();
36102         
36103         this.dragable = true;
36104         this.pinching = false;
36105         
36106         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36107             this.dragable = false;
36108             return;
36109         }
36110         
36111         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36112         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36113         
36114     },
36115     
36116     onMouseMove : function(e)
36117     {   
36118         e.stopEvent();
36119         
36120         if(!this.canvasLoaded){
36121             return;
36122         }
36123         
36124         if (!this.dragable){
36125             return;
36126         }
36127         
36128         var minX = Math.ceil(this.thumbEl.getLeft(true));
36129         var minY = Math.ceil(this.thumbEl.getTop(true));
36130         
36131         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36132         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36133         
36134         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36135         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36136         
36137         x = x - this.mouseX;
36138         y = y - this.mouseY;
36139         
36140         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36141         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36142         
36143         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36144         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36145         
36146         this.previewEl.setLeft(bgX);
36147         this.previewEl.setTop(bgY);
36148         
36149         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36150         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36151     },
36152     
36153     onMouseUp : function(e)
36154     {   
36155         e.stopEvent();
36156         
36157         this.dragable = false;
36158     },
36159     
36160     onMouseWheel : function(e)
36161     {   
36162         e.stopEvent();
36163         
36164         this.startScale = this.scale;
36165         
36166         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36167         
36168         if(!this.zoomable()){
36169             this.scale = this.startScale;
36170             return;
36171         }
36172         
36173         this.draw();
36174         
36175         return;
36176     },
36177     
36178     zoomable : function()
36179     {
36180         var minScale = this.thumbEl.getWidth() / this.minWidth;
36181         
36182         if(this.minWidth < this.minHeight){
36183             minScale = this.thumbEl.getHeight() / this.minHeight;
36184         }
36185         
36186         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36187         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36188         
36189         if(
36190                 this.isDocument &&
36191                 (this.rotate == 0 || this.rotate == 180) && 
36192                 (
36193                     width > this.imageEl.OriginWidth || 
36194                     height > this.imageEl.OriginHeight ||
36195                     (width < this.minWidth && height < this.minHeight)
36196                 )
36197         ){
36198             return false;
36199         }
36200         
36201         if(
36202                 this.isDocument &&
36203                 (this.rotate == 90 || this.rotate == 270) && 
36204                 (
36205                     width > this.imageEl.OriginWidth || 
36206                     height > this.imageEl.OriginHeight ||
36207                     (width < this.minHeight && height < this.minWidth)
36208                 )
36209         ){
36210             return false;
36211         }
36212         
36213         if(
36214                 !this.isDocument &&
36215                 (this.rotate == 0 || this.rotate == 180) && 
36216                 (
36217                     width < this.minWidth || 
36218                     width > this.imageEl.OriginWidth || 
36219                     height < this.minHeight || 
36220                     height > this.imageEl.OriginHeight
36221                 )
36222         ){
36223             return false;
36224         }
36225         
36226         if(
36227                 !this.isDocument &&
36228                 (this.rotate == 90 || this.rotate == 270) && 
36229                 (
36230                     width < this.minHeight || 
36231                     width > this.imageEl.OriginWidth || 
36232                     height < this.minWidth || 
36233                     height > this.imageEl.OriginHeight
36234                 )
36235         ){
36236             return false;
36237         }
36238         
36239         return true;
36240         
36241     },
36242     
36243     onRotateLeft : function(e)
36244     {   
36245         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36246             
36247             var minScale = this.thumbEl.getWidth() / this.minWidth;
36248             
36249             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36250             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36251             
36252             this.startScale = this.scale;
36253             
36254             while (this.getScaleLevel() < minScale){
36255             
36256                 this.scale = this.scale + 1;
36257                 
36258                 if(!this.zoomable()){
36259                     break;
36260                 }
36261                 
36262                 if(
36263                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36264                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36265                 ){
36266                     continue;
36267                 }
36268                 
36269                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36270
36271                 this.draw();
36272                 
36273                 return;
36274             }
36275             
36276             this.scale = this.startScale;
36277             
36278             this.onRotateFail();
36279             
36280             return false;
36281         }
36282         
36283         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36284
36285         if(this.isDocument){
36286             this.setThumbBoxSize();
36287             this.setThumbBoxPosition();
36288             this.setCanvasPosition();
36289         }
36290         
36291         this.draw();
36292         
36293         this.fireEvent('rotate', this, 'left');
36294         
36295     },
36296     
36297     onRotateRight : function(e)
36298     {
36299         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36300             
36301             var minScale = this.thumbEl.getWidth() / this.minWidth;
36302         
36303             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36304             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36305             
36306             this.startScale = this.scale;
36307             
36308             while (this.getScaleLevel() < minScale){
36309             
36310                 this.scale = this.scale + 1;
36311                 
36312                 if(!this.zoomable()){
36313                     break;
36314                 }
36315                 
36316                 if(
36317                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36318                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36319                 ){
36320                     continue;
36321                 }
36322                 
36323                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36324
36325                 this.draw();
36326                 
36327                 return;
36328             }
36329             
36330             this.scale = this.startScale;
36331             
36332             this.onRotateFail();
36333             
36334             return false;
36335         }
36336         
36337         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36338
36339         if(this.isDocument){
36340             this.setThumbBoxSize();
36341             this.setThumbBoxPosition();
36342             this.setCanvasPosition();
36343         }
36344         
36345         this.draw();
36346         
36347         this.fireEvent('rotate', this, 'right');
36348     },
36349     
36350     onRotateFail : function()
36351     {
36352         this.errorEl.show(true);
36353         
36354         var _this = this;
36355         
36356         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36357     },
36358     
36359     draw : function()
36360     {
36361         this.previewEl.dom.innerHTML = '';
36362         
36363         var canvasEl = document.createElement("canvas");
36364         
36365         var contextEl = canvasEl.getContext("2d");
36366         
36367         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36368         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36369         var center = this.imageEl.OriginWidth / 2;
36370         
36371         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36372             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36373             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36374             center = this.imageEl.OriginHeight / 2;
36375         }
36376         
36377         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36378         
36379         contextEl.translate(center, center);
36380         contextEl.rotate(this.rotate * Math.PI / 180);
36381
36382         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36383         
36384         this.canvasEl = document.createElement("canvas");
36385         
36386         this.contextEl = this.canvasEl.getContext("2d");
36387         
36388         switch (this.rotate) {
36389             case 0 :
36390                 
36391                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36392                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36393                 
36394                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36395                 
36396                 break;
36397             case 90 : 
36398                 
36399                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36400                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36401                 
36402                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36403                     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);
36404                     break;
36405                 }
36406                 
36407                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36408                 
36409                 break;
36410             case 180 :
36411                 
36412                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36413                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36414                 
36415                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36416                     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);
36417                     break;
36418                 }
36419                 
36420                 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);
36421                 
36422                 break;
36423             case 270 :
36424                 
36425                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36426                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36427         
36428                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36429                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36430                     break;
36431                 }
36432                 
36433                 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);
36434                 
36435                 break;
36436             default : 
36437                 break;
36438         }
36439         
36440         this.previewEl.appendChild(this.canvasEl);
36441         
36442         this.setCanvasPosition();
36443     },
36444     
36445     crop : function()
36446     {
36447         if(!this.canvasLoaded){
36448             return;
36449         }
36450         
36451         var imageCanvas = document.createElement("canvas");
36452         
36453         var imageContext = imageCanvas.getContext("2d");
36454         
36455         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36456         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36457         
36458         var center = imageCanvas.width / 2;
36459         
36460         imageContext.translate(center, center);
36461         
36462         imageContext.rotate(this.rotate * Math.PI / 180);
36463         
36464         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36465         
36466         var canvas = document.createElement("canvas");
36467         
36468         var context = canvas.getContext("2d");
36469                 
36470         canvas.width = this.minWidth;
36471         canvas.height = this.minHeight;
36472
36473         switch (this.rotate) {
36474             case 0 :
36475                 
36476                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36477                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36478                 
36479                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36480                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36481                 
36482                 var targetWidth = this.minWidth - 2 * x;
36483                 var targetHeight = this.minHeight - 2 * y;
36484                 
36485                 var scale = 1;
36486                 
36487                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36488                     scale = targetWidth / width;
36489                 }
36490                 
36491                 if(x > 0 && y == 0){
36492                     scale = targetHeight / height;
36493                 }
36494                 
36495                 if(x > 0 && y > 0){
36496                     scale = targetWidth / width;
36497                     
36498                     if(width < height){
36499                         scale = targetHeight / height;
36500                     }
36501                 }
36502                 
36503                 context.scale(scale, scale);
36504                 
36505                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36506                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36507
36508                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36509                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36510
36511                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36512                 
36513                 break;
36514             case 90 : 
36515                 
36516                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36517                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36518                 
36519                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36520                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36521                 
36522                 var targetWidth = this.minWidth - 2 * x;
36523                 var targetHeight = this.minHeight - 2 * y;
36524                 
36525                 var scale = 1;
36526                 
36527                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36528                     scale = targetWidth / width;
36529                 }
36530                 
36531                 if(x > 0 && y == 0){
36532                     scale = targetHeight / height;
36533                 }
36534                 
36535                 if(x > 0 && y > 0){
36536                     scale = targetWidth / width;
36537                     
36538                     if(width < height){
36539                         scale = targetHeight / height;
36540                     }
36541                 }
36542                 
36543                 context.scale(scale, scale);
36544                 
36545                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36546                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36547
36548                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36549                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36550                 
36551                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36552                 
36553                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36554                 
36555                 break;
36556             case 180 :
36557                 
36558                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36559                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36560                 
36561                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36562                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36563                 
36564                 var targetWidth = this.minWidth - 2 * x;
36565                 var targetHeight = this.minHeight - 2 * y;
36566                 
36567                 var scale = 1;
36568                 
36569                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36570                     scale = targetWidth / width;
36571                 }
36572                 
36573                 if(x > 0 && y == 0){
36574                     scale = targetHeight / height;
36575                 }
36576                 
36577                 if(x > 0 && y > 0){
36578                     scale = targetWidth / width;
36579                     
36580                     if(width < height){
36581                         scale = targetHeight / height;
36582                     }
36583                 }
36584                 
36585                 context.scale(scale, scale);
36586                 
36587                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36588                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36589
36590                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36591                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36592
36593                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36594                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36595                 
36596                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36597                 
36598                 break;
36599             case 270 :
36600                 
36601                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36602                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36603                 
36604                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36605                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36606                 
36607                 var targetWidth = this.minWidth - 2 * x;
36608                 var targetHeight = this.minHeight - 2 * y;
36609                 
36610                 var scale = 1;
36611                 
36612                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36613                     scale = targetWidth / width;
36614                 }
36615                 
36616                 if(x > 0 && y == 0){
36617                     scale = targetHeight / height;
36618                 }
36619                 
36620                 if(x > 0 && y > 0){
36621                     scale = targetWidth / width;
36622                     
36623                     if(width < height){
36624                         scale = targetHeight / height;
36625                     }
36626                 }
36627                 
36628                 context.scale(scale, scale);
36629                 
36630                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36631                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36632
36633                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36634                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36635                 
36636                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36637                 
36638                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36639                 
36640                 break;
36641             default : 
36642                 break;
36643         }
36644         
36645         this.cropData = canvas.toDataURL(this.cropType);
36646         
36647         if(this.fireEvent('crop', this, this.cropData) !== false){
36648             this.process(this.file, this.cropData);
36649         }
36650         
36651         return;
36652         
36653     },
36654     
36655     setThumbBoxSize : function()
36656     {
36657         var width, height;
36658         
36659         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36660             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36661             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36662             
36663             this.minWidth = width;
36664             this.minHeight = height;
36665             
36666             if(this.rotate == 90 || this.rotate == 270){
36667                 this.minWidth = height;
36668                 this.minHeight = width;
36669             }
36670         }
36671         
36672         height = 300;
36673         width = Math.ceil(this.minWidth * height / this.minHeight);
36674         
36675         if(this.minWidth > this.minHeight){
36676             width = 300;
36677             height = Math.ceil(this.minHeight * width / this.minWidth);
36678         }
36679         
36680         this.thumbEl.setStyle({
36681             width : width + 'px',
36682             height : height + 'px'
36683         });
36684
36685         return;
36686             
36687     },
36688     
36689     setThumbBoxPosition : function()
36690     {
36691         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36692         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36693         
36694         this.thumbEl.setLeft(x);
36695         this.thumbEl.setTop(y);
36696         
36697     },
36698     
36699     baseRotateLevel : function()
36700     {
36701         this.baseRotate = 1;
36702         
36703         if(
36704                 typeof(this.exif) != 'undefined' &&
36705                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36706                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36707         ){
36708             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36709         }
36710         
36711         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36712         
36713     },
36714     
36715     baseScaleLevel : function()
36716     {
36717         var width, height;
36718         
36719         if(this.isDocument){
36720             
36721             if(this.baseRotate == 6 || this.baseRotate == 8){
36722             
36723                 height = this.thumbEl.getHeight();
36724                 this.baseScale = height / this.imageEl.OriginWidth;
36725
36726                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36727                     width = this.thumbEl.getWidth();
36728                     this.baseScale = width / this.imageEl.OriginHeight;
36729                 }
36730
36731                 return;
36732             }
36733
36734             height = this.thumbEl.getHeight();
36735             this.baseScale = height / this.imageEl.OriginHeight;
36736
36737             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36738                 width = this.thumbEl.getWidth();
36739                 this.baseScale = width / this.imageEl.OriginWidth;
36740             }
36741
36742             return;
36743         }
36744         
36745         if(this.baseRotate == 6 || this.baseRotate == 8){
36746             
36747             width = this.thumbEl.getHeight();
36748             this.baseScale = width / this.imageEl.OriginHeight;
36749             
36750             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36751                 height = this.thumbEl.getWidth();
36752                 this.baseScale = height / this.imageEl.OriginHeight;
36753             }
36754             
36755             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36756                 height = this.thumbEl.getWidth();
36757                 this.baseScale = height / this.imageEl.OriginHeight;
36758                 
36759                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36760                     width = this.thumbEl.getHeight();
36761                     this.baseScale = width / this.imageEl.OriginWidth;
36762                 }
36763             }
36764             
36765             return;
36766         }
36767         
36768         width = this.thumbEl.getWidth();
36769         this.baseScale = width / this.imageEl.OriginWidth;
36770         
36771         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36772             height = this.thumbEl.getHeight();
36773             this.baseScale = height / this.imageEl.OriginHeight;
36774         }
36775         
36776         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36777             
36778             height = this.thumbEl.getHeight();
36779             this.baseScale = height / this.imageEl.OriginHeight;
36780             
36781             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36782                 width = this.thumbEl.getWidth();
36783                 this.baseScale = width / this.imageEl.OriginWidth;
36784             }
36785             
36786         }
36787         
36788         return;
36789     },
36790     
36791     getScaleLevel : function()
36792     {
36793         return this.baseScale * Math.pow(1.1, this.scale);
36794     },
36795     
36796     onTouchStart : function(e)
36797     {
36798         if(!this.canvasLoaded){
36799             this.beforeSelectFile(e);
36800             return;
36801         }
36802         
36803         var touches = e.browserEvent.touches;
36804         
36805         if(!touches){
36806             return;
36807         }
36808         
36809         if(touches.length == 1){
36810             this.onMouseDown(e);
36811             return;
36812         }
36813         
36814         if(touches.length != 2){
36815             return;
36816         }
36817         
36818         var coords = [];
36819         
36820         for(var i = 0, finger; finger = touches[i]; i++){
36821             coords.push(finger.pageX, finger.pageY);
36822         }
36823         
36824         var x = Math.pow(coords[0] - coords[2], 2);
36825         var y = Math.pow(coords[1] - coords[3], 2);
36826         
36827         this.startDistance = Math.sqrt(x + y);
36828         
36829         this.startScale = this.scale;
36830         
36831         this.pinching = true;
36832         this.dragable = false;
36833         
36834     },
36835     
36836     onTouchMove : function(e)
36837     {
36838         if(!this.pinching && !this.dragable){
36839             return;
36840         }
36841         
36842         var touches = e.browserEvent.touches;
36843         
36844         if(!touches){
36845             return;
36846         }
36847         
36848         if(this.dragable){
36849             this.onMouseMove(e);
36850             return;
36851         }
36852         
36853         var coords = [];
36854         
36855         for(var i = 0, finger; finger = touches[i]; i++){
36856             coords.push(finger.pageX, finger.pageY);
36857         }
36858         
36859         var x = Math.pow(coords[0] - coords[2], 2);
36860         var y = Math.pow(coords[1] - coords[3], 2);
36861         
36862         this.endDistance = Math.sqrt(x + y);
36863         
36864         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36865         
36866         if(!this.zoomable()){
36867             this.scale = this.startScale;
36868             return;
36869         }
36870         
36871         this.draw();
36872         
36873     },
36874     
36875     onTouchEnd : function(e)
36876     {
36877         this.pinching = false;
36878         this.dragable = false;
36879         
36880     },
36881     
36882     process : function(file, crop)
36883     {
36884         if(this.loadMask){
36885             this.maskEl.mask(this.loadingText);
36886         }
36887         
36888         this.xhr = new XMLHttpRequest();
36889         
36890         file.xhr = this.xhr;
36891
36892         this.xhr.open(this.method, this.url, true);
36893         
36894         var headers = {
36895             "Accept": "application/json",
36896             "Cache-Control": "no-cache",
36897             "X-Requested-With": "XMLHttpRequest"
36898         };
36899         
36900         for (var headerName in headers) {
36901             var headerValue = headers[headerName];
36902             if (headerValue) {
36903                 this.xhr.setRequestHeader(headerName, headerValue);
36904             }
36905         }
36906         
36907         var _this = this;
36908         
36909         this.xhr.onload = function()
36910         {
36911             _this.xhrOnLoad(_this.xhr);
36912         }
36913         
36914         this.xhr.onerror = function()
36915         {
36916             _this.xhrOnError(_this.xhr);
36917         }
36918         
36919         var formData = new FormData();
36920
36921         formData.append('returnHTML', 'NO');
36922         
36923         if(crop){
36924             formData.append('crop', crop);
36925         }
36926         
36927         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36928             formData.append(this.paramName, file, file.name);
36929         }
36930         
36931         if(typeof(file.filename) != 'undefined'){
36932             formData.append('filename', file.filename);
36933         }
36934         
36935         if(typeof(file.mimetype) != 'undefined'){
36936             formData.append('mimetype', file.mimetype);
36937         }
36938         
36939         if(this.fireEvent('arrange', this, formData) != false){
36940             this.xhr.send(formData);
36941         };
36942     },
36943     
36944     xhrOnLoad : function(xhr)
36945     {
36946         if(this.loadMask){
36947             this.maskEl.unmask();
36948         }
36949         
36950         if (xhr.readyState !== 4) {
36951             this.fireEvent('exception', this, xhr);
36952             return;
36953         }
36954
36955         var response = Roo.decode(xhr.responseText);
36956         
36957         if(!response.success){
36958             this.fireEvent('exception', this, xhr);
36959             return;
36960         }
36961         
36962         var response = Roo.decode(xhr.responseText);
36963         
36964         this.fireEvent('upload', this, response);
36965         
36966     },
36967     
36968     xhrOnError : function()
36969     {
36970         if(this.loadMask){
36971             this.maskEl.unmask();
36972         }
36973         
36974         Roo.log('xhr on error');
36975         
36976         var response = Roo.decode(xhr.responseText);
36977           
36978         Roo.log(response);
36979         
36980     },
36981     
36982     prepare : function(file)
36983     {   
36984         if(this.loadMask){
36985             this.maskEl.mask(this.loadingText);
36986         }
36987         
36988         this.file = false;
36989         this.exif = {};
36990         
36991         if(typeof(file) === 'string'){
36992             this.loadCanvas(file);
36993             return;
36994         }
36995         
36996         if(!file || !this.urlAPI){
36997             return;
36998         }
36999         
37000         this.file = file;
37001         this.cropType = file.type;
37002         
37003         var _this = this;
37004         
37005         if(this.fireEvent('prepare', this, this.file) != false){
37006             
37007             var reader = new FileReader();
37008             
37009             reader.onload = function (e) {
37010                 if (e.target.error) {
37011                     Roo.log(e.target.error);
37012                     return;
37013                 }
37014                 
37015                 var buffer = e.target.result,
37016                     dataView = new DataView(buffer),
37017                     offset = 2,
37018                     maxOffset = dataView.byteLength - 4,
37019                     markerBytes,
37020                     markerLength;
37021                 
37022                 if (dataView.getUint16(0) === 0xffd8) {
37023                     while (offset < maxOffset) {
37024                         markerBytes = dataView.getUint16(offset);
37025                         
37026                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37027                             markerLength = dataView.getUint16(offset + 2) + 2;
37028                             if (offset + markerLength > dataView.byteLength) {
37029                                 Roo.log('Invalid meta data: Invalid segment size.');
37030                                 break;
37031                             }
37032                             
37033                             if(markerBytes == 0xffe1){
37034                                 _this.parseExifData(
37035                                     dataView,
37036                                     offset,
37037                                     markerLength
37038                                 );
37039                             }
37040                             
37041                             offset += markerLength;
37042                             
37043                             continue;
37044                         }
37045                         
37046                         break;
37047                     }
37048                     
37049                 }
37050                 
37051                 var url = _this.urlAPI.createObjectURL(_this.file);
37052                 
37053                 _this.loadCanvas(url);
37054                 
37055                 return;
37056             }
37057             
37058             reader.readAsArrayBuffer(this.file);
37059             
37060         }
37061         
37062     },
37063     
37064     parseExifData : function(dataView, offset, length)
37065     {
37066         var tiffOffset = offset + 10,
37067             littleEndian,
37068             dirOffset;
37069     
37070         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37071             // No Exif data, might be XMP data instead
37072             return;
37073         }
37074         
37075         // Check for the ASCII code for "Exif" (0x45786966):
37076         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37077             // No Exif data, might be XMP data instead
37078             return;
37079         }
37080         if (tiffOffset + 8 > dataView.byteLength) {
37081             Roo.log('Invalid Exif data: Invalid segment size.');
37082             return;
37083         }
37084         // Check for the two null bytes:
37085         if (dataView.getUint16(offset + 8) !== 0x0000) {
37086             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37087             return;
37088         }
37089         // Check the byte alignment:
37090         switch (dataView.getUint16(tiffOffset)) {
37091         case 0x4949:
37092             littleEndian = true;
37093             break;
37094         case 0x4D4D:
37095             littleEndian = false;
37096             break;
37097         default:
37098             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37099             return;
37100         }
37101         // Check for the TIFF tag marker (0x002A):
37102         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37103             Roo.log('Invalid Exif data: Missing TIFF marker.');
37104             return;
37105         }
37106         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37107         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37108         
37109         this.parseExifTags(
37110             dataView,
37111             tiffOffset,
37112             tiffOffset + dirOffset,
37113             littleEndian
37114         );
37115     },
37116     
37117     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37118     {
37119         var tagsNumber,
37120             dirEndOffset,
37121             i;
37122         if (dirOffset + 6 > dataView.byteLength) {
37123             Roo.log('Invalid Exif data: Invalid directory offset.');
37124             return;
37125         }
37126         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37127         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37128         if (dirEndOffset + 4 > dataView.byteLength) {
37129             Roo.log('Invalid Exif data: Invalid directory size.');
37130             return;
37131         }
37132         for (i = 0; i < tagsNumber; i += 1) {
37133             this.parseExifTag(
37134                 dataView,
37135                 tiffOffset,
37136                 dirOffset + 2 + 12 * i, // tag offset
37137                 littleEndian
37138             );
37139         }
37140         // Return the offset to the next directory:
37141         return dataView.getUint32(dirEndOffset, littleEndian);
37142     },
37143     
37144     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37145     {
37146         var tag = dataView.getUint16(offset, littleEndian);
37147         
37148         this.exif[tag] = this.getExifValue(
37149             dataView,
37150             tiffOffset,
37151             offset,
37152             dataView.getUint16(offset + 2, littleEndian), // tag type
37153             dataView.getUint32(offset + 4, littleEndian), // tag length
37154             littleEndian
37155         );
37156     },
37157     
37158     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37159     {
37160         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37161             tagSize,
37162             dataOffset,
37163             values,
37164             i,
37165             str,
37166             c;
37167     
37168         if (!tagType) {
37169             Roo.log('Invalid Exif data: Invalid tag type.');
37170             return;
37171         }
37172         
37173         tagSize = tagType.size * length;
37174         // Determine if the value is contained in the dataOffset bytes,
37175         // or if the value at the dataOffset is a pointer to the actual data:
37176         dataOffset = tagSize > 4 ?
37177                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37178         if (dataOffset + tagSize > dataView.byteLength) {
37179             Roo.log('Invalid Exif data: Invalid data offset.');
37180             return;
37181         }
37182         if (length === 1) {
37183             return tagType.getValue(dataView, dataOffset, littleEndian);
37184         }
37185         values = [];
37186         for (i = 0; i < length; i += 1) {
37187             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37188         }
37189         
37190         if (tagType.ascii) {
37191             str = '';
37192             // Concatenate the chars:
37193             for (i = 0; i < values.length; i += 1) {
37194                 c = values[i];
37195                 // Ignore the terminating NULL byte(s):
37196                 if (c === '\u0000') {
37197                     break;
37198                 }
37199                 str += c;
37200             }
37201             return str;
37202         }
37203         return values;
37204     }
37205     
37206 });
37207
37208 Roo.apply(Roo.bootstrap.UploadCropbox, {
37209     tags : {
37210         'Orientation': 0x0112
37211     },
37212     
37213     Orientation: {
37214             1: 0, //'top-left',
37215 //            2: 'top-right',
37216             3: 180, //'bottom-right',
37217 //            4: 'bottom-left',
37218 //            5: 'left-top',
37219             6: 90, //'right-top',
37220 //            7: 'right-bottom',
37221             8: 270 //'left-bottom'
37222     },
37223     
37224     exifTagTypes : {
37225         // byte, 8-bit unsigned int:
37226         1: {
37227             getValue: function (dataView, dataOffset) {
37228                 return dataView.getUint8(dataOffset);
37229             },
37230             size: 1
37231         },
37232         // ascii, 8-bit byte:
37233         2: {
37234             getValue: function (dataView, dataOffset) {
37235                 return String.fromCharCode(dataView.getUint8(dataOffset));
37236             },
37237             size: 1,
37238             ascii: true
37239         },
37240         // short, 16 bit int:
37241         3: {
37242             getValue: function (dataView, dataOffset, littleEndian) {
37243                 return dataView.getUint16(dataOffset, littleEndian);
37244             },
37245             size: 2
37246         },
37247         // long, 32 bit int:
37248         4: {
37249             getValue: function (dataView, dataOffset, littleEndian) {
37250                 return dataView.getUint32(dataOffset, littleEndian);
37251             },
37252             size: 4
37253         },
37254         // rational = two long values, first is numerator, second is denominator:
37255         5: {
37256             getValue: function (dataView, dataOffset, littleEndian) {
37257                 return dataView.getUint32(dataOffset, littleEndian) /
37258                     dataView.getUint32(dataOffset + 4, littleEndian);
37259             },
37260             size: 8
37261         },
37262         // slong, 32 bit signed int:
37263         9: {
37264             getValue: function (dataView, dataOffset, littleEndian) {
37265                 return dataView.getInt32(dataOffset, littleEndian);
37266             },
37267             size: 4
37268         },
37269         // srational, two slongs, first is numerator, second is denominator:
37270         10: {
37271             getValue: function (dataView, dataOffset, littleEndian) {
37272                 return dataView.getInt32(dataOffset, littleEndian) /
37273                     dataView.getInt32(dataOffset + 4, littleEndian);
37274             },
37275             size: 8
37276         }
37277     },
37278     
37279     footer : {
37280         STANDARD : [
37281             {
37282                 tag : 'div',
37283                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37284                 action : 'rotate-left',
37285                 cn : [
37286                     {
37287                         tag : 'button',
37288                         cls : 'btn btn-default',
37289                         html : '<i class="fa fa-undo"></i>'
37290                     }
37291                 ]
37292             },
37293             {
37294                 tag : 'div',
37295                 cls : 'btn-group roo-upload-cropbox-picture',
37296                 action : 'picture',
37297                 cn : [
37298                     {
37299                         tag : 'button',
37300                         cls : 'btn btn-default',
37301                         html : '<i class="fa fa-picture-o"></i>'
37302                     }
37303                 ]
37304             },
37305             {
37306                 tag : 'div',
37307                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37308                 action : 'rotate-right',
37309                 cn : [
37310                     {
37311                         tag : 'button',
37312                         cls : 'btn btn-default',
37313                         html : '<i class="fa fa-repeat"></i>'
37314                     }
37315                 ]
37316             }
37317         ],
37318         DOCUMENT : [
37319             {
37320                 tag : 'div',
37321                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37322                 action : 'rotate-left',
37323                 cn : [
37324                     {
37325                         tag : 'button',
37326                         cls : 'btn btn-default',
37327                         html : '<i class="fa fa-undo"></i>'
37328                     }
37329                 ]
37330             },
37331             {
37332                 tag : 'div',
37333                 cls : 'btn-group roo-upload-cropbox-download',
37334                 action : 'download',
37335                 cn : [
37336                     {
37337                         tag : 'button',
37338                         cls : 'btn btn-default',
37339                         html : '<i class="fa fa-download"></i>'
37340                     }
37341                 ]
37342             },
37343             {
37344                 tag : 'div',
37345                 cls : 'btn-group roo-upload-cropbox-crop',
37346                 action : 'crop',
37347                 cn : [
37348                     {
37349                         tag : 'button',
37350                         cls : 'btn btn-default',
37351                         html : '<i class="fa fa-crop"></i>'
37352                     }
37353                 ]
37354             },
37355             {
37356                 tag : 'div',
37357                 cls : 'btn-group roo-upload-cropbox-trash',
37358                 action : 'trash',
37359                 cn : [
37360                     {
37361                         tag : 'button',
37362                         cls : 'btn btn-default',
37363                         html : '<i class="fa fa-trash"></i>'
37364                     }
37365                 ]
37366             },
37367             {
37368                 tag : 'div',
37369                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37370                 action : 'rotate-right',
37371                 cn : [
37372                     {
37373                         tag : 'button',
37374                         cls : 'btn btn-default',
37375                         html : '<i class="fa fa-repeat"></i>'
37376                     }
37377                 ]
37378             }
37379         ],
37380         ROTATOR : [
37381             {
37382                 tag : 'div',
37383                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37384                 action : 'rotate-left',
37385                 cn : [
37386                     {
37387                         tag : 'button',
37388                         cls : 'btn btn-default',
37389                         html : '<i class="fa fa-undo"></i>'
37390                     }
37391                 ]
37392             },
37393             {
37394                 tag : 'div',
37395                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37396                 action : 'rotate-right',
37397                 cn : [
37398                     {
37399                         tag : 'button',
37400                         cls : 'btn btn-default',
37401                         html : '<i class="fa fa-repeat"></i>'
37402                     }
37403                 ]
37404             }
37405         ]
37406     }
37407 });
37408
37409 /*
37410 * Licence: LGPL
37411 */
37412
37413 /**
37414  * @class Roo.bootstrap.DocumentManager
37415  * @extends Roo.bootstrap.Component
37416  * Bootstrap DocumentManager class
37417  * @cfg {String} paramName default 'imageUpload'
37418  * @cfg {String} toolTipName default 'filename'
37419  * @cfg {String} method default POST
37420  * @cfg {String} url action url
37421  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37422  * @cfg {Boolean} multiple multiple upload default true
37423  * @cfg {Number} thumbSize default 300
37424  * @cfg {String} fieldLabel
37425  * @cfg {Number} labelWidth default 4
37426  * @cfg {String} labelAlign (left|top) default left
37427  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37428 * @cfg {Number} labellg set the width of label (1-12)
37429  * @cfg {Number} labelmd set the width of label (1-12)
37430  * @cfg {Number} labelsm set the width of label (1-12)
37431  * @cfg {Number} labelxs set the width of label (1-12)
37432  * 
37433  * @constructor
37434  * Create a new DocumentManager
37435  * @param {Object} config The config object
37436  */
37437
37438 Roo.bootstrap.DocumentManager = function(config){
37439     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37440     
37441     this.files = [];
37442     this.delegates = [];
37443     
37444     this.addEvents({
37445         /**
37446          * @event initial
37447          * Fire when initial the DocumentManager
37448          * @param {Roo.bootstrap.DocumentManager} this
37449          */
37450         "initial" : true,
37451         /**
37452          * @event inspect
37453          * inspect selected file
37454          * @param {Roo.bootstrap.DocumentManager} this
37455          * @param {File} file
37456          */
37457         "inspect" : true,
37458         /**
37459          * @event exception
37460          * Fire when xhr load exception
37461          * @param {Roo.bootstrap.DocumentManager} this
37462          * @param {XMLHttpRequest} xhr
37463          */
37464         "exception" : true,
37465         /**
37466          * @event afterupload
37467          * Fire when xhr load exception
37468          * @param {Roo.bootstrap.DocumentManager} this
37469          * @param {XMLHttpRequest} xhr
37470          */
37471         "afterupload" : true,
37472         /**
37473          * @event prepare
37474          * prepare the form data
37475          * @param {Roo.bootstrap.DocumentManager} this
37476          * @param {Object} formData
37477          */
37478         "prepare" : true,
37479         /**
37480          * @event remove
37481          * Fire when remove the file
37482          * @param {Roo.bootstrap.DocumentManager} this
37483          * @param {Object} file
37484          */
37485         "remove" : true,
37486         /**
37487          * @event refresh
37488          * Fire after refresh the file
37489          * @param {Roo.bootstrap.DocumentManager} this
37490          */
37491         "refresh" : true,
37492         /**
37493          * @event click
37494          * Fire after click the image
37495          * @param {Roo.bootstrap.DocumentManager} this
37496          * @param {Object} file
37497          */
37498         "click" : true,
37499         /**
37500          * @event edit
37501          * Fire when upload a image and editable set to true
37502          * @param {Roo.bootstrap.DocumentManager} this
37503          * @param {Object} file
37504          */
37505         "edit" : true,
37506         /**
37507          * @event beforeselectfile
37508          * Fire before select file
37509          * @param {Roo.bootstrap.DocumentManager} this
37510          */
37511         "beforeselectfile" : true,
37512         /**
37513          * @event process
37514          * Fire before process file
37515          * @param {Roo.bootstrap.DocumentManager} this
37516          * @param {Object} file
37517          */
37518         "process" : true,
37519         /**
37520          * @event previewrendered
37521          * Fire when preview rendered
37522          * @param {Roo.bootstrap.DocumentManager} this
37523          * @param {Object} file
37524          */
37525         "previewrendered" : true,
37526         /**
37527          */
37528         "previewResize" : true
37529         
37530     });
37531 };
37532
37533 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37534     
37535     boxes : 0,
37536     inputName : '',
37537     thumbSize : 300,
37538     multiple : true,
37539     files : false,
37540     method : 'POST',
37541     url : '',
37542     paramName : 'imageUpload',
37543     toolTipName : 'filename',
37544     fieldLabel : '',
37545     labelWidth : 4,
37546     labelAlign : 'left',
37547     editable : true,
37548     delegates : false,
37549     xhr : false, 
37550     
37551     labellg : 0,
37552     labelmd : 0,
37553     labelsm : 0,
37554     labelxs : 0,
37555     
37556     getAutoCreate : function()
37557     {   
37558         var managerWidget = {
37559             tag : 'div',
37560             cls : 'roo-document-manager',
37561             cn : [
37562                 {
37563                     tag : 'input',
37564                     cls : 'roo-document-manager-selector',
37565                     type : 'file'
37566                 },
37567                 {
37568                     tag : 'div',
37569                     cls : 'roo-document-manager-uploader',
37570                     cn : [
37571                         {
37572                             tag : 'div',
37573                             cls : 'roo-document-manager-upload-btn',
37574                             html : '<i class="fa fa-plus"></i>'
37575                         }
37576                     ]
37577                     
37578                 }
37579             ]
37580         };
37581         
37582         var content = [
37583             {
37584                 tag : 'div',
37585                 cls : 'column col-md-12',
37586                 cn : managerWidget
37587             }
37588         ];
37589         
37590         if(this.fieldLabel.length){
37591             
37592             content = [
37593                 {
37594                     tag : 'div',
37595                     cls : 'column col-md-12',
37596                     html : this.fieldLabel
37597                 },
37598                 {
37599                     tag : 'div',
37600                     cls : 'column col-md-12',
37601                     cn : managerWidget
37602                 }
37603             ];
37604
37605             if(this.labelAlign == 'left'){
37606                 content = [
37607                     {
37608                         tag : 'div',
37609                         cls : 'column',
37610                         html : this.fieldLabel
37611                     },
37612                     {
37613                         tag : 'div',
37614                         cls : 'column',
37615                         cn : managerWidget
37616                     }
37617                 ];
37618                 
37619                 if(this.labelWidth > 12){
37620                     content[0].style = "width: " + this.labelWidth + 'px';
37621                 }
37622
37623                 if(this.labelWidth < 13 && this.labelmd == 0){
37624                     this.labelmd = this.labelWidth;
37625                 }
37626
37627                 if(this.labellg > 0){
37628                     content[0].cls += ' col-lg-' + this.labellg;
37629                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37630                 }
37631
37632                 if(this.labelmd > 0){
37633                     content[0].cls += ' col-md-' + this.labelmd;
37634                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37635                 }
37636
37637                 if(this.labelsm > 0){
37638                     content[0].cls += ' col-sm-' + this.labelsm;
37639                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37640                 }
37641
37642                 if(this.labelxs > 0){
37643                     content[0].cls += ' col-xs-' + this.labelxs;
37644                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37645                 }
37646                 
37647             }
37648         }
37649         
37650         var cfg = {
37651             tag : 'div',
37652             cls : 'row clearfix',
37653             cn : content
37654         };
37655         
37656         return cfg;
37657         
37658     },
37659     
37660     initEvents : function()
37661     {
37662         this.managerEl = this.el.select('.roo-document-manager', true).first();
37663         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37664         
37665         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37666         this.selectorEl.hide();
37667         
37668         if(this.multiple){
37669             this.selectorEl.attr('multiple', 'multiple');
37670         }
37671         
37672         this.selectorEl.on('change', this.onFileSelected, this);
37673         
37674         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37675         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37676         
37677         this.uploader.on('click', this.onUploaderClick, this);
37678         
37679         this.renderProgressDialog();
37680         
37681         var _this = this;
37682         
37683         window.addEventListener("resize", function() { _this.refresh(); } );
37684         
37685         this.fireEvent('initial', this);
37686     },
37687     
37688     renderProgressDialog : function()
37689     {
37690         var _this = this;
37691         
37692         this.progressDialog = new Roo.bootstrap.Modal({
37693             cls : 'roo-document-manager-progress-dialog',
37694             allow_close : false,
37695             animate : false,
37696             title : '',
37697             buttons : [
37698                 {
37699                     name  :'cancel',
37700                     weight : 'danger',
37701                     html : 'Cancel'
37702                 }
37703             ], 
37704             listeners : { 
37705                 btnclick : function() {
37706                     _this.uploadCancel();
37707                     this.hide();
37708                 }
37709             }
37710         });
37711          
37712         this.progressDialog.render(Roo.get(document.body));
37713          
37714         this.progress = new Roo.bootstrap.Progress({
37715             cls : 'roo-document-manager-progress',
37716             active : true,
37717             striped : true
37718         });
37719         
37720         this.progress.render(this.progressDialog.getChildContainer());
37721         
37722         this.progressBar = new Roo.bootstrap.ProgressBar({
37723             cls : 'roo-document-manager-progress-bar',
37724             aria_valuenow : 0,
37725             aria_valuemin : 0,
37726             aria_valuemax : 12,
37727             panel : 'success'
37728         });
37729         
37730         this.progressBar.render(this.progress.getChildContainer());
37731     },
37732     
37733     onUploaderClick : function(e)
37734     {
37735         e.preventDefault();
37736      
37737         if(this.fireEvent('beforeselectfile', this) != false){
37738             this.selectorEl.dom.click();
37739         }
37740         
37741     },
37742     
37743     onFileSelected : function(e)
37744     {
37745         e.preventDefault();
37746         
37747         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37748             return;
37749         }
37750         
37751         Roo.each(this.selectorEl.dom.files, function(file){
37752             if(this.fireEvent('inspect', this, file) != false){
37753                 this.files.push(file);
37754             }
37755         }, this);
37756         
37757         this.queue();
37758         
37759     },
37760     
37761     queue : function()
37762     {
37763         this.selectorEl.dom.value = '';
37764         
37765         if(!this.files || !this.files.length){
37766             return;
37767         }
37768         
37769         if(this.boxes > 0 && this.files.length > this.boxes){
37770             this.files = this.files.slice(0, this.boxes);
37771         }
37772         
37773         this.uploader.show();
37774         
37775         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37776             this.uploader.hide();
37777         }
37778         
37779         var _this = this;
37780         
37781         var files = [];
37782         
37783         var docs = [];
37784         
37785         Roo.each(this.files, function(file){
37786             
37787             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37788                 var f = this.renderPreview(file);
37789                 files.push(f);
37790                 return;
37791             }
37792             
37793             if(file.type.indexOf('image') != -1){
37794                 this.delegates.push(
37795                     (function(){
37796                         _this.process(file);
37797                     }).createDelegate(this)
37798                 );
37799         
37800                 return;
37801             }
37802             
37803             docs.push(
37804                 (function(){
37805                     _this.process(file);
37806                 }).createDelegate(this)
37807             );
37808             
37809         }, this);
37810         
37811         this.files = files;
37812         
37813         this.delegates = this.delegates.concat(docs);
37814         
37815         if(!this.delegates.length){
37816             this.refresh();
37817             return;
37818         }
37819         
37820         this.progressBar.aria_valuemax = this.delegates.length;
37821         
37822         this.arrange();
37823         
37824         return;
37825     },
37826     
37827     arrange : function()
37828     {
37829         if(!this.delegates.length){
37830             this.progressDialog.hide();
37831             this.refresh();
37832             return;
37833         }
37834         
37835         var delegate = this.delegates.shift();
37836         
37837         this.progressDialog.show();
37838         
37839         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37840         
37841         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37842         
37843         delegate();
37844     },
37845     
37846     refresh : function()
37847     {
37848         this.uploader.show();
37849         
37850         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37851             this.uploader.hide();
37852         }
37853         
37854         Roo.isTouch ? this.closable(false) : this.closable(true);
37855         
37856         this.fireEvent('refresh', this);
37857     },
37858     
37859     onRemove : function(e, el, o)
37860     {
37861         e.preventDefault();
37862         
37863         this.fireEvent('remove', this, o);
37864         
37865     },
37866     
37867     remove : function(o)
37868     {
37869         var files = [];
37870         
37871         Roo.each(this.files, function(file){
37872             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37873                 files.push(file);
37874                 return;
37875             }
37876
37877             o.target.remove();
37878
37879         }, this);
37880         
37881         this.files = files;
37882         
37883         this.refresh();
37884     },
37885     
37886     clear : function()
37887     {
37888         Roo.each(this.files, function(file){
37889             if(!file.target){
37890                 return;
37891             }
37892             
37893             file.target.remove();
37894
37895         }, this);
37896         
37897         this.files = [];
37898         
37899         this.refresh();
37900     },
37901     
37902     onClick : function(e, el, o)
37903     {
37904         e.preventDefault();
37905         
37906         this.fireEvent('click', this, o);
37907         
37908     },
37909     
37910     closable : function(closable)
37911     {
37912         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37913             
37914             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37915             
37916             if(closable){
37917                 el.show();
37918                 return;
37919             }
37920             
37921             el.hide();
37922             
37923         }, this);
37924     },
37925     
37926     xhrOnLoad : function(xhr)
37927     {
37928         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37929             el.remove();
37930         }, this);
37931         
37932         if (xhr.readyState !== 4) {
37933             this.arrange();
37934             this.fireEvent('exception', this, xhr);
37935             return;
37936         }
37937
37938         var response = Roo.decode(xhr.responseText);
37939         
37940         if(!response.success){
37941             this.arrange();
37942             this.fireEvent('exception', this, xhr);
37943             return;
37944         }
37945         
37946         var file = this.renderPreview(response.data);
37947         
37948         this.files.push(file);
37949         
37950         this.arrange();
37951         
37952         this.fireEvent('afterupload', this, xhr);
37953         
37954     },
37955     
37956     xhrOnError : function(xhr)
37957     {
37958         Roo.log('xhr on error');
37959         
37960         var response = Roo.decode(xhr.responseText);
37961           
37962         Roo.log(response);
37963         
37964         this.arrange();
37965     },
37966     
37967     process : function(file)
37968     {
37969         if(this.fireEvent('process', this, file) !== false){
37970             if(this.editable && file.type.indexOf('image') != -1){
37971                 this.fireEvent('edit', this, file);
37972                 return;
37973             }
37974
37975             this.uploadStart(file, false);
37976
37977             return;
37978         }
37979         
37980     },
37981     
37982     uploadStart : function(file, crop)
37983     {
37984         this.xhr = new XMLHttpRequest();
37985         
37986         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37987             this.arrange();
37988             return;
37989         }
37990         
37991         file.xhr = this.xhr;
37992             
37993         this.managerEl.createChild({
37994             tag : 'div',
37995             cls : 'roo-document-manager-loading',
37996             cn : [
37997                 {
37998                     tag : 'div',
37999                     tooltip : file.name,
38000                     cls : 'roo-document-manager-thumb',
38001                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38002                 }
38003             ]
38004
38005         });
38006
38007         this.xhr.open(this.method, this.url, true);
38008         
38009         var headers = {
38010             "Accept": "application/json",
38011             "Cache-Control": "no-cache",
38012             "X-Requested-With": "XMLHttpRequest"
38013         };
38014         
38015         for (var headerName in headers) {
38016             var headerValue = headers[headerName];
38017             if (headerValue) {
38018                 this.xhr.setRequestHeader(headerName, headerValue);
38019             }
38020         }
38021         
38022         var _this = this;
38023         
38024         this.xhr.onload = function()
38025         {
38026             _this.xhrOnLoad(_this.xhr);
38027         }
38028         
38029         this.xhr.onerror = function()
38030         {
38031             _this.xhrOnError(_this.xhr);
38032         }
38033         
38034         var formData = new FormData();
38035
38036         formData.append('returnHTML', 'NO');
38037         
38038         if(crop){
38039             formData.append('crop', crop);
38040         }
38041         
38042         formData.append(this.paramName, file, file.name);
38043         
38044         var options = {
38045             file : file, 
38046             manually : false
38047         };
38048         
38049         if(this.fireEvent('prepare', this, formData, options) != false){
38050             
38051             if(options.manually){
38052                 return;
38053             }
38054             
38055             this.xhr.send(formData);
38056             return;
38057         };
38058         
38059         this.uploadCancel();
38060     },
38061     
38062     uploadCancel : function()
38063     {
38064         if (this.xhr) {
38065             this.xhr.abort();
38066         }
38067         
38068         this.delegates = [];
38069         
38070         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38071             el.remove();
38072         }, this);
38073         
38074         this.arrange();
38075     },
38076     
38077     renderPreview : function(file)
38078     {
38079         if(typeof(file.target) != 'undefined' && file.target){
38080             return file;
38081         }
38082         
38083         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38084         
38085         var previewEl = this.managerEl.createChild({
38086             tag : 'div',
38087             cls : 'roo-document-manager-preview',
38088             cn : [
38089                 {
38090                     tag : 'div',
38091                     tooltip : file[this.toolTipName],
38092                     cls : 'roo-document-manager-thumb',
38093                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38094                 },
38095                 {
38096                     tag : 'button',
38097                     cls : 'close',
38098                     html : '<i class="fa fa-times-circle"></i>'
38099                 }
38100             ]
38101         });
38102
38103         var close = previewEl.select('button.close', true).first();
38104
38105         close.on('click', this.onRemove, this, file);
38106
38107         file.target = previewEl;
38108
38109         var image = previewEl.select('img', true).first();
38110         
38111         var _this = this;
38112         
38113         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38114         
38115         image.on('click', this.onClick, this, file);
38116         
38117         this.fireEvent('previewrendered', this, file);
38118         
38119         return file;
38120         
38121     },
38122     
38123     onPreviewLoad : function(file, image)
38124     {
38125         if(typeof(file.target) == 'undefined' || !file.target){
38126             return;
38127         }
38128         
38129         var width = image.dom.naturalWidth || image.dom.width;
38130         var height = image.dom.naturalHeight || image.dom.height;
38131         
38132         if(!this.previewResize) {
38133             return;
38134         }
38135         
38136         if(width > height){
38137             file.target.addClass('wide');
38138             return;
38139         }
38140         
38141         file.target.addClass('tall');
38142         return;
38143         
38144     },
38145     
38146     uploadFromSource : function(file, crop)
38147     {
38148         this.xhr = new XMLHttpRequest();
38149         
38150         this.managerEl.createChild({
38151             tag : 'div',
38152             cls : 'roo-document-manager-loading',
38153             cn : [
38154                 {
38155                     tag : 'div',
38156                     tooltip : file.name,
38157                     cls : 'roo-document-manager-thumb',
38158                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38159                 }
38160             ]
38161
38162         });
38163
38164         this.xhr.open(this.method, this.url, true);
38165         
38166         var headers = {
38167             "Accept": "application/json",
38168             "Cache-Control": "no-cache",
38169             "X-Requested-With": "XMLHttpRequest"
38170         };
38171         
38172         for (var headerName in headers) {
38173             var headerValue = headers[headerName];
38174             if (headerValue) {
38175                 this.xhr.setRequestHeader(headerName, headerValue);
38176             }
38177         }
38178         
38179         var _this = this;
38180         
38181         this.xhr.onload = function()
38182         {
38183             _this.xhrOnLoad(_this.xhr);
38184         }
38185         
38186         this.xhr.onerror = function()
38187         {
38188             _this.xhrOnError(_this.xhr);
38189         }
38190         
38191         var formData = new FormData();
38192
38193         formData.append('returnHTML', 'NO');
38194         
38195         formData.append('crop', crop);
38196         
38197         if(typeof(file.filename) != 'undefined'){
38198             formData.append('filename', file.filename);
38199         }
38200         
38201         if(typeof(file.mimetype) != 'undefined'){
38202             formData.append('mimetype', file.mimetype);
38203         }
38204         
38205         Roo.log(formData);
38206         
38207         if(this.fireEvent('prepare', this, formData) != false){
38208             this.xhr.send(formData);
38209         };
38210     }
38211 });
38212
38213 /*
38214 * Licence: LGPL
38215 */
38216
38217 /**
38218  * @class Roo.bootstrap.DocumentViewer
38219  * @extends Roo.bootstrap.Component
38220  * Bootstrap DocumentViewer class
38221  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38222  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38223  * 
38224  * @constructor
38225  * Create a new DocumentViewer
38226  * @param {Object} config The config object
38227  */
38228
38229 Roo.bootstrap.DocumentViewer = function(config){
38230     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38231     
38232     this.addEvents({
38233         /**
38234          * @event initial
38235          * Fire after initEvent
38236          * @param {Roo.bootstrap.DocumentViewer} this
38237          */
38238         "initial" : true,
38239         /**
38240          * @event click
38241          * Fire after click
38242          * @param {Roo.bootstrap.DocumentViewer} this
38243          */
38244         "click" : true,
38245         /**
38246          * @event download
38247          * Fire after download button
38248          * @param {Roo.bootstrap.DocumentViewer} this
38249          */
38250         "download" : true,
38251         /**
38252          * @event trash
38253          * Fire after trash button
38254          * @param {Roo.bootstrap.DocumentViewer} this
38255          */
38256         "trash" : true
38257         
38258     });
38259 };
38260
38261 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38262     
38263     showDownload : true,
38264     
38265     showTrash : true,
38266     
38267     getAutoCreate : function()
38268     {
38269         var cfg = {
38270             tag : 'div',
38271             cls : 'roo-document-viewer',
38272             cn : [
38273                 {
38274                     tag : 'div',
38275                     cls : 'roo-document-viewer-body',
38276                     cn : [
38277                         {
38278                             tag : 'div',
38279                             cls : 'roo-document-viewer-thumb',
38280                             cn : [
38281                                 {
38282                                     tag : 'img',
38283                                     cls : 'roo-document-viewer-image'
38284                                 }
38285                             ]
38286                         }
38287                     ]
38288                 },
38289                 {
38290                     tag : 'div',
38291                     cls : 'roo-document-viewer-footer',
38292                     cn : {
38293                         tag : 'div',
38294                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38295                         cn : [
38296                             {
38297                                 tag : 'div',
38298                                 cls : 'btn-group roo-document-viewer-download',
38299                                 cn : [
38300                                     {
38301                                         tag : 'button',
38302                                         cls : 'btn btn-default',
38303                                         html : '<i class="fa fa-download"></i>'
38304                                     }
38305                                 ]
38306                             },
38307                             {
38308                                 tag : 'div',
38309                                 cls : 'btn-group roo-document-viewer-trash',
38310                                 cn : [
38311                                     {
38312                                         tag : 'button',
38313                                         cls : 'btn btn-default',
38314                                         html : '<i class="fa fa-trash"></i>'
38315                                     }
38316                                 ]
38317                             }
38318                         ]
38319                     }
38320                 }
38321             ]
38322         };
38323         
38324         return cfg;
38325     },
38326     
38327     initEvents : function()
38328     {
38329         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38330         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38331         
38332         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38333         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38334         
38335         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38336         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38337         
38338         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38339         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38340         
38341         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38342         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38343         
38344         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38345         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38346         
38347         this.bodyEl.on('click', this.onClick, this);
38348         this.downloadBtn.on('click', this.onDownload, this);
38349         this.trashBtn.on('click', this.onTrash, this);
38350         
38351         this.downloadBtn.hide();
38352         this.trashBtn.hide();
38353         
38354         if(this.showDownload){
38355             this.downloadBtn.show();
38356         }
38357         
38358         if(this.showTrash){
38359             this.trashBtn.show();
38360         }
38361         
38362         if(!this.showDownload && !this.showTrash) {
38363             this.footerEl.hide();
38364         }
38365         
38366     },
38367     
38368     initial : function()
38369     {
38370         this.fireEvent('initial', this);
38371         
38372     },
38373     
38374     onClick : function(e)
38375     {
38376         e.preventDefault();
38377         
38378         this.fireEvent('click', this);
38379     },
38380     
38381     onDownload : function(e)
38382     {
38383         e.preventDefault();
38384         
38385         this.fireEvent('download', this);
38386     },
38387     
38388     onTrash : function(e)
38389     {
38390         e.preventDefault();
38391         
38392         this.fireEvent('trash', this);
38393     }
38394     
38395 });
38396 /*
38397  * - LGPL
38398  *
38399  * FieldLabel
38400  * 
38401  */
38402
38403 /**
38404  * @class Roo.bootstrap.form.FieldLabel
38405  * @extends Roo.bootstrap.Component
38406  * Bootstrap FieldLabel class
38407  * @cfg {String} html contents of the element
38408  * @cfg {String} tag tag of the element default label
38409  * @cfg {String} cls class of the element
38410  * @cfg {String} target label target 
38411  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38412  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38413  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38414  * @cfg {String} iconTooltip default "This field is required"
38415  * @cfg {String} indicatorpos (left|right) default left
38416  * 
38417  * @constructor
38418  * Create a new FieldLabel
38419  * @param {Object} config The config object
38420  */
38421
38422 Roo.bootstrap.form.FieldLabel = function(config){
38423     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38424     
38425     this.addEvents({
38426             /**
38427              * @event invalid
38428              * Fires after the field has been marked as invalid.
38429              * @param {Roo.form.FieldLabel} this
38430              * @param {String} msg The validation message
38431              */
38432             invalid : true,
38433             /**
38434              * @event valid
38435              * Fires after the field has been validated with no errors.
38436              * @param {Roo.form.FieldLabel} this
38437              */
38438             valid : true
38439         });
38440 };
38441
38442 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38443     
38444     tag: 'label',
38445     cls: '',
38446     html: '',
38447     target: '',
38448     allowBlank : true,
38449     invalidClass : 'has-warning',
38450     validClass : 'has-success',
38451     iconTooltip : 'This field is required',
38452     indicatorpos : 'left',
38453     
38454     getAutoCreate : function(){
38455         
38456         var cls = "";
38457         if (!this.allowBlank) {
38458             cls  = "visible";
38459         }
38460         
38461         var cfg = {
38462             tag : this.tag,
38463             cls : 'roo-bootstrap-field-label ' + this.cls,
38464             for : this.target,
38465             cn : [
38466                 {
38467                     tag : 'i',
38468                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38469                     tooltip : this.iconTooltip
38470                 },
38471                 {
38472                     tag : 'span',
38473                     html : this.html
38474                 }
38475             ] 
38476         };
38477         
38478         if(this.indicatorpos == 'right'){
38479             var cfg = {
38480                 tag : this.tag,
38481                 cls : 'roo-bootstrap-field-label ' + this.cls,
38482                 for : this.target,
38483                 cn : [
38484                     {
38485                         tag : 'span',
38486                         html : this.html
38487                     },
38488                     {
38489                         tag : 'i',
38490                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38491                         tooltip : this.iconTooltip
38492                     }
38493                 ] 
38494             };
38495         }
38496         
38497         return cfg;
38498     },
38499     
38500     initEvents: function() 
38501     {
38502         Roo.bootstrap.Element.superclass.initEvents.call(this);
38503         
38504         this.indicator = this.indicatorEl();
38505         
38506         if(this.indicator){
38507             this.indicator.removeClass('visible');
38508             this.indicator.addClass('invisible');
38509         }
38510         
38511         Roo.bootstrap.form.FieldLabel.register(this);
38512     },
38513     
38514     indicatorEl : function()
38515     {
38516         var indicator = this.el.select('i.roo-required-indicator',true).first();
38517         
38518         if(!indicator){
38519             return false;
38520         }
38521         
38522         return indicator;
38523         
38524     },
38525     
38526     /**
38527      * Mark this field as valid
38528      */
38529     markValid : function()
38530     {
38531         if(this.indicator){
38532             this.indicator.removeClass('visible');
38533             this.indicator.addClass('invisible');
38534         }
38535         if (Roo.bootstrap.version == 3) {
38536             this.el.removeClass(this.invalidClass);
38537             this.el.addClass(this.validClass);
38538         } else {
38539             this.el.removeClass('is-invalid');
38540             this.el.addClass('is-valid');
38541         }
38542         
38543         
38544         this.fireEvent('valid', this);
38545     },
38546     
38547     /**
38548      * Mark this field as invalid
38549      * @param {String} msg The validation message
38550      */
38551     markInvalid : function(msg)
38552     {
38553         if(this.indicator){
38554             this.indicator.removeClass('invisible');
38555             this.indicator.addClass('visible');
38556         }
38557           if (Roo.bootstrap.version == 3) {
38558             this.el.removeClass(this.validClass);
38559             this.el.addClass(this.invalidClass);
38560         } else {
38561             this.el.removeClass('is-valid');
38562             this.el.addClass('is-invalid');
38563         }
38564         
38565         
38566         this.fireEvent('invalid', this, msg);
38567     }
38568     
38569    
38570 });
38571
38572 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38573     
38574     groups: {},
38575     
38576      /**
38577     * register a FieldLabel Group
38578     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38579     */
38580     register : function(label)
38581     {
38582         if(this.groups.hasOwnProperty(label.target)){
38583             return;
38584         }
38585      
38586         this.groups[label.target] = label;
38587         
38588     },
38589     /**
38590     * fetch a FieldLabel Group based on the target
38591     * @param {string} target
38592     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38593     */
38594     get: function(target) {
38595         if (typeof(this.groups[target]) == 'undefined') {
38596             return false;
38597         }
38598         
38599         return this.groups[target] ;
38600     }
38601 });
38602
38603  
38604
38605  /*
38606  * - LGPL
38607  *
38608  * page DateSplitField.
38609  * 
38610  */
38611
38612
38613 /**
38614  * @class Roo.bootstrap.form.DateSplitField
38615  * @extends Roo.bootstrap.Component
38616  * Bootstrap DateSplitField class
38617  * @cfg {string} fieldLabel - the label associated
38618  * @cfg {Number} labelWidth set the width of label (0-12)
38619  * @cfg {String} labelAlign (top|left)
38620  * @cfg {Boolean} dayAllowBlank (true|false) default false
38621  * @cfg {Boolean} monthAllowBlank (true|false) default false
38622  * @cfg {Boolean} yearAllowBlank (true|false) default false
38623  * @cfg {string} dayPlaceholder 
38624  * @cfg {string} monthPlaceholder
38625  * @cfg {string} yearPlaceholder
38626  * @cfg {string} dayFormat default 'd'
38627  * @cfg {string} monthFormat default 'm'
38628  * @cfg {string} yearFormat default 'Y'
38629  * @cfg {Number} labellg set the width of label (1-12)
38630  * @cfg {Number} labelmd set the width of label (1-12)
38631  * @cfg {Number} labelsm set the width of label (1-12)
38632  * @cfg {Number} labelxs set the width of label (1-12)
38633
38634  *     
38635  * @constructor
38636  * Create a new DateSplitField
38637  * @param {Object} config The config object
38638  */
38639
38640 Roo.bootstrap.form.DateSplitField = function(config){
38641     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38642     
38643     this.addEvents({
38644         // raw events
38645          /**
38646          * @event years
38647          * getting the data of years
38648          * @param {Roo.bootstrap.form.DateSplitField} this
38649          * @param {Object} years
38650          */
38651         "years" : true,
38652         /**
38653          * @event days
38654          * getting the data of days
38655          * @param {Roo.bootstrap.form.DateSplitField} this
38656          * @param {Object} days
38657          */
38658         "days" : true,
38659         /**
38660          * @event invalid
38661          * Fires after the field has been marked as invalid.
38662          * @param {Roo.form.Field} this
38663          * @param {String} msg The validation message
38664          */
38665         invalid : true,
38666        /**
38667          * @event valid
38668          * Fires after the field has been validated with no errors.
38669          * @param {Roo.form.Field} this
38670          */
38671         valid : true
38672     });
38673 };
38674
38675 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38676     
38677     fieldLabel : '',
38678     labelAlign : 'top',
38679     labelWidth : 3,
38680     dayAllowBlank : false,
38681     monthAllowBlank : false,
38682     yearAllowBlank : false,
38683     dayPlaceholder : '',
38684     monthPlaceholder : '',
38685     yearPlaceholder : '',
38686     dayFormat : 'd',
38687     monthFormat : 'm',
38688     yearFormat : 'Y',
38689     isFormField : true,
38690     labellg : 0,
38691     labelmd : 0,
38692     labelsm : 0,
38693     labelxs : 0,
38694     
38695     getAutoCreate : function()
38696     {
38697         var cfg = {
38698             tag : 'div',
38699             cls : 'row roo-date-split-field-group',
38700             cn : [
38701                 {
38702                     tag : 'input',
38703                     type : 'hidden',
38704                     cls : 'form-hidden-field roo-date-split-field-group-value',
38705                     name : this.name
38706                 }
38707             ]
38708         };
38709         
38710         var labelCls = 'col-md-12';
38711         var contentCls = 'col-md-4';
38712         
38713         if(this.fieldLabel){
38714             
38715             var label = {
38716                 tag : 'div',
38717                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38718                 cn : [
38719                     {
38720                         tag : 'label',
38721                         html : this.fieldLabel
38722                     }
38723                 ]
38724             };
38725             
38726             if(this.labelAlign == 'left'){
38727             
38728                 if(this.labelWidth > 12){
38729                     label.style = "width: " + this.labelWidth + 'px';
38730                 }
38731
38732                 if(this.labelWidth < 13 && this.labelmd == 0){
38733                     this.labelmd = this.labelWidth;
38734                 }
38735
38736                 if(this.labellg > 0){
38737                     labelCls = ' col-lg-' + this.labellg;
38738                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38739                 }
38740
38741                 if(this.labelmd > 0){
38742                     labelCls = ' col-md-' + this.labelmd;
38743                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38744                 }
38745
38746                 if(this.labelsm > 0){
38747                     labelCls = ' col-sm-' + this.labelsm;
38748                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38749                 }
38750
38751                 if(this.labelxs > 0){
38752                     labelCls = ' col-xs-' + this.labelxs;
38753                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38754                 }
38755             }
38756             
38757             label.cls += ' ' + labelCls;
38758             
38759             cfg.cn.push(label);
38760         }
38761         
38762         Roo.each(['day', 'month', 'year'], function(t){
38763             cfg.cn.push({
38764                 tag : 'div',
38765                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38766             });
38767         }, this);
38768         
38769         return cfg;
38770     },
38771     
38772     inputEl: function ()
38773     {
38774         return this.el.select('.roo-date-split-field-group-value', true).first();
38775     },
38776     
38777     onRender : function(ct, position) 
38778     {
38779         var _this = this;
38780         
38781         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38782         
38783         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38784         
38785         this.dayField = new Roo.bootstrap.form.ComboBox({
38786             allowBlank : this.dayAllowBlank,
38787             alwaysQuery : true,
38788             displayField : 'value',
38789             editable : false,
38790             fieldLabel : '',
38791             forceSelection : true,
38792             mode : 'local',
38793             placeholder : this.dayPlaceholder,
38794             selectOnFocus : true,
38795             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38796             triggerAction : 'all',
38797             typeAhead : true,
38798             valueField : 'value',
38799             store : new Roo.data.SimpleStore({
38800                 data : (function() {    
38801                     var days = [];
38802                     _this.fireEvent('days', _this, days);
38803                     return days;
38804                 })(),
38805                 fields : [ 'value' ]
38806             }),
38807             listeners : {
38808                 select : function (_self, record, index)
38809                 {
38810                     _this.setValue(_this.getValue());
38811                 }
38812             }
38813         });
38814
38815         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38816         
38817         this.monthField = new Roo.bootstrap.form.MonthField({
38818             after : '<i class=\"fa fa-calendar\"></i>',
38819             allowBlank : this.monthAllowBlank,
38820             placeholder : this.monthPlaceholder,
38821             readOnly : true,
38822             listeners : {
38823                 render : function (_self)
38824                 {
38825                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38826                         e.preventDefault();
38827                         _self.focus();
38828                     });
38829                 },
38830                 select : function (_self, oldvalue, newvalue)
38831                 {
38832                     _this.setValue(_this.getValue());
38833                 }
38834             }
38835         });
38836         
38837         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38838         
38839         this.yearField = new Roo.bootstrap.form.ComboBox({
38840             allowBlank : this.yearAllowBlank,
38841             alwaysQuery : true,
38842             displayField : 'value',
38843             editable : false,
38844             fieldLabel : '',
38845             forceSelection : true,
38846             mode : 'local',
38847             placeholder : this.yearPlaceholder,
38848             selectOnFocus : true,
38849             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38850             triggerAction : 'all',
38851             typeAhead : true,
38852             valueField : 'value',
38853             store : new Roo.data.SimpleStore({
38854                 data : (function() {
38855                     var years = [];
38856                     _this.fireEvent('years', _this, years);
38857                     return years;
38858                 })(),
38859                 fields : [ 'value' ]
38860             }),
38861             listeners : {
38862                 select : function (_self, record, index)
38863                 {
38864                     _this.setValue(_this.getValue());
38865                 }
38866             }
38867         });
38868
38869         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38870     },
38871     
38872     setValue : function(v, format)
38873     {
38874         this.inputEl.dom.value = v;
38875         
38876         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38877         
38878         var d = Date.parseDate(v, f);
38879         
38880         if(!d){
38881             this.validate();
38882             return;
38883         }
38884         
38885         this.setDay(d.format(this.dayFormat));
38886         this.setMonth(d.format(this.monthFormat));
38887         this.setYear(d.format(this.yearFormat));
38888         
38889         this.validate();
38890         
38891         return;
38892     },
38893     
38894     setDay : function(v)
38895     {
38896         this.dayField.setValue(v);
38897         this.inputEl.dom.value = this.getValue();
38898         this.validate();
38899         return;
38900     },
38901     
38902     setMonth : function(v)
38903     {
38904         this.monthField.setValue(v, true);
38905         this.inputEl.dom.value = this.getValue();
38906         this.validate();
38907         return;
38908     },
38909     
38910     setYear : function(v)
38911     {
38912         this.yearField.setValue(v);
38913         this.inputEl.dom.value = this.getValue();
38914         this.validate();
38915         return;
38916     },
38917     
38918     getDay : function()
38919     {
38920         return this.dayField.getValue();
38921     },
38922     
38923     getMonth : function()
38924     {
38925         return this.monthField.getValue();
38926     },
38927     
38928     getYear : function()
38929     {
38930         return this.yearField.getValue();
38931     },
38932     
38933     getValue : function()
38934     {
38935         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38936         
38937         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38938         
38939         return date;
38940     },
38941     
38942     reset : function()
38943     {
38944         this.setDay('');
38945         this.setMonth('');
38946         this.setYear('');
38947         this.inputEl.dom.value = '';
38948         this.validate();
38949         return;
38950     },
38951     
38952     validate : function()
38953     {
38954         var d = this.dayField.validate();
38955         var m = this.monthField.validate();
38956         var y = this.yearField.validate();
38957         
38958         var valid = true;
38959         
38960         if(
38961                 (!this.dayAllowBlank && !d) ||
38962                 (!this.monthAllowBlank && !m) ||
38963                 (!this.yearAllowBlank && !y)
38964         ){
38965             valid = false;
38966         }
38967         
38968         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38969             return valid;
38970         }
38971         
38972         if(valid){
38973             this.markValid();
38974             return valid;
38975         }
38976         
38977         this.markInvalid();
38978         
38979         return valid;
38980     },
38981     
38982     markValid : function()
38983     {
38984         
38985         var label = this.el.select('label', true).first();
38986         var icon = this.el.select('i.fa-star', true).first();
38987
38988         if(label && icon){
38989             icon.remove();
38990         }
38991         
38992         this.fireEvent('valid', this);
38993     },
38994     
38995      /**
38996      * Mark this field as invalid
38997      * @param {String} msg The validation message
38998      */
38999     markInvalid : function(msg)
39000     {
39001         
39002         var label = this.el.select('label', true).first();
39003         var icon = this.el.select('i.fa-star', true).first();
39004
39005         if(label && !icon){
39006             this.el.select('.roo-date-split-field-label', true).createChild({
39007                 tag : 'i',
39008                 cls : 'text-danger fa fa-lg fa-star',
39009                 tooltip : 'This field is required',
39010                 style : 'margin-right:5px;'
39011             }, label, true);
39012         }
39013         
39014         this.fireEvent('invalid', this, msg);
39015     },
39016     
39017     clearInvalid : function()
39018     {
39019         var label = this.el.select('label', true).first();
39020         var icon = this.el.select('i.fa-star', true).first();
39021
39022         if(label && icon){
39023             icon.remove();
39024         }
39025         
39026         this.fireEvent('valid', this);
39027     },
39028     
39029     getName: function()
39030     {
39031         return this.name;
39032     }
39033     
39034 });
39035
39036  
39037
39038 /**
39039  * @class Roo.bootstrap.LayoutMasonry
39040  * @extends Roo.bootstrap.Component
39041  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39042  * Bootstrap Layout Masonry class
39043  *
39044  * This is based on 
39045  * http://masonry.desandro.com
39046  *
39047  * The idea is to render all the bricks based on vertical width...
39048  *
39049  * The original code extends 'outlayer' - we might need to use that....
39050
39051  * @constructor
39052  * Create a new Element
39053  * @param {Object} config The config object
39054  */
39055
39056 Roo.bootstrap.LayoutMasonry = function(config){
39057     
39058     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39059     
39060     this.bricks = [];
39061     
39062     Roo.bootstrap.LayoutMasonry.register(this);
39063     
39064     this.addEvents({
39065         // raw events
39066         /**
39067          * @event layout
39068          * Fire after layout the items
39069          * @param {Roo.bootstrap.LayoutMasonry} this
39070          * @param {Roo.EventObject} e
39071          */
39072         "layout" : true
39073     });
39074     
39075 };
39076
39077 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39078     
39079     /**
39080      * @cfg {Boolean} isLayoutInstant = no animation?
39081      */   
39082     isLayoutInstant : false, // needed?
39083    
39084     /**
39085      * @cfg {Number} boxWidth  width of the columns
39086      */   
39087     boxWidth : 450,
39088     
39089       /**
39090      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39091      */   
39092     boxHeight : 0,
39093     
39094     /**
39095      * @cfg {Number} padWidth padding below box..
39096      */   
39097     padWidth : 10, 
39098     
39099     /**
39100      * @cfg {Number} gutter gutter width..
39101      */   
39102     gutter : 10,
39103     
39104      /**
39105      * @cfg {Number} maxCols maximum number of columns
39106      */   
39107     
39108     maxCols: 0,
39109     
39110     /**
39111      * @cfg {Boolean} isAutoInitial defalut true
39112      */   
39113     isAutoInitial : true, 
39114     
39115     containerWidth: 0,
39116     
39117     /**
39118      * @cfg {Boolean} isHorizontal defalut false
39119      */   
39120     isHorizontal : false, 
39121
39122     currentSize : null,
39123     
39124     tag: 'div',
39125     
39126     cls: '',
39127     
39128     bricks: null, //CompositeElement
39129     
39130     cols : 1,
39131     
39132     _isLayoutInited : false,
39133     
39134 //    isAlternative : false, // only use for vertical layout...
39135     
39136     /**
39137      * @cfg {Number} alternativePadWidth padding below box..
39138      */   
39139     alternativePadWidth : 50,
39140     
39141     selectedBrick : [],
39142     
39143     getAutoCreate : function(){
39144         
39145         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39146         
39147         var cfg = {
39148             tag: this.tag,
39149             cls: 'blog-masonary-wrapper ' + this.cls,
39150             cn : {
39151                 cls : 'mas-boxes masonary'
39152             }
39153         };
39154         
39155         return cfg;
39156     },
39157     
39158     getChildContainer: function( )
39159     {
39160         if (this.boxesEl) {
39161             return this.boxesEl;
39162         }
39163         
39164         this.boxesEl = this.el.select('.mas-boxes').first();
39165         
39166         return this.boxesEl;
39167     },
39168     
39169     
39170     initEvents : function()
39171     {
39172         var _this = this;
39173         
39174         if(this.isAutoInitial){
39175             Roo.log('hook children rendered');
39176             this.on('childrenrendered', function() {
39177                 Roo.log('children rendered');
39178                 _this.initial();
39179             } ,this);
39180         }
39181     },
39182     
39183     initial : function()
39184     {
39185         this.selectedBrick = [];
39186         
39187         this.currentSize = this.el.getBox(true);
39188         
39189         Roo.EventManager.onWindowResize(this.resize, this); 
39190
39191         if(!this.isAutoInitial){
39192             this.layout();
39193             return;
39194         }
39195         
39196         this.layout();
39197         
39198         return;
39199         //this.layout.defer(500,this);
39200         
39201     },
39202     
39203     resize : function()
39204     {
39205         var cs = this.el.getBox(true);
39206         
39207         if (
39208                 this.currentSize.width == cs.width && 
39209                 this.currentSize.x == cs.x && 
39210                 this.currentSize.height == cs.height && 
39211                 this.currentSize.y == cs.y 
39212         ) {
39213             Roo.log("no change in with or X or Y");
39214             return;
39215         }
39216         
39217         this.currentSize = cs;
39218         
39219         this.layout();
39220         
39221     },
39222     
39223     layout : function()
39224     {   
39225         this._resetLayout();
39226         
39227         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39228         
39229         this.layoutItems( isInstant );
39230       
39231         this._isLayoutInited = true;
39232         
39233         this.fireEvent('layout', this);
39234         
39235     },
39236     
39237     _resetLayout : function()
39238     {
39239         if(this.isHorizontal){
39240             this.horizontalMeasureColumns();
39241             return;
39242         }
39243         
39244         this.verticalMeasureColumns();
39245         
39246     },
39247     
39248     verticalMeasureColumns : function()
39249     {
39250         this.getContainerWidth();
39251         
39252 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39253 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39254 //            return;
39255 //        }
39256         
39257         var boxWidth = this.boxWidth + this.padWidth;
39258         
39259         if(this.containerWidth < this.boxWidth){
39260             boxWidth = this.containerWidth
39261         }
39262         
39263         var containerWidth = this.containerWidth;
39264         
39265         var cols = Math.floor(containerWidth / boxWidth);
39266         
39267         this.cols = Math.max( cols, 1 );
39268         
39269         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39270         
39271         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39272         
39273         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39274         
39275         this.colWidth = boxWidth + avail - this.padWidth;
39276         
39277         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39278         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39279     },
39280     
39281     horizontalMeasureColumns : function()
39282     {
39283         this.getContainerWidth();
39284         
39285         var boxWidth = this.boxWidth;
39286         
39287         if(this.containerWidth < boxWidth){
39288             boxWidth = this.containerWidth;
39289         }
39290         
39291         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39292         
39293         this.el.setHeight(boxWidth);
39294         
39295     },
39296     
39297     getContainerWidth : function()
39298     {
39299         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39300     },
39301     
39302     layoutItems : function( isInstant )
39303     {
39304         Roo.log(this.bricks);
39305         
39306         var items = Roo.apply([], this.bricks);
39307         
39308         if(this.isHorizontal){
39309             this._horizontalLayoutItems( items , isInstant );
39310             return;
39311         }
39312         
39313 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39314 //            this._verticalAlternativeLayoutItems( items , isInstant );
39315 //            return;
39316 //        }
39317         
39318         this._verticalLayoutItems( items , isInstant );
39319         
39320     },
39321     
39322     _verticalLayoutItems : function ( items , isInstant)
39323     {
39324         if ( !items || !items.length ) {
39325             return;
39326         }
39327         
39328         var standard = [
39329             ['xs', 'xs', 'xs', 'tall'],
39330             ['xs', 'xs', 'tall'],
39331             ['xs', 'xs', 'sm'],
39332             ['xs', 'xs', 'xs'],
39333             ['xs', 'tall'],
39334             ['xs', 'sm'],
39335             ['xs', 'xs'],
39336             ['xs'],
39337             
39338             ['sm', 'xs', 'xs'],
39339             ['sm', 'xs'],
39340             ['sm'],
39341             
39342             ['tall', 'xs', 'xs', 'xs'],
39343             ['tall', 'xs', 'xs'],
39344             ['tall', 'xs'],
39345             ['tall']
39346             
39347         ];
39348         
39349         var queue = [];
39350         
39351         var boxes = [];
39352         
39353         var box = [];
39354         
39355         Roo.each(items, function(item, k){
39356             
39357             switch (item.size) {
39358                 // these layouts take up a full box,
39359                 case 'md' :
39360                 case 'md-left' :
39361                 case 'md-right' :
39362                 case 'wide' :
39363                     
39364                     if(box.length){
39365                         boxes.push(box);
39366                         box = [];
39367                     }
39368                     
39369                     boxes.push([item]);
39370                     
39371                     break;
39372                     
39373                 case 'xs' :
39374                 case 'sm' :
39375                 case 'tall' :
39376                     
39377                     box.push(item);
39378                     
39379                     break;
39380                 default :
39381                     break;
39382                     
39383             }
39384             
39385         }, this);
39386         
39387         if(box.length){
39388             boxes.push(box);
39389             box = [];
39390         }
39391         
39392         var filterPattern = function(box, length)
39393         {
39394             if(!box.length){
39395                 return;
39396             }
39397             
39398             var match = false;
39399             
39400             var pattern = box.slice(0, length);
39401             
39402             var format = [];
39403             
39404             Roo.each(pattern, function(i){
39405                 format.push(i.size);
39406             }, this);
39407             
39408             Roo.each(standard, function(s){
39409                 
39410                 if(String(s) != String(format)){
39411                     return;
39412                 }
39413                 
39414                 match = true;
39415                 return false;
39416                 
39417             }, this);
39418             
39419             if(!match && length == 1){
39420                 return;
39421             }
39422             
39423             if(!match){
39424                 filterPattern(box, length - 1);
39425                 return;
39426             }
39427                 
39428             queue.push(pattern);
39429
39430             box = box.slice(length, box.length);
39431
39432             filterPattern(box, 4);
39433
39434             return;
39435             
39436         }
39437         
39438         Roo.each(boxes, function(box, k){
39439             
39440             if(!box.length){
39441                 return;
39442             }
39443             
39444             if(box.length == 1){
39445                 queue.push(box);
39446                 return;
39447             }
39448             
39449             filterPattern(box, 4);
39450             
39451         }, this);
39452         
39453         this._processVerticalLayoutQueue( queue, isInstant );
39454         
39455     },
39456     
39457 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39458 //    {
39459 //        if ( !items || !items.length ) {
39460 //            return;
39461 //        }
39462 //
39463 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39464 //        
39465 //    },
39466     
39467     _horizontalLayoutItems : function ( items , isInstant)
39468     {
39469         if ( !items || !items.length || items.length < 3) {
39470             return;
39471         }
39472         
39473         items.reverse();
39474         
39475         var eItems = items.slice(0, 3);
39476         
39477         items = items.slice(3, items.length);
39478         
39479         var standard = [
39480             ['xs', 'xs', 'xs', 'wide'],
39481             ['xs', 'xs', 'wide'],
39482             ['xs', 'xs', 'sm'],
39483             ['xs', 'xs', 'xs'],
39484             ['xs', 'wide'],
39485             ['xs', 'sm'],
39486             ['xs', 'xs'],
39487             ['xs'],
39488             
39489             ['sm', 'xs', 'xs'],
39490             ['sm', 'xs'],
39491             ['sm'],
39492             
39493             ['wide', 'xs', 'xs', 'xs'],
39494             ['wide', 'xs', 'xs'],
39495             ['wide', 'xs'],
39496             ['wide'],
39497             
39498             ['wide-thin']
39499         ];
39500         
39501         var queue = [];
39502         
39503         var boxes = [];
39504         
39505         var box = [];
39506         
39507         Roo.each(items, function(item, k){
39508             
39509             switch (item.size) {
39510                 case 'md' :
39511                 case 'md-left' :
39512                 case 'md-right' :
39513                 case 'tall' :
39514                     
39515                     if(box.length){
39516                         boxes.push(box);
39517                         box = [];
39518                     }
39519                     
39520                     boxes.push([item]);
39521                     
39522                     break;
39523                     
39524                 case 'xs' :
39525                 case 'sm' :
39526                 case 'wide' :
39527                 case 'wide-thin' :
39528                     
39529                     box.push(item);
39530                     
39531                     break;
39532                 default :
39533                     break;
39534                     
39535             }
39536             
39537         }, this);
39538         
39539         if(box.length){
39540             boxes.push(box);
39541             box = [];
39542         }
39543         
39544         var filterPattern = function(box, length)
39545         {
39546             if(!box.length){
39547                 return;
39548             }
39549             
39550             var match = false;
39551             
39552             var pattern = box.slice(0, length);
39553             
39554             var format = [];
39555             
39556             Roo.each(pattern, function(i){
39557                 format.push(i.size);
39558             }, this);
39559             
39560             Roo.each(standard, function(s){
39561                 
39562                 if(String(s) != String(format)){
39563                     return;
39564                 }
39565                 
39566                 match = true;
39567                 return false;
39568                 
39569             }, this);
39570             
39571             if(!match && length == 1){
39572                 return;
39573             }
39574             
39575             if(!match){
39576                 filterPattern(box, length - 1);
39577                 return;
39578             }
39579                 
39580             queue.push(pattern);
39581
39582             box = box.slice(length, box.length);
39583
39584             filterPattern(box, 4);
39585
39586             return;
39587             
39588         }
39589         
39590         Roo.each(boxes, function(box, k){
39591             
39592             if(!box.length){
39593                 return;
39594             }
39595             
39596             if(box.length == 1){
39597                 queue.push(box);
39598                 return;
39599             }
39600             
39601             filterPattern(box, 4);
39602             
39603         }, this);
39604         
39605         
39606         var prune = [];
39607         
39608         var pos = this.el.getBox(true);
39609         
39610         var minX = pos.x;
39611         
39612         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39613         
39614         var hit_end = false;
39615         
39616         Roo.each(queue, function(box){
39617             
39618             if(hit_end){
39619                 
39620                 Roo.each(box, function(b){
39621                 
39622                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39623                     b.el.hide();
39624
39625                 }, this);
39626
39627                 return;
39628             }
39629             
39630             var mx = 0;
39631             
39632             Roo.each(box, function(b){
39633                 
39634                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39635                 b.el.show();
39636
39637                 mx = Math.max(mx, b.x);
39638                 
39639             }, this);
39640             
39641             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39642             
39643             if(maxX < minX){
39644                 
39645                 Roo.each(box, function(b){
39646                 
39647                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39648                     b.el.hide();
39649                     
39650                 }, this);
39651                 
39652                 hit_end = true;
39653                 
39654                 return;
39655             }
39656             
39657             prune.push(box);
39658             
39659         }, this);
39660         
39661         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39662     },
39663     
39664     /** Sets position of item in DOM
39665     * @param {Element} item
39666     * @param {Number} x - horizontal position
39667     * @param {Number} y - vertical position
39668     * @param {Boolean} isInstant - disables transitions
39669     */
39670     _processVerticalLayoutQueue : function( queue, isInstant )
39671     {
39672         var pos = this.el.getBox(true);
39673         var x = pos.x;
39674         var y = pos.y;
39675         var maxY = [];
39676         
39677         for (var i = 0; i < this.cols; i++){
39678             maxY[i] = pos.y;
39679         }
39680         
39681         Roo.each(queue, function(box, k){
39682             
39683             var col = k % this.cols;
39684             
39685             Roo.each(box, function(b,kk){
39686                 
39687                 b.el.position('absolute');
39688                 
39689                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39690                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39691                 
39692                 if(b.size == 'md-left' || b.size == 'md-right'){
39693                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39694                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39695                 }
39696                 
39697                 b.el.setWidth(width);
39698                 b.el.setHeight(height);
39699                 // iframe?
39700                 b.el.select('iframe',true).setSize(width,height);
39701                 
39702             }, this);
39703             
39704             for (var i = 0; i < this.cols; i++){
39705                 
39706                 if(maxY[i] < maxY[col]){
39707                     col = i;
39708                     continue;
39709                 }
39710                 
39711                 col = Math.min(col, i);
39712                 
39713             }
39714             
39715             x = pos.x + col * (this.colWidth + this.padWidth);
39716             
39717             y = maxY[col];
39718             
39719             var positions = [];
39720             
39721             switch (box.length){
39722                 case 1 :
39723                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39724                     break;
39725                 case 2 :
39726                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39727                     break;
39728                 case 3 :
39729                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39730                     break;
39731                 case 4 :
39732                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39733                     break;
39734                 default :
39735                     break;
39736             }
39737             
39738             Roo.each(box, function(b,kk){
39739                 
39740                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39741                 
39742                 var sz = b.el.getSize();
39743                 
39744                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39745                 
39746             }, this);
39747             
39748         }, this);
39749         
39750         var mY = 0;
39751         
39752         for (var i = 0; i < this.cols; i++){
39753             mY = Math.max(mY, maxY[i]);
39754         }
39755         
39756         this.el.setHeight(mY - pos.y);
39757         
39758     },
39759     
39760 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39761 //    {
39762 //        var pos = this.el.getBox(true);
39763 //        var x = pos.x;
39764 //        var y = pos.y;
39765 //        var maxX = pos.right;
39766 //        
39767 //        var maxHeight = 0;
39768 //        
39769 //        Roo.each(items, function(item, k){
39770 //            
39771 //            var c = k % 2;
39772 //            
39773 //            item.el.position('absolute');
39774 //                
39775 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39776 //
39777 //            item.el.setWidth(width);
39778 //
39779 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39780 //
39781 //            item.el.setHeight(height);
39782 //            
39783 //            if(c == 0){
39784 //                item.el.setXY([x, y], isInstant ? false : true);
39785 //            } else {
39786 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39787 //            }
39788 //            
39789 //            y = y + height + this.alternativePadWidth;
39790 //            
39791 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39792 //            
39793 //        }, this);
39794 //        
39795 //        this.el.setHeight(maxHeight);
39796 //        
39797 //    },
39798     
39799     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39800     {
39801         var pos = this.el.getBox(true);
39802         
39803         var minX = pos.x;
39804         var minY = pos.y;
39805         
39806         var maxX = pos.right;
39807         
39808         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39809         
39810         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39811         
39812         Roo.each(queue, function(box, k){
39813             
39814             Roo.each(box, function(b, kk){
39815                 
39816                 b.el.position('absolute');
39817                 
39818                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39819                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39820                 
39821                 if(b.size == 'md-left' || b.size == 'md-right'){
39822                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39823                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39824                 }
39825                 
39826                 b.el.setWidth(width);
39827                 b.el.setHeight(height);
39828                 
39829             }, this);
39830             
39831             if(!box.length){
39832                 return;
39833             }
39834             
39835             var positions = [];
39836             
39837             switch (box.length){
39838                 case 1 :
39839                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39840                     break;
39841                 case 2 :
39842                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39843                     break;
39844                 case 3 :
39845                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39846                     break;
39847                 case 4 :
39848                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39849                     break;
39850                 default :
39851                     break;
39852             }
39853             
39854             Roo.each(box, function(b,kk){
39855                 
39856                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39857                 
39858                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39859                 
39860             }, this);
39861             
39862         }, this);
39863         
39864     },
39865     
39866     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39867     {
39868         Roo.each(eItems, function(b,k){
39869             
39870             b.size = (k == 0) ? 'sm' : 'xs';
39871             b.x = (k == 0) ? 2 : 1;
39872             b.y = (k == 0) ? 2 : 1;
39873             
39874             b.el.position('absolute');
39875             
39876             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39877                 
39878             b.el.setWidth(width);
39879             
39880             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39881             
39882             b.el.setHeight(height);
39883             
39884         }, this);
39885
39886         var positions = [];
39887         
39888         positions.push({
39889             x : maxX - this.unitWidth * 2 - this.gutter,
39890             y : minY
39891         });
39892         
39893         positions.push({
39894             x : maxX - this.unitWidth,
39895             y : minY + (this.unitWidth + this.gutter) * 2
39896         });
39897         
39898         positions.push({
39899             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39900             y : minY
39901         });
39902         
39903         Roo.each(eItems, function(b,k){
39904             
39905             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39906
39907         }, this);
39908         
39909     },
39910     
39911     getVerticalOneBoxColPositions : function(x, y, box)
39912     {
39913         var pos = [];
39914         
39915         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39916         
39917         if(box[0].size == 'md-left'){
39918             rand = 0;
39919         }
39920         
39921         if(box[0].size == 'md-right'){
39922             rand = 1;
39923         }
39924         
39925         pos.push({
39926             x : x + (this.unitWidth + this.gutter) * rand,
39927             y : y
39928         });
39929         
39930         return pos;
39931     },
39932     
39933     getVerticalTwoBoxColPositions : function(x, y, box)
39934     {
39935         var pos = [];
39936         
39937         if(box[0].size == 'xs'){
39938             
39939             pos.push({
39940                 x : x,
39941                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39942             });
39943
39944             pos.push({
39945                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39946                 y : y
39947             });
39948             
39949             return pos;
39950             
39951         }
39952         
39953         pos.push({
39954             x : x,
39955             y : y
39956         });
39957
39958         pos.push({
39959             x : x + (this.unitWidth + this.gutter) * 2,
39960             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39961         });
39962         
39963         return pos;
39964         
39965     },
39966     
39967     getVerticalThreeBoxColPositions : function(x, y, box)
39968     {
39969         var pos = [];
39970         
39971         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39972             
39973             pos.push({
39974                 x : x,
39975                 y : y
39976             });
39977
39978             pos.push({
39979                 x : x + (this.unitWidth + this.gutter) * 1,
39980                 y : y
39981             });
39982             
39983             pos.push({
39984                 x : x + (this.unitWidth + this.gutter) * 2,
39985                 y : y
39986             });
39987             
39988             return pos;
39989             
39990         }
39991         
39992         if(box[0].size == 'xs' && box[1].size == 'xs'){
39993             
39994             pos.push({
39995                 x : x,
39996                 y : y
39997             });
39998
39999             pos.push({
40000                 x : x,
40001                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40002             });
40003             
40004             pos.push({
40005                 x : x + (this.unitWidth + this.gutter) * 1,
40006                 y : y
40007             });
40008             
40009             return pos;
40010             
40011         }
40012         
40013         pos.push({
40014             x : x,
40015             y : y
40016         });
40017
40018         pos.push({
40019             x : x + (this.unitWidth + this.gutter) * 2,
40020             y : y
40021         });
40022
40023         pos.push({
40024             x : x + (this.unitWidth + this.gutter) * 2,
40025             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40026         });
40027             
40028         return pos;
40029         
40030     },
40031     
40032     getVerticalFourBoxColPositions : function(x, y, box)
40033     {
40034         var pos = [];
40035         
40036         if(box[0].size == 'xs'){
40037             
40038             pos.push({
40039                 x : x,
40040                 y : y
40041             });
40042
40043             pos.push({
40044                 x : x,
40045                 y : y + (this.unitHeight + this.gutter) * 1
40046             });
40047             
40048             pos.push({
40049                 x : x,
40050                 y : y + (this.unitHeight + this.gutter) * 2
40051             });
40052             
40053             pos.push({
40054                 x : x + (this.unitWidth + this.gutter) * 1,
40055                 y : y
40056             });
40057             
40058             return pos;
40059             
40060         }
40061         
40062         pos.push({
40063             x : x,
40064             y : y
40065         });
40066
40067         pos.push({
40068             x : x + (this.unitWidth + this.gutter) * 2,
40069             y : y
40070         });
40071
40072         pos.push({
40073             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40074             y : y + (this.unitHeight + this.gutter) * 1
40075         });
40076
40077         pos.push({
40078             x : x + (this.unitWidth + this.gutter) * 2,
40079             y : y + (this.unitWidth + this.gutter) * 2
40080         });
40081
40082         return pos;
40083         
40084     },
40085     
40086     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40087     {
40088         var pos = [];
40089         
40090         if(box[0].size == 'md-left'){
40091             pos.push({
40092                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40093                 y : minY
40094             });
40095             
40096             return pos;
40097         }
40098         
40099         if(box[0].size == 'md-right'){
40100             pos.push({
40101                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40102                 y : minY + (this.unitWidth + this.gutter) * 1
40103             });
40104             
40105             return pos;
40106         }
40107         
40108         var rand = Math.floor(Math.random() * (4 - box[0].y));
40109         
40110         pos.push({
40111             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40112             y : minY + (this.unitWidth + this.gutter) * rand
40113         });
40114         
40115         return pos;
40116         
40117     },
40118     
40119     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40120     {
40121         var pos = [];
40122         
40123         if(box[0].size == 'xs'){
40124             
40125             pos.push({
40126                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40127                 y : minY
40128             });
40129
40130             pos.push({
40131                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40132                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40133             });
40134             
40135             return pos;
40136             
40137         }
40138         
40139         pos.push({
40140             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40141             y : minY
40142         });
40143
40144         pos.push({
40145             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40146             y : minY + (this.unitWidth + this.gutter) * 2
40147         });
40148         
40149         return pos;
40150         
40151     },
40152     
40153     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40154     {
40155         var pos = [];
40156         
40157         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40158             
40159             pos.push({
40160                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40161                 y : minY
40162             });
40163
40164             pos.push({
40165                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40166                 y : minY + (this.unitWidth + this.gutter) * 1
40167             });
40168             
40169             pos.push({
40170                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40171                 y : minY + (this.unitWidth + this.gutter) * 2
40172             });
40173             
40174             return pos;
40175             
40176         }
40177         
40178         if(box[0].size == 'xs' && box[1].size == 'xs'){
40179             
40180             pos.push({
40181                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40182                 y : minY
40183             });
40184
40185             pos.push({
40186                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40187                 y : minY
40188             });
40189             
40190             pos.push({
40191                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40192                 y : minY + (this.unitWidth + this.gutter) * 1
40193             });
40194             
40195             return pos;
40196             
40197         }
40198         
40199         pos.push({
40200             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40201             y : minY
40202         });
40203
40204         pos.push({
40205             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40206             y : minY + (this.unitWidth + this.gutter) * 2
40207         });
40208
40209         pos.push({
40210             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40211             y : minY + (this.unitWidth + this.gutter) * 2
40212         });
40213             
40214         return pos;
40215         
40216     },
40217     
40218     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40219     {
40220         var pos = [];
40221         
40222         if(box[0].size == 'xs'){
40223             
40224             pos.push({
40225                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40226                 y : minY
40227             });
40228
40229             pos.push({
40230                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40231                 y : minY
40232             });
40233             
40234             pos.push({
40235                 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),
40236                 y : minY
40237             });
40238             
40239             pos.push({
40240                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40241                 y : minY + (this.unitWidth + this.gutter) * 1
40242             });
40243             
40244             return pos;
40245             
40246         }
40247         
40248         pos.push({
40249             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40250             y : minY
40251         });
40252         
40253         pos.push({
40254             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40255             y : minY + (this.unitWidth + this.gutter) * 2
40256         });
40257         
40258         pos.push({
40259             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40260             y : minY + (this.unitWidth + this.gutter) * 2
40261         });
40262         
40263         pos.push({
40264             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),
40265             y : minY + (this.unitWidth + this.gutter) * 2
40266         });
40267
40268         return pos;
40269         
40270     },
40271     
40272     /**
40273     * remove a Masonry Brick
40274     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40275     */
40276     removeBrick : function(brick_id)
40277     {
40278         if (!brick_id) {
40279             return;
40280         }
40281         
40282         for (var i = 0; i<this.bricks.length; i++) {
40283             if (this.bricks[i].id == brick_id) {
40284                 this.bricks.splice(i,1);
40285                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40286                 this.initial();
40287             }
40288         }
40289     },
40290     
40291     /**
40292     * adds a Masonry Brick
40293     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40294     */
40295     addBrick : function(cfg)
40296     {
40297         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40298         //this.register(cn);
40299         cn.parentId = this.id;
40300         cn.render(this.el);
40301         return cn;
40302     },
40303     
40304     /**
40305     * register a Masonry Brick
40306     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40307     */
40308     
40309     register : function(brick)
40310     {
40311         this.bricks.push(brick);
40312         brick.masonryId = this.id;
40313     },
40314     
40315     /**
40316     * clear all the Masonry Brick
40317     */
40318     clearAll : function()
40319     {
40320         this.bricks = [];
40321         //this.getChildContainer().dom.innerHTML = "";
40322         this.el.dom.innerHTML = '';
40323     },
40324     
40325     getSelected : function()
40326     {
40327         if (!this.selectedBrick) {
40328             return false;
40329         }
40330         
40331         return this.selectedBrick;
40332     }
40333 });
40334
40335 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40336     
40337     groups: {},
40338      /**
40339     * register a Masonry Layout
40340     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40341     */
40342     
40343     register : function(layout)
40344     {
40345         this.groups[layout.id] = layout;
40346     },
40347     /**
40348     * fetch a  Masonry Layout based on the masonry layout ID
40349     * @param {string} the masonry layout to add
40350     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40351     */
40352     
40353     get: function(layout_id) {
40354         if (typeof(this.groups[layout_id]) == 'undefined') {
40355             return false;
40356         }
40357         return this.groups[layout_id] ;
40358     }
40359     
40360     
40361     
40362 });
40363
40364  
40365
40366  /**
40367  *
40368  * This is based on 
40369  * http://masonry.desandro.com
40370  *
40371  * The idea is to render all the bricks based on vertical width...
40372  *
40373  * The original code extends 'outlayer' - we might need to use that....
40374  * 
40375  */
40376
40377
40378 /**
40379  * @class Roo.bootstrap.LayoutMasonryAuto
40380  * @extends Roo.bootstrap.Component
40381  * Bootstrap Layout Masonry class
40382  * 
40383  * @constructor
40384  * Create a new Element
40385  * @param {Object} config The config object
40386  */
40387
40388 Roo.bootstrap.LayoutMasonryAuto = function(config){
40389     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40390 };
40391
40392 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40393     
40394       /**
40395      * @cfg {Boolean} isFitWidth  - resize the width..
40396      */   
40397     isFitWidth : false,  // options..
40398     /**
40399      * @cfg {Boolean} isOriginLeft = left align?
40400      */   
40401     isOriginLeft : true,
40402     /**
40403      * @cfg {Boolean} isOriginTop = top align?
40404      */   
40405     isOriginTop : false,
40406     /**
40407      * @cfg {Boolean} isLayoutInstant = no animation?
40408      */   
40409     isLayoutInstant : false, // needed?
40410     /**
40411      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40412      */   
40413     isResizingContainer : true,
40414     /**
40415      * @cfg {Number} columnWidth  width of the columns 
40416      */   
40417     
40418     columnWidth : 0,
40419     
40420     /**
40421      * @cfg {Number} maxCols maximum number of columns
40422      */   
40423     
40424     maxCols: 0,
40425     /**
40426      * @cfg {Number} padHeight padding below box..
40427      */   
40428     
40429     padHeight : 10, 
40430     
40431     /**
40432      * @cfg {Boolean} isAutoInitial defalut true
40433      */   
40434     
40435     isAutoInitial : true, 
40436     
40437     // private?
40438     gutter : 0,
40439     
40440     containerWidth: 0,
40441     initialColumnWidth : 0,
40442     currentSize : null,
40443     
40444     colYs : null, // array.
40445     maxY : 0,
40446     padWidth: 10,
40447     
40448     
40449     tag: 'div',
40450     cls: '',
40451     bricks: null, //CompositeElement
40452     cols : 0, // array?
40453     // element : null, // wrapped now this.el
40454     _isLayoutInited : null, 
40455     
40456     
40457     getAutoCreate : function(){
40458         
40459         var cfg = {
40460             tag: this.tag,
40461             cls: 'blog-masonary-wrapper ' + this.cls,
40462             cn : {
40463                 cls : 'mas-boxes masonary'
40464             }
40465         };
40466         
40467         return cfg;
40468     },
40469     
40470     getChildContainer: function( )
40471     {
40472         if (this.boxesEl) {
40473             return this.boxesEl;
40474         }
40475         
40476         this.boxesEl = this.el.select('.mas-boxes').first();
40477         
40478         return this.boxesEl;
40479     },
40480     
40481     
40482     initEvents : function()
40483     {
40484         var _this = this;
40485         
40486         if(this.isAutoInitial){
40487             Roo.log('hook children rendered');
40488             this.on('childrenrendered', function() {
40489                 Roo.log('children rendered');
40490                 _this.initial();
40491             } ,this);
40492         }
40493         
40494     },
40495     
40496     initial : function()
40497     {
40498         this.reloadItems();
40499
40500         this.currentSize = this.el.getBox(true);
40501
40502         /// was window resize... - let's see if this works..
40503         Roo.EventManager.onWindowResize(this.resize, this); 
40504
40505         if(!this.isAutoInitial){
40506             this.layout();
40507             return;
40508         }
40509         
40510         this.layout.defer(500,this);
40511     },
40512     
40513     reloadItems: function()
40514     {
40515         this.bricks = this.el.select('.masonry-brick', true);
40516         
40517         this.bricks.each(function(b) {
40518             //Roo.log(b.getSize());
40519             if (!b.attr('originalwidth')) {
40520                 b.attr('originalwidth',  b.getSize().width);
40521             }
40522             
40523         });
40524         
40525         Roo.log(this.bricks.elements.length);
40526     },
40527     
40528     resize : function()
40529     {
40530         Roo.log('resize');
40531         var cs = this.el.getBox(true);
40532         
40533         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40534             Roo.log("no change in with or X");
40535             return;
40536         }
40537         this.currentSize = cs;
40538         this.layout();
40539     },
40540     
40541     layout : function()
40542     {
40543          Roo.log('layout');
40544         this._resetLayout();
40545         //this._manageStamps();
40546       
40547         // don't animate first layout
40548         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40549         this.layoutItems( isInstant );
40550       
40551         // flag for initalized
40552         this._isLayoutInited = true;
40553     },
40554     
40555     layoutItems : function( isInstant )
40556     {
40557         //var items = this._getItemsForLayout( this.items );
40558         // original code supports filtering layout items.. we just ignore it..
40559         
40560         this._layoutItems( this.bricks , isInstant );
40561       
40562         this._postLayout();
40563     },
40564     _layoutItems : function ( items , isInstant)
40565     {
40566        //this.fireEvent( 'layout', this, items );
40567     
40568
40569         if ( !items || !items.elements.length ) {
40570           // no items, emit event with empty array
40571             return;
40572         }
40573
40574         var queue = [];
40575         items.each(function(item) {
40576             Roo.log("layout item");
40577             Roo.log(item);
40578             // get x/y object from method
40579             var position = this._getItemLayoutPosition( item );
40580             // enqueue
40581             position.item = item;
40582             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40583             queue.push( position );
40584         }, this);
40585       
40586         this._processLayoutQueue( queue );
40587     },
40588     /** Sets position of item in DOM
40589     * @param {Element} item
40590     * @param {Number} x - horizontal position
40591     * @param {Number} y - vertical position
40592     * @param {Boolean} isInstant - disables transitions
40593     */
40594     _processLayoutQueue : function( queue )
40595     {
40596         for ( var i=0, len = queue.length; i < len; i++ ) {
40597             var obj = queue[i];
40598             obj.item.position('absolute');
40599             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40600         }
40601     },
40602       
40603     
40604     /**
40605     * Any logic you want to do after each layout,
40606     * i.e. size the container
40607     */
40608     _postLayout : function()
40609     {
40610         this.resizeContainer();
40611     },
40612     
40613     resizeContainer : function()
40614     {
40615         if ( !this.isResizingContainer ) {
40616             return;
40617         }
40618         var size = this._getContainerSize();
40619         if ( size ) {
40620             this.el.setSize(size.width,size.height);
40621             this.boxesEl.setSize(size.width,size.height);
40622         }
40623     },
40624     
40625     
40626     
40627     _resetLayout : function()
40628     {
40629         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40630         this.colWidth = this.el.getWidth();
40631         //this.gutter = this.el.getWidth(); 
40632         
40633         this.measureColumns();
40634
40635         // reset column Y
40636         var i = this.cols;
40637         this.colYs = [];
40638         while (i--) {
40639             this.colYs.push( 0 );
40640         }
40641     
40642         this.maxY = 0;
40643     },
40644
40645     measureColumns : function()
40646     {
40647         this.getContainerWidth();
40648       // if columnWidth is 0, default to outerWidth of first item
40649         if ( !this.columnWidth ) {
40650             var firstItem = this.bricks.first();
40651             Roo.log(firstItem);
40652             this.columnWidth  = this.containerWidth;
40653             if (firstItem && firstItem.attr('originalwidth') ) {
40654                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40655             }
40656             // columnWidth fall back to item of first element
40657             Roo.log("set column width?");
40658                         this.initialColumnWidth = this.columnWidth  ;
40659
40660             // if first elem has no width, default to size of container
40661             
40662         }
40663         
40664         
40665         if (this.initialColumnWidth) {
40666             this.columnWidth = this.initialColumnWidth;
40667         }
40668         
40669         
40670             
40671         // column width is fixed at the top - however if container width get's smaller we should
40672         // reduce it...
40673         
40674         // this bit calcs how man columns..
40675             
40676         var columnWidth = this.columnWidth += this.gutter;
40677       
40678         // calculate columns
40679         var containerWidth = this.containerWidth + this.gutter;
40680         
40681         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40682         // fix rounding errors, typically with gutters
40683         var excess = columnWidth - containerWidth % columnWidth;
40684         
40685         
40686         // if overshoot is less than a pixel, round up, otherwise floor it
40687         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40688         cols = Math[ mathMethod ]( cols );
40689         this.cols = Math.max( cols, 1 );
40690         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40691         
40692          // padding positioning..
40693         var totalColWidth = this.cols * this.columnWidth;
40694         var padavail = this.containerWidth - totalColWidth;
40695         // so for 2 columns - we need 3 'pads'
40696         
40697         var padNeeded = (1+this.cols) * this.padWidth;
40698         
40699         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40700         
40701         this.columnWidth += padExtra
40702         //this.padWidth = Math.floor(padavail /  ( this.cols));
40703         
40704         // adjust colum width so that padding is fixed??
40705         
40706         // we have 3 columns ... total = width * 3
40707         // we have X left over... that should be used by 
40708         
40709         //if (this.expandC) {
40710             
40711         //}
40712         
40713         
40714         
40715     },
40716     
40717     getContainerWidth : function()
40718     {
40719        /* // container is parent if fit width
40720         var container = this.isFitWidth ? this.element.parentNode : this.element;
40721         // check that this.size and size are there
40722         // IE8 triggers resize on body size change, so they might not be
40723         
40724         var size = getSize( container );  //FIXME
40725         this.containerWidth = size && size.innerWidth; //FIXME
40726         */
40727          
40728         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40729         
40730     },
40731     
40732     _getItemLayoutPosition : function( item )  // what is item?
40733     {
40734         // we resize the item to our columnWidth..
40735       
40736         item.setWidth(this.columnWidth);
40737         item.autoBoxAdjust  = false;
40738         
40739         var sz = item.getSize();
40740  
40741         // how many columns does this brick span
40742         var remainder = this.containerWidth % this.columnWidth;
40743         
40744         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40745         // round if off by 1 pixel, otherwise use ceil
40746         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40747         colSpan = Math.min( colSpan, this.cols );
40748         
40749         // normally this should be '1' as we dont' currently allow multi width columns..
40750         
40751         var colGroup = this._getColGroup( colSpan );
40752         // get the minimum Y value from the columns
40753         var minimumY = Math.min.apply( Math, colGroup );
40754         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40755         
40756         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40757          
40758         // position the brick
40759         var position = {
40760             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40761             y: this.currentSize.y + minimumY + this.padHeight
40762         };
40763         
40764         Roo.log(position);
40765         // apply setHeight to necessary columns
40766         var setHeight = minimumY + sz.height + this.padHeight;
40767         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40768         
40769         var setSpan = this.cols + 1 - colGroup.length;
40770         for ( var i = 0; i < setSpan; i++ ) {
40771           this.colYs[ shortColIndex + i ] = setHeight ;
40772         }
40773       
40774         return position;
40775     },
40776     
40777     /**
40778      * @param {Number} colSpan - number of columns the element spans
40779      * @returns {Array} colGroup
40780      */
40781     _getColGroup : function( colSpan )
40782     {
40783         if ( colSpan < 2 ) {
40784           // if brick spans only one column, use all the column Ys
40785           return this.colYs;
40786         }
40787       
40788         var colGroup = [];
40789         // how many different places could this brick fit horizontally
40790         var groupCount = this.cols + 1 - colSpan;
40791         // for each group potential horizontal position
40792         for ( var i = 0; i < groupCount; i++ ) {
40793           // make an array of colY values for that one group
40794           var groupColYs = this.colYs.slice( i, i + colSpan );
40795           // and get the max value of the array
40796           colGroup[i] = Math.max.apply( Math, groupColYs );
40797         }
40798         return colGroup;
40799     },
40800     /*
40801     _manageStamp : function( stamp )
40802     {
40803         var stampSize =  stamp.getSize();
40804         var offset = stamp.getBox();
40805         // get the columns that this stamp affects
40806         var firstX = this.isOriginLeft ? offset.x : offset.right;
40807         var lastX = firstX + stampSize.width;
40808         var firstCol = Math.floor( firstX / this.columnWidth );
40809         firstCol = Math.max( 0, firstCol );
40810         
40811         var lastCol = Math.floor( lastX / this.columnWidth );
40812         // lastCol should not go over if multiple of columnWidth #425
40813         lastCol -= lastX % this.columnWidth ? 0 : 1;
40814         lastCol = Math.min( this.cols - 1, lastCol );
40815         
40816         // set colYs to bottom of the stamp
40817         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40818             stampSize.height;
40819             
40820         for ( var i = firstCol; i <= lastCol; i++ ) {
40821           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40822         }
40823     },
40824     */
40825     
40826     _getContainerSize : function()
40827     {
40828         this.maxY = Math.max.apply( Math, this.colYs );
40829         var size = {
40830             height: this.maxY
40831         };
40832       
40833         if ( this.isFitWidth ) {
40834             size.width = this._getContainerFitWidth();
40835         }
40836       
40837         return size;
40838     },
40839     
40840     _getContainerFitWidth : function()
40841     {
40842         var unusedCols = 0;
40843         // count unused columns
40844         var i = this.cols;
40845         while ( --i ) {
40846           if ( this.colYs[i] !== 0 ) {
40847             break;
40848           }
40849           unusedCols++;
40850         }
40851         // fit container to columns that have been used
40852         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40853     },
40854     
40855     needsResizeLayout : function()
40856     {
40857         var previousWidth = this.containerWidth;
40858         this.getContainerWidth();
40859         return previousWidth !== this.containerWidth;
40860     }
40861  
40862 });
40863
40864  
40865
40866  /*
40867  * - LGPL
40868  *
40869  * element
40870  * 
40871  */
40872
40873 /**
40874  * @class Roo.bootstrap.MasonryBrick
40875  * @extends Roo.bootstrap.Component
40876  * Bootstrap MasonryBrick class
40877  * 
40878  * @constructor
40879  * Create a new MasonryBrick
40880  * @param {Object} config The config object
40881  */
40882
40883 Roo.bootstrap.MasonryBrick = function(config){
40884     
40885     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40886     
40887     Roo.bootstrap.MasonryBrick.register(this);
40888     
40889     this.addEvents({
40890         // raw events
40891         /**
40892          * @event click
40893          * When a MasonryBrick is clcik
40894          * @param {Roo.bootstrap.MasonryBrick} this
40895          * @param {Roo.EventObject} e
40896          */
40897         "click" : true
40898     });
40899 };
40900
40901 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40902     
40903     /**
40904      * @cfg {String} title
40905      */   
40906     title : '',
40907     /**
40908      * @cfg {String} html
40909      */   
40910     html : '',
40911     /**
40912      * @cfg {String} bgimage
40913      */   
40914     bgimage : '',
40915     /**
40916      * @cfg {String} videourl
40917      */   
40918     videourl : '',
40919     /**
40920      * @cfg {String} cls
40921      */   
40922     cls : '',
40923     /**
40924      * @cfg {String} href
40925      */   
40926     href : '',
40927     /**
40928      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40929      */   
40930     size : 'xs',
40931     
40932     /**
40933      * @cfg {String} placetitle (center|bottom)
40934      */   
40935     placetitle : '',
40936     
40937     /**
40938      * @cfg {Boolean} isFitContainer defalut true
40939      */   
40940     isFitContainer : true, 
40941     
40942     /**
40943      * @cfg {Boolean} preventDefault defalut false
40944      */   
40945     preventDefault : false, 
40946     
40947     /**
40948      * @cfg {Boolean} inverse defalut false
40949      */   
40950     maskInverse : false, 
40951     
40952     getAutoCreate : function()
40953     {
40954         if(!this.isFitContainer){
40955             return this.getSplitAutoCreate();
40956         }
40957         
40958         var cls = 'masonry-brick masonry-brick-full';
40959         
40960         if(this.href.length){
40961             cls += ' masonry-brick-link';
40962         }
40963         
40964         if(this.bgimage.length){
40965             cls += ' masonry-brick-image';
40966         }
40967         
40968         if(this.maskInverse){
40969             cls += ' mask-inverse';
40970         }
40971         
40972         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40973             cls += ' enable-mask';
40974         }
40975         
40976         if(this.size){
40977             cls += ' masonry-' + this.size + '-brick';
40978         }
40979         
40980         if(this.placetitle.length){
40981             
40982             switch (this.placetitle) {
40983                 case 'center' :
40984                     cls += ' masonry-center-title';
40985                     break;
40986                 case 'bottom' :
40987                     cls += ' masonry-bottom-title';
40988                     break;
40989                 default:
40990                     break;
40991             }
40992             
40993         } else {
40994             if(!this.html.length && !this.bgimage.length){
40995                 cls += ' masonry-center-title';
40996             }
40997
40998             if(!this.html.length && this.bgimage.length){
40999                 cls += ' masonry-bottom-title';
41000             }
41001         }
41002         
41003         if(this.cls){
41004             cls += ' ' + this.cls;
41005         }
41006         
41007         var cfg = {
41008             tag: (this.href.length) ? 'a' : 'div',
41009             cls: cls,
41010             cn: [
41011                 {
41012                     tag: 'div',
41013                     cls: 'masonry-brick-mask'
41014                 },
41015                 {
41016                     tag: 'div',
41017                     cls: 'masonry-brick-paragraph',
41018                     cn: []
41019                 }
41020             ]
41021         };
41022         
41023         if(this.href.length){
41024             cfg.href = this.href;
41025         }
41026         
41027         var cn = cfg.cn[1].cn;
41028         
41029         if(this.title.length){
41030             cn.push({
41031                 tag: 'h4',
41032                 cls: 'masonry-brick-title',
41033                 html: this.title
41034             });
41035         }
41036         
41037         if(this.html.length){
41038             cn.push({
41039                 tag: 'p',
41040                 cls: 'masonry-brick-text',
41041                 html: this.html
41042             });
41043         }
41044         
41045         if (!this.title.length && !this.html.length) {
41046             cfg.cn[1].cls += ' hide';
41047         }
41048         
41049         if(this.bgimage.length){
41050             cfg.cn.push({
41051                 tag: 'img',
41052                 cls: 'masonry-brick-image-view',
41053                 src: this.bgimage
41054             });
41055         }
41056         
41057         if(this.videourl.length){
41058             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41059             // youtube support only?
41060             cfg.cn.push({
41061                 tag: 'iframe',
41062                 cls: 'masonry-brick-image-view',
41063                 src: vurl,
41064                 frameborder : 0,
41065                 allowfullscreen : true
41066             });
41067         }
41068         
41069         return cfg;
41070         
41071     },
41072     
41073     getSplitAutoCreate : function()
41074     {
41075         var cls = 'masonry-brick masonry-brick-split';
41076         
41077         if(this.href.length){
41078             cls += ' masonry-brick-link';
41079         }
41080         
41081         if(this.bgimage.length){
41082             cls += ' masonry-brick-image';
41083         }
41084         
41085         if(this.size){
41086             cls += ' masonry-' + this.size + '-brick';
41087         }
41088         
41089         switch (this.placetitle) {
41090             case 'center' :
41091                 cls += ' masonry-center-title';
41092                 break;
41093             case 'bottom' :
41094                 cls += ' masonry-bottom-title';
41095                 break;
41096             default:
41097                 if(!this.bgimage.length){
41098                     cls += ' masonry-center-title';
41099                 }
41100
41101                 if(this.bgimage.length){
41102                     cls += ' masonry-bottom-title';
41103                 }
41104                 break;
41105         }
41106         
41107         if(this.cls){
41108             cls += ' ' + this.cls;
41109         }
41110         
41111         var cfg = {
41112             tag: (this.href.length) ? 'a' : 'div',
41113             cls: cls,
41114             cn: [
41115                 {
41116                     tag: 'div',
41117                     cls: 'masonry-brick-split-head',
41118                     cn: [
41119                         {
41120                             tag: 'div',
41121                             cls: 'masonry-brick-paragraph',
41122                             cn: []
41123                         }
41124                     ]
41125                 },
41126                 {
41127                     tag: 'div',
41128                     cls: 'masonry-brick-split-body',
41129                     cn: []
41130                 }
41131             ]
41132         };
41133         
41134         if(this.href.length){
41135             cfg.href = this.href;
41136         }
41137         
41138         if(this.title.length){
41139             cfg.cn[0].cn[0].cn.push({
41140                 tag: 'h4',
41141                 cls: 'masonry-brick-title',
41142                 html: this.title
41143             });
41144         }
41145         
41146         if(this.html.length){
41147             cfg.cn[1].cn.push({
41148                 tag: 'p',
41149                 cls: 'masonry-brick-text',
41150                 html: this.html
41151             });
41152         }
41153
41154         if(this.bgimage.length){
41155             cfg.cn[0].cn.push({
41156                 tag: 'img',
41157                 cls: 'masonry-brick-image-view',
41158                 src: this.bgimage
41159             });
41160         }
41161         
41162         if(this.videourl.length){
41163             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41164             // youtube support only?
41165             cfg.cn[0].cn.cn.push({
41166                 tag: 'iframe',
41167                 cls: 'masonry-brick-image-view',
41168                 src: vurl,
41169                 frameborder : 0,
41170                 allowfullscreen : true
41171             });
41172         }
41173         
41174         return cfg;
41175     },
41176     
41177     initEvents: function() 
41178     {
41179         switch (this.size) {
41180             case 'xs' :
41181                 this.x = 1;
41182                 this.y = 1;
41183                 break;
41184             case 'sm' :
41185                 this.x = 2;
41186                 this.y = 2;
41187                 break;
41188             case 'md' :
41189             case 'md-left' :
41190             case 'md-right' :
41191                 this.x = 3;
41192                 this.y = 3;
41193                 break;
41194             case 'tall' :
41195                 this.x = 2;
41196                 this.y = 3;
41197                 break;
41198             case 'wide' :
41199                 this.x = 3;
41200                 this.y = 2;
41201                 break;
41202             case 'wide-thin' :
41203                 this.x = 3;
41204                 this.y = 1;
41205                 break;
41206                         
41207             default :
41208                 break;
41209         }
41210         
41211         if(Roo.isTouch){
41212             this.el.on('touchstart', this.onTouchStart, this);
41213             this.el.on('touchmove', this.onTouchMove, this);
41214             this.el.on('touchend', this.onTouchEnd, this);
41215             this.el.on('contextmenu', this.onContextMenu, this);
41216         } else {
41217             this.el.on('mouseenter'  ,this.enter, this);
41218             this.el.on('mouseleave', this.leave, this);
41219             this.el.on('click', this.onClick, this);
41220         }
41221         
41222         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41223             this.parent().bricks.push(this);   
41224         }
41225         
41226     },
41227     
41228     onClick: function(e, el)
41229     {
41230         var time = this.endTimer - this.startTimer;
41231         // Roo.log(e.preventDefault());
41232         if(Roo.isTouch){
41233             if(time > 1000){
41234                 e.preventDefault();
41235                 return;
41236             }
41237         }
41238         
41239         if(!this.preventDefault){
41240             return;
41241         }
41242         
41243         e.preventDefault();
41244         
41245         if (this.activeClass != '') {
41246             this.selectBrick();
41247         }
41248         
41249         this.fireEvent('click', this, e);
41250     },
41251     
41252     enter: function(e, el)
41253     {
41254         e.preventDefault();
41255         
41256         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41257             return;
41258         }
41259         
41260         if(this.bgimage.length && this.html.length){
41261             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41262         }
41263     },
41264     
41265     leave: function(e, el)
41266     {
41267         e.preventDefault();
41268         
41269         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41270             return;
41271         }
41272         
41273         if(this.bgimage.length && this.html.length){
41274             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41275         }
41276     },
41277     
41278     onTouchStart: function(e, el)
41279     {
41280 //        e.preventDefault();
41281         
41282         this.touchmoved = false;
41283         
41284         if(!this.isFitContainer){
41285             return;
41286         }
41287         
41288         if(!this.bgimage.length || !this.html.length){
41289             return;
41290         }
41291         
41292         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41293         
41294         this.timer = new Date().getTime();
41295         
41296     },
41297     
41298     onTouchMove: function(e, el)
41299     {
41300         this.touchmoved = true;
41301     },
41302     
41303     onContextMenu : function(e,el)
41304     {
41305         e.preventDefault();
41306         e.stopPropagation();
41307         return false;
41308     },
41309     
41310     onTouchEnd: function(e, el)
41311     {
41312 //        e.preventDefault();
41313         
41314         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41315         
41316             this.leave(e,el);
41317             
41318             return;
41319         }
41320         
41321         if(!this.bgimage.length || !this.html.length){
41322             
41323             if(this.href.length){
41324                 window.location.href = this.href;
41325             }
41326             
41327             return;
41328         }
41329         
41330         if(!this.isFitContainer){
41331             return;
41332         }
41333         
41334         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41335         
41336         window.location.href = this.href;
41337     },
41338     
41339     //selection on single brick only
41340     selectBrick : function() {
41341         
41342         if (!this.parentId) {
41343             return;
41344         }
41345         
41346         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41347         var index = m.selectedBrick.indexOf(this.id);
41348         
41349         if ( index > -1) {
41350             m.selectedBrick.splice(index,1);
41351             this.el.removeClass(this.activeClass);
41352             return;
41353         }
41354         
41355         for(var i = 0; i < m.selectedBrick.length; i++) {
41356             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41357             b.el.removeClass(b.activeClass);
41358         }
41359         
41360         m.selectedBrick = [];
41361         
41362         m.selectedBrick.push(this.id);
41363         this.el.addClass(this.activeClass);
41364         return;
41365     },
41366     
41367     isSelected : function(){
41368         return this.el.hasClass(this.activeClass);
41369         
41370     }
41371 });
41372
41373 Roo.apply(Roo.bootstrap.MasonryBrick, {
41374     
41375     //groups: {},
41376     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41377      /**
41378     * register a Masonry Brick
41379     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41380     */
41381     
41382     register : function(brick)
41383     {
41384         //this.groups[brick.id] = brick;
41385         this.groups.add(brick.id, brick);
41386     },
41387     /**
41388     * fetch a  masonry brick based on the masonry brick ID
41389     * @param {string} the masonry brick to add
41390     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41391     */
41392     
41393     get: function(brick_id) 
41394     {
41395         // if (typeof(this.groups[brick_id]) == 'undefined') {
41396         //     return false;
41397         // }
41398         // return this.groups[brick_id] ;
41399         
41400         if(this.groups.key(brick_id)) {
41401             return this.groups.key(brick_id);
41402         }
41403         
41404         return false;
41405     }
41406     
41407     
41408     
41409 });
41410
41411  /*
41412  * - LGPL
41413  *
41414  * element
41415  * 
41416  */
41417
41418 /**
41419  * @class Roo.bootstrap.Brick
41420  * @extends Roo.bootstrap.Component
41421  * Bootstrap Brick class
41422  * 
41423  * @constructor
41424  * Create a new Brick
41425  * @param {Object} config The config object
41426  */
41427
41428 Roo.bootstrap.Brick = function(config){
41429     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41430     
41431     this.addEvents({
41432         // raw events
41433         /**
41434          * @event click
41435          * When a Brick is click
41436          * @param {Roo.bootstrap.Brick} this
41437          * @param {Roo.EventObject} e
41438          */
41439         "click" : true
41440     });
41441 };
41442
41443 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41444     
41445     /**
41446      * @cfg {String} title
41447      */   
41448     title : '',
41449     /**
41450      * @cfg {String} html
41451      */   
41452     html : '',
41453     /**
41454      * @cfg {String} bgimage
41455      */   
41456     bgimage : '',
41457     /**
41458      * @cfg {String} cls
41459      */   
41460     cls : '',
41461     /**
41462      * @cfg {String} href
41463      */   
41464     href : '',
41465     /**
41466      * @cfg {String} video
41467      */   
41468     video : '',
41469     /**
41470      * @cfg {Boolean} square
41471      */   
41472     square : true,
41473     
41474     getAutoCreate : function()
41475     {
41476         var cls = 'roo-brick';
41477         
41478         if(this.href.length){
41479             cls += ' roo-brick-link';
41480         }
41481         
41482         if(this.bgimage.length){
41483             cls += ' roo-brick-image';
41484         }
41485         
41486         if(!this.html.length && !this.bgimage.length){
41487             cls += ' roo-brick-center-title';
41488         }
41489         
41490         if(!this.html.length && this.bgimage.length){
41491             cls += ' roo-brick-bottom-title';
41492         }
41493         
41494         if(this.cls){
41495             cls += ' ' + this.cls;
41496         }
41497         
41498         var cfg = {
41499             tag: (this.href.length) ? 'a' : 'div',
41500             cls: cls,
41501             cn: [
41502                 {
41503                     tag: 'div',
41504                     cls: 'roo-brick-paragraph',
41505                     cn: []
41506                 }
41507             ]
41508         };
41509         
41510         if(this.href.length){
41511             cfg.href = this.href;
41512         }
41513         
41514         var cn = cfg.cn[0].cn;
41515         
41516         if(this.title.length){
41517             cn.push({
41518                 tag: 'h4',
41519                 cls: 'roo-brick-title',
41520                 html: this.title
41521             });
41522         }
41523         
41524         if(this.html.length){
41525             cn.push({
41526                 tag: 'p',
41527                 cls: 'roo-brick-text',
41528                 html: this.html
41529             });
41530         } else {
41531             cn.cls += ' hide';
41532         }
41533         
41534         if(this.bgimage.length){
41535             cfg.cn.push({
41536                 tag: 'img',
41537                 cls: 'roo-brick-image-view',
41538                 src: this.bgimage
41539             });
41540         }
41541         
41542         return cfg;
41543     },
41544     
41545     initEvents: function() 
41546     {
41547         if(this.title.length || this.html.length){
41548             this.el.on('mouseenter'  ,this.enter, this);
41549             this.el.on('mouseleave', this.leave, this);
41550         }
41551         
41552         Roo.EventManager.onWindowResize(this.resize, this); 
41553         
41554         if(this.bgimage.length){
41555             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41556             this.imageEl.on('load', this.onImageLoad, this);
41557             return;
41558         }
41559         
41560         this.resize();
41561     },
41562     
41563     onImageLoad : function()
41564     {
41565         this.resize();
41566     },
41567     
41568     resize : function()
41569     {
41570         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41571         
41572         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41573         
41574         if(this.bgimage.length){
41575             var image = this.el.select('.roo-brick-image-view', true).first();
41576             
41577             image.setWidth(paragraph.getWidth());
41578             
41579             if(this.square){
41580                 image.setHeight(paragraph.getWidth());
41581             }
41582             
41583             this.el.setHeight(image.getHeight());
41584             paragraph.setHeight(image.getHeight());
41585             
41586         }
41587         
41588     },
41589     
41590     enter: function(e, el)
41591     {
41592         e.preventDefault();
41593         
41594         if(this.bgimage.length){
41595             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41596             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41597         }
41598     },
41599     
41600     leave: function(e, el)
41601     {
41602         e.preventDefault();
41603         
41604         if(this.bgimage.length){
41605             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41606             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41607         }
41608     }
41609     
41610 });
41611
41612  
41613
41614  /*
41615  * - LGPL
41616  *
41617  * Number field 
41618  */
41619
41620 /**
41621  * @class Roo.bootstrap.form.NumberField
41622  * @extends Roo.bootstrap.form.Input
41623  * Bootstrap NumberField class
41624  * 
41625  * 
41626  * 
41627  * 
41628  * @constructor
41629  * Create a new NumberField
41630  * @param {Object} config The config object
41631  */
41632
41633 Roo.bootstrap.form.NumberField = function(config){
41634     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41635 };
41636
41637 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41638     
41639     /**
41640      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41641      */
41642     allowDecimals : true,
41643     /**
41644      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41645      */
41646     decimalSeparator : ".",
41647     /**
41648      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41649      */
41650     decimalPrecision : 2,
41651     /**
41652      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41653      */
41654     allowNegative : true,
41655     
41656     /**
41657      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41658      */
41659     allowZero: true,
41660     /**
41661      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41662      */
41663     minValue : Number.NEGATIVE_INFINITY,
41664     /**
41665      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41666      */
41667     maxValue : Number.MAX_VALUE,
41668     /**
41669      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41670      */
41671     minText : "The minimum value for this field is {0}",
41672     /**
41673      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41674      */
41675     maxText : "The maximum value for this field is {0}",
41676     /**
41677      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41678      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41679      */
41680     nanText : "{0} is not a valid number",
41681     /**
41682      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41683      */
41684     thousandsDelimiter : false,
41685     /**
41686      * @cfg {String} valueAlign alignment of value
41687      */
41688     valueAlign : "left",
41689
41690     getAutoCreate : function()
41691     {
41692         var hiddenInput = {
41693             tag: 'input',
41694             type: 'hidden',
41695             id: Roo.id(),
41696             cls: 'hidden-number-input'
41697         };
41698         
41699         if (this.name) {
41700             hiddenInput.name = this.name;
41701         }
41702         
41703         this.name = '';
41704         
41705         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41706         
41707         this.name = hiddenInput.name;
41708         
41709         if(cfg.cn.length > 0) {
41710             cfg.cn.push(hiddenInput);
41711         }
41712         
41713         return cfg;
41714     },
41715
41716     // private
41717     initEvents : function()
41718     {   
41719         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41720         
41721         var allowed = "0123456789";
41722         
41723         if(this.allowDecimals){
41724             allowed += this.decimalSeparator;
41725         }
41726         
41727         if(this.allowNegative){
41728             allowed += "-";
41729         }
41730         
41731         if(this.thousandsDelimiter) {
41732             allowed += ",";
41733         }
41734         
41735         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41736         
41737         var keyPress = function(e){
41738             
41739             var k = e.getKey();
41740             
41741             var c = e.getCharCode();
41742             
41743             if(
41744                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41745                     allowed.indexOf(String.fromCharCode(c)) === -1
41746             ){
41747                 e.stopEvent();
41748                 return;
41749             }
41750             
41751             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41752                 return;
41753             }
41754             
41755             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41756                 e.stopEvent();
41757             }
41758         };
41759         
41760         this.el.on("keypress", keyPress, this);
41761     },
41762     
41763     validateValue : function(value)
41764     {
41765         
41766         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41767             return false;
41768         }
41769         
41770         var num = this.parseValue(value);
41771         
41772         if(isNaN(num)){
41773             this.markInvalid(String.format(this.nanText, value));
41774             return false;
41775         }
41776         
41777         if(num < this.minValue){
41778             this.markInvalid(String.format(this.minText, this.minValue));
41779             return false;
41780         }
41781         
41782         if(num > this.maxValue){
41783             this.markInvalid(String.format(this.maxText, this.maxValue));
41784             return false;
41785         }
41786         
41787         return true;
41788     },
41789
41790     getValue : function()
41791     {
41792         var v = this.hiddenEl().getValue();
41793         
41794         return this.fixPrecision(this.parseValue(v));
41795     },
41796
41797     parseValue : function(value)
41798     {
41799         if(this.thousandsDelimiter) {
41800             value += "";
41801             r = new RegExp(",", "g");
41802             value = value.replace(r, "");
41803         }
41804         
41805         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41806         return isNaN(value) ? '' : value;
41807     },
41808
41809     fixPrecision : function(value)
41810     {
41811         if(this.thousandsDelimiter) {
41812             value += "";
41813             r = new RegExp(",", "g");
41814             value = value.replace(r, "");
41815         }
41816         
41817         var nan = isNaN(value);
41818         
41819         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41820             return nan ? '' : value;
41821         }
41822         return parseFloat(value).toFixed(this.decimalPrecision);
41823     },
41824
41825     setValue : function(v)
41826     {
41827         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41828         
41829         this.value = v;
41830         
41831         if(this.rendered){
41832             
41833             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41834             
41835             this.inputEl().dom.value = (v == '') ? '' :
41836                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41837             
41838             if(!this.allowZero && v === '0') {
41839                 this.hiddenEl().dom.value = '';
41840                 this.inputEl().dom.value = '';
41841             }
41842             
41843             this.validate();
41844         }
41845     },
41846
41847     decimalPrecisionFcn : function(v)
41848     {
41849         return Math.floor(v);
41850     },
41851
41852     beforeBlur : function()
41853     {
41854         var v = this.parseValue(this.getRawValue());
41855         
41856         if(v || v === 0 || v === ''){
41857             this.setValue(v);
41858         }
41859     },
41860     
41861     hiddenEl : function()
41862     {
41863         return this.el.select('input.hidden-number-input',true).first();
41864     }
41865     
41866 });
41867
41868  
41869
41870 /*
41871 * Licence: LGPL
41872 */
41873
41874 /**
41875  * @class Roo.bootstrap.DocumentSlider
41876  * @extends Roo.bootstrap.Component
41877  * Bootstrap DocumentSlider class
41878  * 
41879  * @constructor
41880  * Create a new DocumentViewer
41881  * @param {Object} config The config object
41882  */
41883
41884 Roo.bootstrap.DocumentSlider = function(config){
41885     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41886     
41887     this.files = [];
41888     
41889     this.addEvents({
41890         /**
41891          * @event initial
41892          * Fire after initEvent
41893          * @param {Roo.bootstrap.DocumentSlider} this
41894          */
41895         "initial" : true,
41896         /**
41897          * @event update
41898          * Fire after update
41899          * @param {Roo.bootstrap.DocumentSlider} this
41900          */
41901         "update" : true,
41902         /**
41903          * @event click
41904          * Fire after click
41905          * @param {Roo.bootstrap.DocumentSlider} this
41906          */
41907         "click" : true
41908     });
41909 };
41910
41911 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41912     
41913     files : false,
41914     
41915     indicator : 0,
41916     
41917     getAutoCreate : function()
41918     {
41919         var cfg = {
41920             tag : 'div',
41921             cls : 'roo-document-slider',
41922             cn : [
41923                 {
41924                     tag : 'div',
41925                     cls : 'roo-document-slider-header',
41926                     cn : [
41927                         {
41928                             tag : 'div',
41929                             cls : 'roo-document-slider-header-title'
41930                         }
41931                     ]
41932                 },
41933                 {
41934                     tag : 'div',
41935                     cls : 'roo-document-slider-body',
41936                     cn : [
41937                         {
41938                             tag : 'div',
41939                             cls : 'roo-document-slider-prev',
41940                             cn : [
41941                                 {
41942                                     tag : 'i',
41943                                     cls : 'fa fa-chevron-left'
41944                                 }
41945                             ]
41946                         },
41947                         {
41948                             tag : 'div',
41949                             cls : 'roo-document-slider-thumb',
41950                             cn : [
41951                                 {
41952                                     tag : 'img',
41953                                     cls : 'roo-document-slider-image'
41954                                 }
41955                             ]
41956                         },
41957                         {
41958                             tag : 'div',
41959                             cls : 'roo-document-slider-next',
41960                             cn : [
41961                                 {
41962                                     tag : 'i',
41963                                     cls : 'fa fa-chevron-right'
41964                                 }
41965                             ]
41966                         }
41967                     ]
41968                 }
41969             ]
41970         };
41971         
41972         return cfg;
41973     },
41974     
41975     initEvents : function()
41976     {
41977         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41978         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41979         
41980         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41981         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41982         
41983         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41984         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41985         
41986         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41987         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41988         
41989         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41990         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41991         
41992         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41993         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41994         
41995         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41996         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41997         
41998         this.thumbEl.on('click', this.onClick, this);
41999         
42000         this.prevIndicator.on('click', this.prev, this);
42001         
42002         this.nextIndicator.on('click', this.next, this);
42003         
42004     },
42005     
42006     initial : function()
42007     {
42008         if(this.files.length){
42009             this.indicator = 1;
42010             this.update()
42011         }
42012         
42013         this.fireEvent('initial', this);
42014     },
42015     
42016     update : function()
42017     {
42018         this.imageEl.attr('src', this.files[this.indicator - 1]);
42019         
42020         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42021         
42022         this.prevIndicator.show();
42023         
42024         if(this.indicator == 1){
42025             this.prevIndicator.hide();
42026         }
42027         
42028         this.nextIndicator.show();
42029         
42030         if(this.indicator == this.files.length){
42031             this.nextIndicator.hide();
42032         }
42033         
42034         this.thumbEl.scrollTo('top');
42035         
42036         this.fireEvent('update', this);
42037     },
42038     
42039     onClick : function(e)
42040     {
42041         e.preventDefault();
42042         
42043         this.fireEvent('click', this);
42044     },
42045     
42046     prev : function(e)
42047     {
42048         e.preventDefault();
42049         
42050         this.indicator = Math.max(1, this.indicator - 1);
42051         
42052         this.update();
42053     },
42054     
42055     next : function(e)
42056     {
42057         e.preventDefault();
42058         
42059         this.indicator = Math.min(this.files.length, this.indicator + 1);
42060         
42061         this.update();
42062     }
42063 });
42064 /*
42065  * - LGPL
42066  *
42067  * RadioSet
42068  *
42069  *
42070  */
42071
42072 /**
42073  * @class Roo.bootstrap.form.RadioSet
42074  * @extends Roo.bootstrap.form.Input
42075  * @children Roo.bootstrap.form.Radio
42076  * Bootstrap RadioSet class
42077  * @cfg {String} indicatorpos (left|right) default left
42078  * @cfg {Boolean} inline (true|false) inline the element (default true)
42079  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42080  * @constructor
42081  * Create a new RadioSet
42082  * @param {Object} config The config object
42083  */
42084
42085 Roo.bootstrap.form.RadioSet = function(config){
42086     
42087     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42088     
42089     this.radioes = [];
42090     
42091     Roo.bootstrap.form.RadioSet.register(this);
42092     
42093     this.addEvents({
42094         /**
42095         * @event check
42096         * Fires when the element is checked or unchecked.
42097         * @param {Roo.bootstrap.form.RadioSet} this This radio
42098         * @param {Roo.bootstrap.form.Radio} item The checked item
42099         */
42100        check : true,
42101        /**
42102         * @event click
42103         * Fires when the element is click.
42104         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42105         * @param {Roo.bootstrap.form.Radio} item The checked item
42106         * @param {Roo.EventObject} e The event object
42107         */
42108        click : true
42109     });
42110     
42111 };
42112
42113 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42114
42115     radioes : false,
42116     
42117     inline : true,
42118     
42119     weight : '',
42120     
42121     indicatorpos : 'left',
42122     
42123     getAutoCreate : function()
42124     {
42125         var label = {
42126             tag : 'label',
42127             cls : 'roo-radio-set-label',
42128             cn : [
42129                 {
42130                     tag : 'span',
42131                     html : this.fieldLabel
42132                 }
42133             ]
42134         };
42135         if (Roo.bootstrap.version == 3) {
42136             
42137             
42138             if(this.indicatorpos == 'left'){
42139                 label.cn.unshift({
42140                     tag : 'i',
42141                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42142                     tooltip : 'This field is required'
42143                 });
42144             } else {
42145                 label.cn.push({
42146                     tag : 'i',
42147                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42148                     tooltip : 'This field is required'
42149                 });
42150             }
42151         }
42152         var items = {
42153             tag : 'div',
42154             cls : 'roo-radio-set-items'
42155         };
42156         
42157         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42158         
42159         if (align === 'left' && this.fieldLabel.length) {
42160             
42161             items = {
42162                 cls : "roo-radio-set-right", 
42163                 cn: [
42164                     items
42165                 ]
42166             };
42167             
42168             if(this.labelWidth > 12){
42169                 label.style = "width: " + this.labelWidth + 'px';
42170             }
42171             
42172             if(this.labelWidth < 13 && this.labelmd == 0){
42173                 this.labelmd = this.labelWidth;
42174             }
42175             
42176             if(this.labellg > 0){
42177                 label.cls += ' col-lg-' + this.labellg;
42178                 items.cls += ' col-lg-' + (12 - this.labellg);
42179             }
42180             
42181             if(this.labelmd > 0){
42182                 label.cls += ' col-md-' + this.labelmd;
42183                 items.cls += ' col-md-' + (12 - this.labelmd);
42184             }
42185             
42186             if(this.labelsm > 0){
42187                 label.cls += ' col-sm-' + this.labelsm;
42188                 items.cls += ' col-sm-' + (12 - this.labelsm);
42189             }
42190             
42191             if(this.labelxs > 0){
42192                 label.cls += ' col-xs-' + this.labelxs;
42193                 items.cls += ' col-xs-' + (12 - this.labelxs);
42194             }
42195         }
42196         
42197         var cfg = {
42198             tag : 'div',
42199             cls : 'roo-radio-set',
42200             cn : [
42201                 {
42202                     tag : 'input',
42203                     cls : 'roo-radio-set-input',
42204                     type : 'hidden',
42205                     name : this.name,
42206                     value : this.value ? this.value :  ''
42207                 },
42208                 label,
42209                 items
42210             ]
42211         };
42212         
42213         if(this.weight.length){
42214             cfg.cls += ' roo-radio-' + this.weight;
42215         }
42216         
42217         if(this.inline) {
42218             cfg.cls += ' roo-radio-set-inline';
42219         }
42220         
42221         var settings=this;
42222         ['xs','sm','md','lg'].map(function(size){
42223             if (settings[size]) {
42224                 cfg.cls += ' col-' + size + '-' + settings[size];
42225             }
42226         });
42227         
42228         return cfg;
42229         
42230     },
42231
42232     initEvents : function()
42233     {
42234         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42235         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42236         
42237         if(!this.fieldLabel.length){
42238             this.labelEl.hide();
42239         }
42240         
42241         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42242         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42243         
42244         this.indicator = this.indicatorEl();
42245         
42246         if(this.indicator){
42247             this.indicator.addClass('invisible');
42248         }
42249         
42250         this.originalValue = this.getValue();
42251         
42252     },
42253     
42254     inputEl: function ()
42255     {
42256         return this.el.select('.roo-radio-set-input', true).first();
42257     },
42258     
42259     getChildContainer : function()
42260     {
42261         return this.itemsEl;
42262     },
42263     
42264     register : function(item)
42265     {
42266         this.radioes.push(item);
42267         
42268     },
42269     
42270     validate : function()
42271     {   
42272         if(this.getVisibilityEl().hasClass('hidden')){
42273             return true;
42274         }
42275         
42276         var valid = false;
42277         
42278         Roo.each(this.radioes, function(i){
42279             if(!i.checked){
42280                 return;
42281             }
42282             
42283             valid = true;
42284             return false;
42285         });
42286         
42287         if(this.allowBlank) {
42288             return true;
42289         }
42290         
42291         if(this.disabled || valid){
42292             this.markValid();
42293             return true;
42294         }
42295         
42296         this.markInvalid();
42297         return false;
42298         
42299     },
42300     
42301     markValid : function()
42302     {
42303         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42304             this.indicatorEl().removeClass('visible');
42305             this.indicatorEl().addClass('invisible');
42306         }
42307         
42308         
42309         if (Roo.bootstrap.version == 3) {
42310             this.el.removeClass([this.invalidClass, this.validClass]);
42311             this.el.addClass(this.validClass);
42312         } else {
42313             this.el.removeClass(['is-invalid','is-valid']);
42314             this.el.addClass(['is-valid']);
42315         }
42316         this.fireEvent('valid', this);
42317     },
42318     
42319     markInvalid : function(msg)
42320     {
42321         if(this.allowBlank || this.disabled){
42322             return;
42323         }
42324         
42325         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42326             this.indicatorEl().removeClass('invisible');
42327             this.indicatorEl().addClass('visible');
42328         }
42329         if (Roo.bootstrap.version == 3) {
42330             this.el.removeClass([this.invalidClass, this.validClass]);
42331             this.el.addClass(this.invalidClass);
42332         } else {
42333             this.el.removeClass(['is-invalid','is-valid']);
42334             this.el.addClass(['is-invalid']);
42335         }
42336         
42337         this.fireEvent('invalid', this, msg);
42338         
42339     },
42340     
42341     setValue : function(v, suppressEvent)
42342     {   
42343         if(this.value === v){
42344             return;
42345         }
42346         
42347         this.value = v;
42348         
42349         if(this.rendered){
42350             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42351         }
42352         
42353         Roo.each(this.radioes, function(i){
42354             i.checked = false;
42355             i.el.removeClass('checked');
42356         });
42357         
42358         Roo.each(this.radioes, function(i){
42359             
42360             if(i.value === v || i.value.toString() === v.toString()){
42361                 i.checked = true;
42362                 i.el.addClass('checked');
42363                 
42364                 if(suppressEvent !== true){
42365                     this.fireEvent('check', this, i);
42366                 }
42367                 
42368                 return false;
42369             }
42370             
42371         }, this);
42372         
42373         this.validate();
42374     },
42375     
42376     clearInvalid : function(){
42377         
42378         if(!this.el || this.preventMark){
42379             return;
42380         }
42381         
42382         this.el.removeClass([this.invalidClass]);
42383         
42384         this.fireEvent('valid', this);
42385     }
42386     
42387 });
42388
42389 Roo.apply(Roo.bootstrap.form.RadioSet, {
42390     
42391     groups: {},
42392     
42393     register : function(set)
42394     {
42395         this.groups[set.name] = set;
42396     },
42397     
42398     get: function(name) 
42399     {
42400         if (typeof(this.groups[name]) == 'undefined') {
42401             return false;
42402         }
42403         
42404         return this.groups[name] ;
42405     }
42406     
42407 });
42408 /*
42409  * Based on:
42410  * Ext JS Library 1.1.1
42411  * Copyright(c) 2006-2007, Ext JS, LLC.
42412  *
42413  * Originally Released Under LGPL - original licence link has changed is not relivant.
42414  *
42415  * Fork - LGPL
42416  * <script type="text/javascript">
42417  */
42418
42419
42420 /**
42421  * @class Roo.bootstrap.SplitBar
42422  * @extends Roo.util.Observable
42423  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42424  * <br><br>
42425  * Usage:
42426  * <pre><code>
42427 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42428                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42429 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42430 split.minSize = 100;
42431 split.maxSize = 600;
42432 split.animate = true;
42433 split.on('moved', splitterMoved);
42434 </code></pre>
42435  * @constructor
42436  * Create a new SplitBar
42437  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42438  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42439  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42440  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42441                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42442                         position of the SplitBar).
42443  */
42444 Roo.bootstrap.SplitBar = function(cfg){
42445     
42446     /** @private */
42447     
42448     //{
42449     //  dragElement : elm
42450     //  resizingElement: el,
42451         // optional..
42452     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42453     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42454         // existingProxy ???
42455     //}
42456     
42457     this.el = Roo.get(cfg.dragElement, true);
42458     this.el.dom.unselectable = "on";
42459     /** @private */
42460     this.resizingEl = Roo.get(cfg.resizingElement, true);
42461
42462     /**
42463      * @private
42464      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42465      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42466      * @type Number
42467      */
42468     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42469     
42470     /**
42471      * The minimum size of the resizing element. (Defaults to 0)
42472      * @type Number
42473      */
42474     this.minSize = 0;
42475     
42476     /**
42477      * The maximum size of the resizing element. (Defaults to 2000)
42478      * @type Number
42479      */
42480     this.maxSize = 2000;
42481     
42482     /**
42483      * Whether to animate the transition to the new size
42484      * @type Boolean
42485      */
42486     this.animate = false;
42487     
42488     /**
42489      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42490      * @type Boolean
42491      */
42492     this.useShim = false;
42493     
42494     /** @private */
42495     this.shim = null;
42496     
42497     if(!cfg.existingProxy){
42498         /** @private */
42499         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42500     }else{
42501         this.proxy = Roo.get(cfg.existingProxy).dom;
42502     }
42503     /** @private */
42504     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42505     
42506     /** @private */
42507     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42508     
42509     /** @private */
42510     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42511     
42512     /** @private */
42513     this.dragSpecs = {};
42514     
42515     /**
42516      * @private The adapter to use to positon and resize elements
42517      */
42518     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42519     this.adapter.init(this);
42520     
42521     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42522         /** @private */
42523         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42524         this.el.addClass("roo-splitbar-h");
42525     }else{
42526         /** @private */
42527         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42528         this.el.addClass("roo-splitbar-v");
42529     }
42530     
42531     this.addEvents({
42532         /**
42533          * @event resize
42534          * Fires when the splitter is moved (alias for {@link #event-moved})
42535          * @param {Roo.bootstrap.SplitBar} this
42536          * @param {Number} newSize the new width or height
42537          */
42538         "resize" : true,
42539         /**
42540          * @event moved
42541          * Fires when the splitter is moved
42542          * @param {Roo.bootstrap.SplitBar} this
42543          * @param {Number} newSize the new width or height
42544          */
42545         "moved" : true,
42546         /**
42547          * @event beforeresize
42548          * Fires before the splitter is dragged
42549          * @param {Roo.bootstrap.SplitBar} this
42550          */
42551         "beforeresize" : true,
42552
42553         "beforeapply" : true
42554     });
42555
42556     Roo.util.Observable.call(this);
42557 };
42558
42559 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42560     onStartProxyDrag : function(x, y){
42561         this.fireEvent("beforeresize", this);
42562         if(!this.overlay){
42563             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42564             o.unselectable();
42565             o.enableDisplayMode("block");
42566             // all splitbars share the same overlay
42567             Roo.bootstrap.SplitBar.prototype.overlay = o;
42568         }
42569         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42570         this.overlay.show();
42571         Roo.get(this.proxy).setDisplayed("block");
42572         var size = this.adapter.getElementSize(this);
42573         this.activeMinSize = this.getMinimumSize();;
42574         this.activeMaxSize = this.getMaximumSize();;
42575         var c1 = size - this.activeMinSize;
42576         var c2 = Math.max(this.activeMaxSize - size, 0);
42577         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42578             this.dd.resetConstraints();
42579             this.dd.setXConstraint(
42580                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42581                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42582             );
42583             this.dd.setYConstraint(0, 0);
42584         }else{
42585             this.dd.resetConstraints();
42586             this.dd.setXConstraint(0, 0);
42587             this.dd.setYConstraint(
42588                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42589                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42590             );
42591          }
42592         this.dragSpecs.startSize = size;
42593         this.dragSpecs.startPoint = [x, y];
42594         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42595     },
42596     
42597     /** 
42598      * @private Called after the drag operation by the DDProxy
42599      */
42600     onEndProxyDrag : function(e){
42601         Roo.get(this.proxy).setDisplayed(false);
42602         var endPoint = Roo.lib.Event.getXY(e);
42603         if(this.overlay){
42604             this.overlay.hide();
42605         }
42606         var newSize;
42607         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42608             newSize = this.dragSpecs.startSize + 
42609                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42610                     endPoint[0] - this.dragSpecs.startPoint[0] :
42611                     this.dragSpecs.startPoint[0] - endPoint[0]
42612                 );
42613         }else{
42614             newSize = this.dragSpecs.startSize + 
42615                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42616                     endPoint[1] - this.dragSpecs.startPoint[1] :
42617                     this.dragSpecs.startPoint[1] - endPoint[1]
42618                 );
42619         }
42620         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42621         if(newSize != this.dragSpecs.startSize){
42622             if(this.fireEvent('beforeapply', this, newSize) !== false){
42623                 this.adapter.setElementSize(this, newSize);
42624                 this.fireEvent("moved", this, newSize);
42625                 this.fireEvent("resize", this, newSize);
42626             }
42627         }
42628     },
42629     
42630     /**
42631      * Get the adapter this SplitBar uses
42632      * @return The adapter object
42633      */
42634     getAdapter : function(){
42635         return this.adapter;
42636     },
42637     
42638     /**
42639      * Set the adapter this SplitBar uses
42640      * @param {Object} adapter A SplitBar adapter object
42641      */
42642     setAdapter : function(adapter){
42643         this.adapter = adapter;
42644         this.adapter.init(this);
42645     },
42646     
42647     /**
42648      * Gets the minimum size for the resizing element
42649      * @return {Number} The minimum size
42650      */
42651     getMinimumSize : function(){
42652         return this.minSize;
42653     },
42654     
42655     /**
42656      * Sets the minimum size for the resizing element
42657      * @param {Number} minSize The minimum size
42658      */
42659     setMinimumSize : function(minSize){
42660         this.minSize = minSize;
42661     },
42662     
42663     /**
42664      * Gets the maximum size for the resizing element
42665      * @return {Number} The maximum size
42666      */
42667     getMaximumSize : function(){
42668         return this.maxSize;
42669     },
42670     
42671     /**
42672      * Sets the maximum size for the resizing element
42673      * @param {Number} maxSize The maximum size
42674      */
42675     setMaximumSize : function(maxSize){
42676         this.maxSize = maxSize;
42677     },
42678     
42679     /**
42680      * Sets the initialize size for the resizing element
42681      * @param {Number} size The initial size
42682      */
42683     setCurrentSize : function(size){
42684         var oldAnimate = this.animate;
42685         this.animate = false;
42686         this.adapter.setElementSize(this, size);
42687         this.animate = oldAnimate;
42688     },
42689     
42690     /**
42691      * Destroy this splitbar. 
42692      * @param {Boolean} removeEl True to remove the element
42693      */
42694     destroy : function(removeEl){
42695         if(this.shim){
42696             this.shim.remove();
42697         }
42698         this.dd.unreg();
42699         this.proxy.parentNode.removeChild(this.proxy);
42700         if(removeEl){
42701             this.el.remove();
42702         }
42703     }
42704 });
42705
42706 /**
42707  * @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.
42708  */
42709 Roo.bootstrap.SplitBar.createProxy = function(dir){
42710     var proxy = new Roo.Element(document.createElement("div"));
42711     proxy.unselectable();
42712     var cls = 'roo-splitbar-proxy';
42713     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42714     document.body.appendChild(proxy.dom);
42715     return proxy.dom;
42716 };
42717
42718 /** 
42719  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42720  * Default Adapter. It assumes the splitter and resizing element are not positioned
42721  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42722  */
42723 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42724 };
42725
42726 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42727     // do nothing for now
42728     init : function(s){
42729     
42730     },
42731     /**
42732      * Called before drag operations to get the current size of the resizing element. 
42733      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42734      */
42735      getElementSize : function(s){
42736         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42737             return s.resizingEl.getWidth();
42738         }else{
42739             return s.resizingEl.getHeight();
42740         }
42741     },
42742     
42743     /**
42744      * Called after drag operations to set the size of the resizing element.
42745      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42746      * @param {Number} newSize The new size to set
42747      * @param {Function} onComplete A function to be invoked when resizing is complete
42748      */
42749     setElementSize : function(s, newSize, onComplete){
42750         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42751             if(!s.animate){
42752                 s.resizingEl.setWidth(newSize);
42753                 if(onComplete){
42754                     onComplete(s, newSize);
42755                 }
42756             }else{
42757                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42758             }
42759         }else{
42760             
42761             if(!s.animate){
42762                 s.resizingEl.setHeight(newSize);
42763                 if(onComplete){
42764                     onComplete(s, newSize);
42765                 }
42766             }else{
42767                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42768             }
42769         }
42770     }
42771 };
42772
42773 /** 
42774  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42775  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42776  * Adapter that  moves the splitter element to align with the resized sizing element. 
42777  * Used with an absolute positioned SplitBar.
42778  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42779  * document.body, make sure you assign an id to the body element.
42780  */
42781 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42782     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42783     this.container = Roo.get(container);
42784 };
42785
42786 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42787     init : function(s){
42788         this.basic.init(s);
42789     },
42790     
42791     getElementSize : function(s){
42792         return this.basic.getElementSize(s);
42793     },
42794     
42795     setElementSize : function(s, newSize, onComplete){
42796         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42797     },
42798     
42799     moveSplitter : function(s){
42800         var yes = Roo.bootstrap.SplitBar;
42801         switch(s.placement){
42802             case yes.LEFT:
42803                 s.el.setX(s.resizingEl.getRight());
42804                 break;
42805             case yes.RIGHT:
42806                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42807                 break;
42808             case yes.TOP:
42809                 s.el.setY(s.resizingEl.getBottom());
42810                 break;
42811             case yes.BOTTOM:
42812                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42813                 break;
42814         }
42815     }
42816 };
42817
42818 /**
42819  * Orientation constant - Create a vertical SplitBar
42820  * @static
42821  * @type Number
42822  */
42823 Roo.bootstrap.SplitBar.VERTICAL = 1;
42824
42825 /**
42826  * Orientation constant - Create a horizontal SplitBar
42827  * @static
42828  * @type Number
42829  */
42830 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42831
42832 /**
42833  * Placement constant - The resizing element is to the left of the splitter element
42834  * @static
42835  * @type Number
42836  */
42837 Roo.bootstrap.SplitBar.LEFT = 1;
42838
42839 /**
42840  * Placement constant - The resizing element is to the right of the splitter element
42841  * @static
42842  * @type Number
42843  */
42844 Roo.bootstrap.SplitBar.RIGHT = 2;
42845
42846 /**
42847  * Placement constant - The resizing element is positioned above the splitter element
42848  * @static
42849  * @type Number
42850  */
42851 Roo.bootstrap.SplitBar.TOP = 3;
42852
42853 /**
42854  * Placement constant - The resizing element is positioned under splitter element
42855  * @static
42856  * @type Number
42857  */
42858 Roo.bootstrap.SplitBar.BOTTOM = 4;
42859 /*
42860  * Based on:
42861  * Ext JS Library 1.1.1
42862  * Copyright(c) 2006-2007, Ext JS, LLC.
42863  *
42864  * Originally Released Under LGPL - original licence link has changed is not relivant.
42865  *
42866  * Fork - LGPL
42867  * <script type="text/javascript">
42868  */
42869
42870 /**
42871  * @class Roo.bootstrap.layout.Manager
42872  * @extends Roo.bootstrap.Component
42873  * @abstract
42874  * Base class for layout managers.
42875  */
42876 Roo.bootstrap.layout.Manager = function(config)
42877 {
42878     this.monitorWindowResize = true; // do this before we apply configuration.
42879     
42880     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42881
42882
42883
42884
42885
42886     /** false to disable window resize monitoring @type Boolean */
42887     
42888     this.regions = {};
42889     this.addEvents({
42890         /**
42891          * @event layout
42892          * Fires when a layout is performed.
42893          * @param {Roo.LayoutManager} this
42894          */
42895         "layout" : true,
42896         /**
42897          * @event regionresized
42898          * Fires when the user resizes a region.
42899          * @param {Roo.LayoutRegion} region The resized region
42900          * @param {Number} newSize The new size (width for east/west, height for north/south)
42901          */
42902         "regionresized" : true,
42903         /**
42904          * @event regioncollapsed
42905          * Fires when a region is collapsed.
42906          * @param {Roo.LayoutRegion} region The collapsed region
42907          */
42908         "regioncollapsed" : true,
42909         /**
42910          * @event regionexpanded
42911          * Fires when a region is expanded.
42912          * @param {Roo.LayoutRegion} region The expanded region
42913          */
42914         "regionexpanded" : true
42915     });
42916     this.updating = false;
42917
42918     if (config.el) {
42919         this.el = Roo.get(config.el);
42920         this.initEvents();
42921     }
42922
42923 };
42924
42925 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42926
42927
42928     regions : null,
42929
42930     monitorWindowResize : true,
42931
42932
42933     updating : false,
42934
42935
42936     onRender : function(ct, position)
42937     {
42938         if(!this.el){
42939             this.el = Roo.get(ct);
42940             this.initEvents();
42941         }
42942         //this.fireEvent('render',this);
42943     },
42944
42945
42946     initEvents: function()
42947     {
42948
42949
42950         // ie scrollbar fix
42951         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42952             document.body.scroll = "no";
42953         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42954             this.el.position('relative');
42955         }
42956         this.id = this.el.id;
42957         this.el.addClass("roo-layout-container");
42958         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42959         if(this.el.dom != document.body ) {
42960             this.el.on('resize', this.layout,this);
42961             this.el.on('show', this.layout,this);
42962         }
42963
42964     },
42965
42966     /**
42967      * Returns true if this layout is currently being updated
42968      * @return {Boolean}
42969      */
42970     isUpdating : function(){
42971         return this.updating;
42972     },
42973
42974     /**
42975      * Suspend the LayoutManager from doing auto-layouts while
42976      * making multiple add or remove calls
42977      */
42978     beginUpdate : function(){
42979         this.updating = true;
42980     },
42981
42982     /**
42983      * Restore auto-layouts and optionally disable the manager from performing a layout
42984      * @param {Boolean} noLayout true to disable a layout update
42985      */
42986     endUpdate : function(noLayout){
42987         this.updating = false;
42988         if(!noLayout){
42989             this.layout();
42990         }
42991     },
42992
42993     layout: function(){
42994         // abstract...
42995     },
42996
42997     onRegionResized : function(region, newSize){
42998         this.fireEvent("regionresized", region, newSize);
42999         this.layout();
43000     },
43001
43002     onRegionCollapsed : function(region){
43003         this.fireEvent("regioncollapsed", region);
43004     },
43005
43006     onRegionExpanded : function(region){
43007         this.fireEvent("regionexpanded", region);
43008     },
43009
43010     /**
43011      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43012      * performs box-model adjustments.
43013      * @return {Object} The size as an object {width: (the width), height: (the height)}
43014      */
43015     getViewSize : function()
43016     {
43017         var size;
43018         if(this.el.dom != document.body){
43019             size = this.el.getSize();
43020         }else{
43021             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43022         }
43023         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43024         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43025         return size;
43026     },
43027
43028     /**
43029      * Returns the Element this layout is bound to.
43030      * @return {Roo.Element}
43031      */
43032     getEl : function(){
43033         return this.el;
43034     },
43035
43036     /**
43037      * Returns the specified region.
43038      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43039      * @return {Roo.LayoutRegion}
43040      */
43041     getRegion : function(target){
43042         return this.regions[target.toLowerCase()];
43043     },
43044
43045     onWindowResize : function(){
43046         if(this.monitorWindowResize){
43047             this.layout();
43048         }
43049     }
43050 });
43051 /*
43052  * Based on:
43053  * Ext JS Library 1.1.1
43054  * Copyright(c) 2006-2007, Ext JS, LLC.
43055  *
43056  * Originally Released Under LGPL - original licence link has changed is not relivant.
43057  *
43058  * Fork - LGPL
43059  * <script type="text/javascript">
43060  */
43061 /**
43062  * @class Roo.bootstrap.layout.Border
43063  * @extends Roo.bootstrap.layout.Manager
43064  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43065  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43066  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43067  * please see: examples/bootstrap/nested.html<br><br>
43068  
43069 <b>The container the layout is rendered into can be either the body element or any other element.
43070 If it is not the body element, the container needs to either be an absolute positioned element,
43071 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43072 the container size if it is not the body element.</b>
43073
43074 * @constructor
43075 * Create a new Border
43076 * @param {Object} config Configuration options
43077  */
43078 Roo.bootstrap.layout.Border = function(config){
43079     config = config || {};
43080     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43081     
43082     
43083     
43084     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43085         if(config[region]){
43086             config[region].region = region;
43087             this.addRegion(config[region]);
43088         }
43089     },this);
43090     
43091 };
43092
43093 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43094
43095 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43096     
43097         /**
43098          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43099          */
43100         /**
43101          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43102          */
43103         /**
43104          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43105          */
43106         /**
43107          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43108          */
43109         /**
43110          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43111          */
43112         
43113         
43114         
43115         
43116     parent : false, // this might point to a 'nest' or a ???
43117     
43118     /**
43119      * Creates and adds a new region if it doesn't already exist.
43120      * @param {String} target The target region key (north, south, east, west or center).
43121      * @param {Object} config The regions config object
43122      * @return {BorderLayoutRegion} The new region
43123      */
43124     addRegion : function(config)
43125     {
43126         if(!this.regions[config.region]){
43127             var r = this.factory(config);
43128             this.bindRegion(r);
43129         }
43130         return this.regions[config.region];
43131     },
43132
43133     // private (kinda)
43134     bindRegion : function(r){
43135         this.regions[r.config.region] = r;
43136         
43137         r.on("visibilitychange",    this.layout, this);
43138         r.on("paneladded",          this.layout, this);
43139         r.on("panelremoved",        this.layout, this);
43140         r.on("invalidated",         this.layout, this);
43141         r.on("resized",             this.onRegionResized, this);
43142         r.on("collapsed",           this.onRegionCollapsed, this);
43143         r.on("expanded",            this.onRegionExpanded, this);
43144     },
43145
43146     /**
43147      * Performs a layout update.
43148      */
43149     layout : function()
43150     {
43151         if(this.updating) {
43152             return;
43153         }
43154         
43155         // render all the rebions if they have not been done alreayd?
43156         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43157             if(this.regions[region] && !this.regions[region].bodyEl){
43158                 this.regions[region].onRender(this.el)
43159             }
43160         },this);
43161         
43162         var size = this.getViewSize();
43163         var w = size.width;
43164         var h = size.height;
43165         var centerW = w;
43166         var centerH = h;
43167         var centerY = 0;
43168         var centerX = 0;
43169         //var x = 0, y = 0;
43170
43171         var rs = this.regions;
43172         var north = rs["north"];
43173         var south = rs["south"]; 
43174         var west = rs["west"];
43175         var east = rs["east"];
43176         var center = rs["center"];
43177         //if(this.hideOnLayout){ // not supported anymore
43178             //c.el.setStyle("display", "none");
43179         //}
43180         if(north && north.isVisible()){
43181             var b = north.getBox();
43182             var m = north.getMargins();
43183             b.width = w - (m.left+m.right);
43184             b.x = m.left;
43185             b.y = m.top;
43186             centerY = b.height + b.y + m.bottom;
43187             centerH -= centerY;
43188             north.updateBox(this.safeBox(b));
43189         }
43190         if(south && south.isVisible()){
43191             var b = south.getBox();
43192             var m = south.getMargins();
43193             b.width = w - (m.left+m.right);
43194             b.x = m.left;
43195             var totalHeight = (b.height + m.top + m.bottom);
43196             b.y = h - totalHeight + m.top;
43197             centerH -= totalHeight;
43198             south.updateBox(this.safeBox(b));
43199         }
43200         if(west && west.isVisible()){
43201             var b = west.getBox();
43202             var m = west.getMargins();
43203             b.height = centerH - (m.top+m.bottom);
43204             b.x = m.left;
43205             b.y = centerY + m.top;
43206             var totalWidth = (b.width + m.left + m.right);
43207             centerX += totalWidth;
43208             centerW -= totalWidth;
43209             west.updateBox(this.safeBox(b));
43210         }
43211         if(east && east.isVisible()){
43212             var b = east.getBox();
43213             var m = east.getMargins();
43214             b.height = centerH - (m.top+m.bottom);
43215             var totalWidth = (b.width + m.left + m.right);
43216             b.x = w - totalWidth + m.left;
43217             b.y = centerY + m.top;
43218             centerW -= totalWidth;
43219             east.updateBox(this.safeBox(b));
43220         }
43221         if(center){
43222             var m = center.getMargins();
43223             var centerBox = {
43224                 x: centerX + m.left,
43225                 y: centerY + m.top,
43226                 width: centerW - (m.left+m.right),
43227                 height: centerH - (m.top+m.bottom)
43228             };
43229             //if(this.hideOnLayout){
43230                 //center.el.setStyle("display", "block");
43231             //}
43232             center.updateBox(this.safeBox(centerBox));
43233         }
43234         this.el.repaint();
43235         this.fireEvent("layout", this);
43236     },
43237
43238     // private
43239     safeBox : function(box){
43240         box.width = Math.max(0, box.width);
43241         box.height = Math.max(0, box.height);
43242         return box;
43243     },
43244
43245     /**
43246      * Adds a ContentPanel (or subclass) to this layout.
43247      * @param {String} target The target region key (north, south, east, west or center).
43248      * @param {Roo.ContentPanel} panel The panel to add
43249      * @return {Roo.ContentPanel} The added panel
43250      */
43251     add : function(target, panel){
43252          
43253         target = target.toLowerCase();
43254         return this.regions[target].add(panel);
43255     },
43256
43257     /**
43258      * Remove a ContentPanel (or subclass) to this layout.
43259      * @param {String} target The target region key (north, south, east, west or center).
43260      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43261      * @return {Roo.ContentPanel} The removed panel
43262      */
43263     remove : function(target, panel){
43264         target = target.toLowerCase();
43265         return this.regions[target].remove(panel);
43266     },
43267
43268     /**
43269      * Searches all regions for a panel with the specified id
43270      * @param {String} panelId
43271      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43272      */
43273     findPanel : function(panelId){
43274         var rs = this.regions;
43275         for(var target in rs){
43276             if(typeof rs[target] != "function"){
43277                 var p = rs[target].getPanel(panelId);
43278                 if(p){
43279                     return p;
43280                 }
43281             }
43282         }
43283         return null;
43284     },
43285
43286     /**
43287      * Searches all regions for a panel with the specified id and activates (shows) it.
43288      * @param {String/ContentPanel} panelId The panels id or the panel itself
43289      * @return {Roo.ContentPanel} The shown panel or null
43290      */
43291     showPanel : function(panelId) {
43292       var rs = this.regions;
43293       for(var target in rs){
43294          var r = rs[target];
43295          if(typeof r != "function"){
43296             if(r.hasPanel(panelId)){
43297                return r.showPanel(panelId);
43298             }
43299          }
43300       }
43301       return null;
43302    },
43303
43304    /**
43305      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43306      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43307      */
43308    /*
43309     restoreState : function(provider){
43310         if(!provider){
43311             provider = Roo.state.Manager;
43312         }
43313         var sm = new Roo.LayoutStateManager();
43314         sm.init(this, provider);
43315     },
43316 */
43317  
43318  
43319     /**
43320      * Adds a xtype elements to the layout.
43321      * <pre><code>
43322
43323 layout.addxtype({
43324        xtype : 'ContentPanel',
43325        region: 'west',
43326        items: [ .... ]
43327    }
43328 );
43329
43330 layout.addxtype({
43331         xtype : 'NestedLayoutPanel',
43332         region: 'west',
43333         layout: {
43334            center: { },
43335            west: { }   
43336         },
43337         items : [ ... list of content panels or nested layout panels.. ]
43338    }
43339 );
43340 </code></pre>
43341      * @param {Object} cfg Xtype definition of item to add.
43342      */
43343     addxtype : function(cfg)
43344     {
43345         // basically accepts a pannel...
43346         // can accept a layout region..!?!?
43347         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43348         
43349         
43350         // theory?  children can only be panels??
43351         
43352         //if (!cfg.xtype.match(/Panel$/)) {
43353         //    return false;
43354         //}
43355         var ret = false;
43356         
43357         if (typeof(cfg.region) == 'undefined') {
43358             Roo.log("Failed to add Panel, region was not set");
43359             Roo.log(cfg);
43360             return false;
43361         }
43362         var region = cfg.region;
43363         delete cfg.region;
43364         
43365           
43366         var xitems = [];
43367         if (cfg.items) {
43368             xitems = cfg.items;
43369             delete cfg.items;
43370         }
43371         var nb = false;
43372         
43373         if ( region == 'center') {
43374             Roo.log("Center: " + cfg.title);
43375         }
43376         
43377         
43378         switch(cfg.xtype) 
43379         {
43380             case 'Content':  // ContentPanel (el, cfg)
43381             case 'Scroll':  // ContentPanel (el, cfg)
43382             case 'View': 
43383                 cfg.autoCreate = cfg.autoCreate || true;
43384                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43385                 //} else {
43386                 //    var el = this.el.createChild();
43387                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43388                 //}
43389                 
43390                 this.add(region, ret);
43391                 break;
43392             
43393             /*
43394             case 'TreePanel': // our new panel!
43395                 cfg.el = this.el.createChild();
43396                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43397                 this.add(region, ret);
43398                 break;
43399             */
43400             
43401             case 'Nest': 
43402                 // create a new Layout (which is  a Border Layout...
43403                 
43404                 var clayout = cfg.layout;
43405                 clayout.el  = this.el.createChild();
43406                 clayout.items   = clayout.items  || [];
43407                 
43408                 delete cfg.layout;
43409                 
43410                 // replace this exitems with the clayout ones..
43411                 xitems = clayout.items;
43412                  
43413                 // force background off if it's in center...
43414                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43415                     cfg.background = false;
43416                 }
43417                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43418                 
43419                 
43420                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43421                 //console.log('adding nested layout panel '  + cfg.toSource());
43422                 this.add(region, ret);
43423                 nb = {}; /// find first...
43424                 break;
43425             
43426             case 'Grid':
43427                 
43428                 // needs grid and region
43429                 
43430                 //var el = this.getRegion(region).el.createChild();
43431                 /*
43432                  *var el = this.el.createChild();
43433                 // create the grid first...
43434                 cfg.grid.container = el;
43435                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43436                 */
43437                 
43438                 if (region == 'center' && this.active ) {
43439                     cfg.background = false;
43440                 }
43441                 
43442                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43443                 
43444                 this.add(region, ret);
43445                 /*
43446                 if (cfg.background) {
43447                     // render grid on panel activation (if panel background)
43448                     ret.on('activate', function(gp) {
43449                         if (!gp.grid.rendered) {
43450                     //        gp.grid.render(el);
43451                         }
43452                     });
43453                 } else {
43454                   //  cfg.grid.render(el);
43455                 }
43456                 */
43457                 break;
43458            
43459            
43460             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43461                 // it was the old xcomponent building that caused this before.
43462                 // espeically if border is the top element in the tree.
43463                 ret = this;
43464                 break; 
43465                 
43466                     
43467                 
43468                 
43469                 
43470             default:
43471                 /*
43472                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43473                     
43474                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43475                     this.add(region, ret);
43476                 } else {
43477                 */
43478                     Roo.log(cfg);
43479                     throw "Can not add '" + cfg.xtype + "' to Border";
43480                     return null;
43481              
43482                                 
43483              
43484         }
43485         this.beginUpdate();
43486         // add children..
43487         var region = '';
43488         var abn = {};
43489         Roo.each(xitems, function(i)  {
43490             region = nb && i.region ? i.region : false;
43491             
43492             var add = ret.addxtype(i);
43493            
43494             if (region) {
43495                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43496                 if (!i.background) {
43497                     abn[region] = nb[region] ;
43498                 }
43499             }
43500             
43501         });
43502         this.endUpdate();
43503
43504         // make the last non-background panel active..
43505         //if (nb) { Roo.log(abn); }
43506         if (nb) {
43507             
43508             for(var r in abn) {
43509                 region = this.getRegion(r);
43510                 if (region) {
43511                     // tried using nb[r], but it does not work..
43512                      
43513                     region.showPanel(abn[r]);
43514                    
43515                 }
43516             }
43517         }
43518         return ret;
43519         
43520     },
43521     
43522     
43523 // private
43524     factory : function(cfg)
43525     {
43526         
43527         var validRegions = Roo.bootstrap.layout.Border.regions;
43528
43529         var target = cfg.region;
43530         cfg.mgr = this;
43531         
43532         var r = Roo.bootstrap.layout;
43533         Roo.log(target);
43534         switch(target){
43535             case "north":
43536                 return new r.North(cfg);
43537             case "south":
43538                 return new r.South(cfg);
43539             case "east":
43540                 return new r.East(cfg);
43541             case "west":
43542                 return new r.West(cfg);
43543             case "center":
43544                 return new r.Center(cfg);
43545         }
43546         throw 'Layout region "'+target+'" not supported.';
43547     }
43548     
43549     
43550 });
43551  /*
43552  * Based on:
43553  * Ext JS Library 1.1.1
43554  * Copyright(c) 2006-2007, Ext JS, LLC.
43555  *
43556  * Originally Released Under LGPL - original licence link has changed is not relivant.
43557  *
43558  * Fork - LGPL
43559  * <script type="text/javascript">
43560  */
43561  
43562 /**
43563  * @class Roo.bootstrap.layout.Basic
43564  * @extends Roo.util.Observable
43565  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43566  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43567  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43568  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43569  * @cfg {string}   region  the region that it inhabits..
43570  * @cfg {bool}   skipConfig skip config?
43571  * 
43572
43573  */
43574 Roo.bootstrap.layout.Basic = function(config){
43575     
43576     this.mgr = config.mgr;
43577     
43578     this.position = config.region;
43579     
43580     var skipConfig = config.skipConfig;
43581     
43582     this.events = {
43583         /**
43584          * @scope Roo.BasicLayoutRegion
43585          */
43586         
43587         /**
43588          * @event beforeremove
43589          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43590          * @param {Roo.LayoutRegion} this
43591          * @param {Roo.ContentPanel} panel The panel
43592          * @param {Object} e The cancel event object
43593          */
43594         "beforeremove" : true,
43595         /**
43596          * @event invalidated
43597          * Fires when the layout for this region is changed.
43598          * @param {Roo.LayoutRegion} this
43599          */
43600         "invalidated" : true,
43601         /**
43602          * @event visibilitychange
43603          * Fires when this region is shown or hidden 
43604          * @param {Roo.LayoutRegion} this
43605          * @param {Boolean} visibility true or false
43606          */
43607         "visibilitychange" : true,
43608         /**
43609          * @event paneladded
43610          * Fires when a panel is added. 
43611          * @param {Roo.LayoutRegion} this
43612          * @param {Roo.ContentPanel} panel The panel
43613          */
43614         "paneladded" : true,
43615         /**
43616          * @event panelremoved
43617          * Fires when a panel is removed. 
43618          * @param {Roo.LayoutRegion} this
43619          * @param {Roo.ContentPanel} panel The panel
43620          */
43621         "panelremoved" : true,
43622         /**
43623          * @event beforecollapse
43624          * Fires when this region before collapse.
43625          * @param {Roo.LayoutRegion} this
43626          */
43627         "beforecollapse" : true,
43628         /**
43629          * @event collapsed
43630          * Fires when this region is collapsed.
43631          * @param {Roo.LayoutRegion} this
43632          */
43633         "collapsed" : true,
43634         /**
43635          * @event expanded
43636          * Fires when this region is expanded.
43637          * @param {Roo.LayoutRegion} this
43638          */
43639         "expanded" : true,
43640         /**
43641          * @event slideshow
43642          * Fires when this region is slid into view.
43643          * @param {Roo.LayoutRegion} this
43644          */
43645         "slideshow" : true,
43646         /**
43647          * @event slidehide
43648          * Fires when this region slides out of view. 
43649          * @param {Roo.LayoutRegion} this
43650          */
43651         "slidehide" : true,
43652         /**
43653          * @event panelactivated
43654          * Fires when a panel is activated. 
43655          * @param {Roo.LayoutRegion} this
43656          * @param {Roo.ContentPanel} panel The activated panel
43657          */
43658         "panelactivated" : true,
43659         /**
43660          * @event resized
43661          * Fires when the user resizes this region. 
43662          * @param {Roo.LayoutRegion} this
43663          * @param {Number} newSize The new size (width for east/west, height for north/south)
43664          */
43665         "resized" : true
43666     };
43667     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43668     this.panels = new Roo.util.MixedCollection();
43669     this.panels.getKey = this.getPanelId.createDelegate(this);
43670     this.box = null;
43671     this.activePanel = null;
43672     // ensure listeners are added...
43673     
43674     if (config.listeners || config.events) {
43675         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43676             listeners : config.listeners || {},
43677             events : config.events || {}
43678         });
43679     }
43680     
43681     if(skipConfig !== true){
43682         this.applyConfig(config);
43683     }
43684 };
43685
43686 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43687 {
43688     getPanelId : function(p){
43689         return p.getId();
43690     },
43691     
43692     applyConfig : function(config){
43693         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43694         this.config = config;
43695         
43696     },
43697     
43698     /**
43699      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43700      * the width, for horizontal (north, south) the height.
43701      * @param {Number} newSize The new width or height
43702      */
43703     resizeTo : function(newSize){
43704         var el = this.el ? this.el :
43705                  (this.activePanel ? this.activePanel.getEl() : null);
43706         if(el){
43707             switch(this.position){
43708                 case "east":
43709                 case "west":
43710                     el.setWidth(newSize);
43711                     this.fireEvent("resized", this, newSize);
43712                 break;
43713                 case "north":
43714                 case "south":
43715                     el.setHeight(newSize);
43716                     this.fireEvent("resized", this, newSize);
43717                 break;                
43718             }
43719         }
43720     },
43721     
43722     getBox : function(){
43723         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43724     },
43725     
43726     getMargins : function(){
43727         return this.margins;
43728     },
43729     
43730     updateBox : function(box){
43731         this.box = box;
43732         var el = this.activePanel.getEl();
43733         el.dom.style.left = box.x + "px";
43734         el.dom.style.top = box.y + "px";
43735         this.activePanel.setSize(box.width, box.height);
43736     },
43737     
43738     /**
43739      * Returns the container element for this region.
43740      * @return {Roo.Element}
43741      */
43742     getEl : function(){
43743         return this.activePanel;
43744     },
43745     
43746     /**
43747      * Returns true if this region is currently visible.
43748      * @return {Boolean}
43749      */
43750     isVisible : function(){
43751         return this.activePanel ? true : false;
43752     },
43753     
43754     setActivePanel : function(panel){
43755         panel = this.getPanel(panel);
43756         if(this.activePanel && this.activePanel != panel){
43757             this.activePanel.setActiveState(false);
43758             this.activePanel.getEl().setLeftTop(-10000,-10000);
43759         }
43760         this.activePanel = panel;
43761         panel.setActiveState(true);
43762         if(this.box){
43763             panel.setSize(this.box.width, this.box.height);
43764         }
43765         this.fireEvent("panelactivated", this, panel);
43766         this.fireEvent("invalidated");
43767     },
43768     
43769     /**
43770      * Show the specified panel.
43771      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43772      * @return {Roo.ContentPanel} The shown panel or null
43773      */
43774     showPanel : function(panel){
43775         panel = this.getPanel(panel);
43776         if(panel){
43777             this.setActivePanel(panel);
43778         }
43779         return panel;
43780     },
43781     
43782     /**
43783      * Get the active panel for this region.
43784      * @return {Roo.ContentPanel} The active panel or null
43785      */
43786     getActivePanel : function(){
43787         return this.activePanel;
43788     },
43789     
43790     /**
43791      * Add the passed ContentPanel(s)
43792      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43793      * @return {Roo.ContentPanel} The panel added (if only one was added)
43794      */
43795     add : function(panel){
43796         if(arguments.length > 1){
43797             for(var i = 0, len = arguments.length; i < len; i++) {
43798                 this.add(arguments[i]);
43799             }
43800             return null;
43801         }
43802         if(this.hasPanel(panel)){
43803             this.showPanel(panel);
43804             return panel;
43805         }
43806         var el = panel.getEl();
43807         if(el.dom.parentNode != this.mgr.el.dom){
43808             this.mgr.el.dom.appendChild(el.dom);
43809         }
43810         if(panel.setRegion){
43811             panel.setRegion(this);
43812         }
43813         this.panels.add(panel);
43814         el.setStyle("position", "absolute");
43815         if(!panel.background){
43816             this.setActivePanel(panel);
43817             if(this.config.initialSize && this.panels.getCount()==1){
43818                 this.resizeTo(this.config.initialSize);
43819             }
43820         }
43821         this.fireEvent("paneladded", this, panel);
43822         return panel;
43823     },
43824     
43825     /**
43826      * Returns true if the panel is in this region.
43827      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43828      * @return {Boolean}
43829      */
43830     hasPanel : function(panel){
43831         if(typeof panel == "object"){ // must be panel obj
43832             panel = panel.getId();
43833         }
43834         return this.getPanel(panel) ? true : false;
43835     },
43836     
43837     /**
43838      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43839      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43840      * @param {Boolean} preservePanel Overrides the config preservePanel option
43841      * @return {Roo.ContentPanel} The panel that was removed
43842      */
43843     remove : function(panel, preservePanel){
43844         panel = this.getPanel(panel);
43845         if(!panel){
43846             return null;
43847         }
43848         var e = {};
43849         this.fireEvent("beforeremove", this, panel, e);
43850         if(e.cancel === true){
43851             return null;
43852         }
43853         var panelId = panel.getId();
43854         this.panels.removeKey(panelId);
43855         return panel;
43856     },
43857     
43858     /**
43859      * Returns the panel specified or null if it's not in this region.
43860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43861      * @return {Roo.ContentPanel}
43862      */
43863     getPanel : function(id){
43864         if(typeof id == "object"){ // must be panel obj
43865             return id;
43866         }
43867         return this.panels.get(id);
43868     },
43869     
43870     /**
43871      * Returns this regions position (north/south/east/west/center).
43872      * @return {String} 
43873      */
43874     getPosition: function(){
43875         return this.position;    
43876     }
43877 });/*
43878  * Based on:
43879  * Ext JS Library 1.1.1
43880  * Copyright(c) 2006-2007, Ext JS, LLC.
43881  *
43882  * Originally Released Under LGPL - original licence link has changed is not relivant.
43883  *
43884  * Fork - LGPL
43885  * <script type="text/javascript">
43886  */
43887  
43888 /**
43889  * @class Roo.bootstrap.layout.Region
43890  * @extends Roo.bootstrap.layout.Basic
43891  * This class represents a region in a layout manager.
43892  
43893  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43894  * @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})
43895  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43896  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43897  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43898  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43899  * @cfg {String}    title           The title for the region (overrides panel titles)
43900  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43901  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43902  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43903  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43904  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43905  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43906  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43907  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43908  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43909  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43910
43911  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43912  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43913  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43914  * @cfg {Number}    width           For East/West panels
43915  * @cfg {Number}    height          For North/South panels
43916  * @cfg {Boolean}   split           To show the splitter
43917  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43918  * 
43919  * @cfg {string}   cls             Extra CSS classes to add to region
43920  * 
43921  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43922  * @cfg {string}   region  the region that it inhabits..
43923  *
43924
43925  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43926  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43927
43928  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43929  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43930  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43931  */
43932 Roo.bootstrap.layout.Region = function(config)
43933 {
43934     this.applyConfig(config);
43935
43936     var mgr = config.mgr;
43937     var pos = config.region;
43938     config.skipConfig = true;
43939     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43940     
43941     if (mgr.el) {
43942         this.onRender(mgr.el);   
43943     }
43944      
43945     this.visible = true;
43946     this.collapsed = false;
43947     this.unrendered_panels = [];
43948 };
43949
43950 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43951
43952     position: '', // set by wrapper (eg. north/south etc..)
43953     unrendered_panels : null,  // unrendered panels.
43954     
43955     tabPosition : false,
43956     
43957     mgr: false, // points to 'Border'
43958     
43959     
43960     createBody : function(){
43961         /** This region's body element 
43962         * @type Roo.Element */
43963         this.bodyEl = this.el.createChild({
43964                 tag: "div",
43965                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43966         });
43967     },
43968
43969     onRender: function(ctr, pos)
43970     {
43971         var dh = Roo.DomHelper;
43972         /** This region's container element 
43973         * @type Roo.Element */
43974         this.el = dh.append(ctr.dom, {
43975                 tag: "div",
43976                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43977             }, true);
43978         /** This region's title element 
43979         * @type Roo.Element */
43980     
43981         this.titleEl = dh.append(this.el.dom,  {
43982                 tag: "div",
43983                 unselectable: "on",
43984                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43985                 children:[
43986                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43987                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43988                 ]
43989             }, true);
43990         
43991         this.titleEl.enableDisplayMode();
43992         /** This region's title text element 
43993         * @type HTMLElement */
43994         this.titleTextEl = this.titleEl.dom.firstChild;
43995         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43996         /*
43997         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43998         this.closeBtn.enableDisplayMode();
43999         this.closeBtn.on("click", this.closeClicked, this);
44000         this.closeBtn.hide();
44001     */
44002         this.createBody(this.config);
44003         if(this.config.hideWhenEmpty){
44004             this.hide();
44005             this.on("paneladded", this.validateVisibility, this);
44006             this.on("panelremoved", this.validateVisibility, this);
44007         }
44008         if(this.autoScroll){
44009             this.bodyEl.setStyle("overflow", "auto");
44010         }else{
44011             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44012         }
44013         //if(c.titlebar !== false){
44014             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44015                 this.titleEl.hide();
44016             }else{
44017                 this.titleEl.show();
44018                 if(this.config.title){
44019                     this.titleTextEl.innerHTML = this.config.title;
44020                 }
44021             }
44022         //}
44023         if(this.config.collapsed){
44024             this.collapse(true);
44025         }
44026         if(this.config.hidden){
44027             this.hide();
44028         }
44029         
44030         if (this.unrendered_panels && this.unrendered_panels.length) {
44031             for (var i =0;i< this.unrendered_panels.length; i++) {
44032                 this.add(this.unrendered_panels[i]);
44033             }
44034             this.unrendered_panels = null;
44035             
44036         }
44037         
44038     },
44039     
44040     applyConfig : function(c)
44041     {
44042         /*
44043          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44044             var dh = Roo.DomHelper;
44045             if(c.titlebar !== false){
44046                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44047                 this.collapseBtn.on("click", this.collapse, this);
44048                 this.collapseBtn.enableDisplayMode();
44049                 /*
44050                 if(c.showPin === true || this.showPin){
44051                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44052                     this.stickBtn.enableDisplayMode();
44053                     this.stickBtn.on("click", this.expand, this);
44054                     this.stickBtn.hide();
44055                 }
44056                 
44057             }
44058             */
44059             /** This region's collapsed element
44060             * @type Roo.Element */
44061             /*
44062              *
44063             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44064                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44065             ]}, true);
44066             
44067             if(c.floatable !== false){
44068                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44069                this.collapsedEl.on("click", this.collapseClick, this);
44070             }
44071
44072             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44073                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44074                    id: "message", unselectable: "on", style:{"float":"left"}});
44075                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44076              }
44077             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44078             this.expandBtn.on("click", this.expand, this);
44079             
44080         }
44081         
44082         if(this.collapseBtn){
44083             this.collapseBtn.setVisible(c.collapsible == true);
44084         }
44085         
44086         this.cmargins = c.cmargins || this.cmargins ||
44087                          (this.position == "west" || this.position == "east" ?
44088                              {top: 0, left: 2, right:2, bottom: 0} :
44089                              {top: 2, left: 0, right:0, bottom: 2});
44090         */
44091         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44092         
44093         
44094         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44095         
44096         this.autoScroll = c.autoScroll || false;
44097         
44098         
44099        
44100         
44101         this.duration = c.duration || .30;
44102         this.slideDuration = c.slideDuration || .45;
44103         this.config = c;
44104        
44105     },
44106     /**
44107      * Returns true if this region is currently visible.
44108      * @return {Boolean}
44109      */
44110     isVisible : function(){
44111         return this.visible;
44112     },
44113
44114     /**
44115      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44116      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44117      */
44118     //setCollapsedTitle : function(title){
44119     //    title = title || "&#160;";
44120      //   if(this.collapsedTitleTextEl){
44121       //      this.collapsedTitleTextEl.innerHTML = title;
44122        // }
44123     //},
44124
44125     getBox : function(){
44126         var b;
44127       //  if(!this.collapsed){
44128             b = this.el.getBox(false, true);
44129        // }else{
44130           //  b = this.collapsedEl.getBox(false, true);
44131         //}
44132         return b;
44133     },
44134
44135     getMargins : function(){
44136         return this.margins;
44137         //return this.collapsed ? this.cmargins : this.margins;
44138     },
44139 /*
44140     highlight : function(){
44141         this.el.addClass("x-layout-panel-dragover");
44142     },
44143
44144     unhighlight : function(){
44145         this.el.removeClass("x-layout-panel-dragover");
44146     },
44147 */
44148     updateBox : function(box)
44149     {
44150         if (!this.bodyEl) {
44151             return; // not rendered yet..
44152         }
44153         
44154         this.box = box;
44155         if(!this.collapsed){
44156             this.el.dom.style.left = box.x + "px";
44157             this.el.dom.style.top = box.y + "px";
44158             this.updateBody(box.width, box.height);
44159         }else{
44160             this.collapsedEl.dom.style.left = box.x + "px";
44161             this.collapsedEl.dom.style.top = box.y + "px";
44162             this.collapsedEl.setSize(box.width, box.height);
44163         }
44164         if(this.tabs){
44165             this.tabs.autoSizeTabs();
44166         }
44167     },
44168
44169     updateBody : function(w, h)
44170     {
44171         if(w !== null){
44172             this.el.setWidth(w);
44173             w -= this.el.getBorderWidth("rl");
44174             if(this.config.adjustments){
44175                 w += this.config.adjustments[0];
44176             }
44177         }
44178         if(h !== null && h > 0){
44179             this.el.setHeight(h);
44180             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44181             h -= this.el.getBorderWidth("tb");
44182             if(this.config.adjustments){
44183                 h += this.config.adjustments[1];
44184             }
44185             this.bodyEl.setHeight(h);
44186             if(this.tabs){
44187                 h = this.tabs.syncHeight(h);
44188             }
44189         }
44190         if(this.panelSize){
44191             w = w !== null ? w : this.panelSize.width;
44192             h = h !== null ? h : this.panelSize.height;
44193         }
44194         if(this.activePanel){
44195             var el = this.activePanel.getEl();
44196             w = w !== null ? w : el.getWidth();
44197             h = h !== null ? h : el.getHeight();
44198             this.panelSize = {width: w, height: h};
44199             this.activePanel.setSize(w, h);
44200         }
44201         if(Roo.isIE && this.tabs){
44202             this.tabs.el.repaint();
44203         }
44204     },
44205
44206     /**
44207      * Returns the container element for this region.
44208      * @return {Roo.Element}
44209      */
44210     getEl : function(){
44211         return this.el;
44212     },
44213
44214     /**
44215      * Hides this region.
44216      */
44217     hide : function(){
44218         //if(!this.collapsed){
44219             this.el.dom.style.left = "-2000px";
44220             this.el.hide();
44221         //}else{
44222          //   this.collapsedEl.dom.style.left = "-2000px";
44223          //   this.collapsedEl.hide();
44224        // }
44225         this.visible = false;
44226         this.fireEvent("visibilitychange", this, false);
44227     },
44228
44229     /**
44230      * Shows this region if it was previously hidden.
44231      */
44232     show : function(){
44233         //if(!this.collapsed){
44234             this.el.show();
44235         //}else{
44236         //    this.collapsedEl.show();
44237        // }
44238         this.visible = true;
44239         this.fireEvent("visibilitychange", this, true);
44240     },
44241 /*
44242     closeClicked : function(){
44243         if(this.activePanel){
44244             this.remove(this.activePanel);
44245         }
44246     },
44247
44248     collapseClick : function(e){
44249         if(this.isSlid){
44250            e.stopPropagation();
44251            this.slideIn();
44252         }else{
44253            e.stopPropagation();
44254            this.slideOut();
44255         }
44256     },
44257 */
44258     /**
44259      * Collapses this region.
44260      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44261      */
44262     /*
44263     collapse : function(skipAnim, skipCheck = false){
44264         if(this.collapsed) {
44265             return;
44266         }
44267         
44268         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44269             
44270             this.collapsed = true;
44271             if(this.split){
44272                 this.split.el.hide();
44273             }
44274             if(this.config.animate && skipAnim !== true){
44275                 this.fireEvent("invalidated", this);
44276                 this.animateCollapse();
44277             }else{
44278                 this.el.setLocation(-20000,-20000);
44279                 this.el.hide();
44280                 this.collapsedEl.show();
44281                 this.fireEvent("collapsed", this);
44282                 this.fireEvent("invalidated", this);
44283             }
44284         }
44285         
44286     },
44287 */
44288     animateCollapse : function(){
44289         // overridden
44290     },
44291
44292     /**
44293      * Expands this region if it was previously collapsed.
44294      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44295      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44296      */
44297     /*
44298     expand : function(e, skipAnim){
44299         if(e) {
44300             e.stopPropagation();
44301         }
44302         if(!this.collapsed || this.el.hasActiveFx()) {
44303             return;
44304         }
44305         if(this.isSlid){
44306             this.afterSlideIn();
44307             skipAnim = true;
44308         }
44309         this.collapsed = false;
44310         if(this.config.animate && skipAnim !== true){
44311             this.animateExpand();
44312         }else{
44313             this.el.show();
44314             if(this.split){
44315                 this.split.el.show();
44316             }
44317             this.collapsedEl.setLocation(-2000,-2000);
44318             this.collapsedEl.hide();
44319             this.fireEvent("invalidated", this);
44320             this.fireEvent("expanded", this);
44321         }
44322     },
44323 */
44324     animateExpand : function(){
44325         // overridden
44326     },
44327
44328     initTabs : function()
44329     {
44330         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44331         
44332         var ts = new Roo.bootstrap.panel.Tabs({
44333             el: this.bodyEl.dom,
44334             region : this,
44335             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44336             disableTooltips: this.config.disableTabTips,
44337             toolbar : this.config.toolbar
44338         });
44339         
44340         if(this.config.hideTabs){
44341             ts.stripWrap.setDisplayed(false);
44342         }
44343         this.tabs = ts;
44344         ts.resizeTabs = this.config.resizeTabs === true;
44345         ts.minTabWidth = this.config.minTabWidth || 40;
44346         ts.maxTabWidth = this.config.maxTabWidth || 250;
44347         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44348         ts.monitorResize = false;
44349         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44350         ts.bodyEl.addClass('roo-layout-tabs-body');
44351         this.panels.each(this.initPanelAsTab, this);
44352     },
44353
44354     initPanelAsTab : function(panel){
44355         var ti = this.tabs.addTab(
44356             panel.getEl().id,
44357             panel.getTitle(),
44358             null,
44359             this.config.closeOnTab && panel.isClosable(),
44360             panel.tpl
44361         );
44362         if(panel.tabTip !== undefined){
44363             ti.setTooltip(panel.tabTip);
44364         }
44365         ti.on("activate", function(){
44366               this.setActivePanel(panel);
44367         }, this);
44368         
44369         if(this.config.closeOnTab){
44370             ti.on("beforeclose", function(t, e){
44371                 e.cancel = true;
44372                 this.remove(panel);
44373             }, this);
44374         }
44375         
44376         panel.tabItem = ti;
44377         
44378         return ti;
44379     },
44380
44381     updatePanelTitle : function(panel, title)
44382     {
44383         if(this.activePanel == panel){
44384             this.updateTitle(title);
44385         }
44386         if(this.tabs){
44387             var ti = this.tabs.getTab(panel.getEl().id);
44388             ti.setText(title);
44389             if(panel.tabTip !== undefined){
44390                 ti.setTooltip(panel.tabTip);
44391             }
44392         }
44393     },
44394
44395     updateTitle : function(title){
44396         if(this.titleTextEl && !this.config.title){
44397             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44398         }
44399     },
44400
44401     setActivePanel : function(panel)
44402     {
44403         panel = this.getPanel(panel);
44404         if(this.activePanel && this.activePanel != panel){
44405             if(this.activePanel.setActiveState(false) === false){
44406                 return;
44407             }
44408         }
44409         this.activePanel = panel;
44410         panel.setActiveState(true);
44411         if(this.panelSize){
44412             panel.setSize(this.panelSize.width, this.panelSize.height);
44413         }
44414         if(this.closeBtn){
44415             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44416         }
44417         this.updateTitle(panel.getTitle());
44418         if(this.tabs){
44419             this.fireEvent("invalidated", this);
44420         }
44421         this.fireEvent("panelactivated", this, panel);
44422     },
44423
44424     /**
44425      * Shows the specified panel.
44426      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44427      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44428      */
44429     showPanel : function(panel)
44430     {
44431         panel = this.getPanel(panel);
44432         if(panel){
44433             if(this.tabs){
44434                 var tab = this.tabs.getTab(panel.getEl().id);
44435                 if(tab.isHidden()){
44436                     this.tabs.unhideTab(tab.id);
44437                 }
44438                 tab.activate();
44439             }else{
44440                 this.setActivePanel(panel);
44441             }
44442         }
44443         return panel;
44444     },
44445
44446     /**
44447      * Get the active panel for this region.
44448      * @return {Roo.ContentPanel} The active panel or null
44449      */
44450     getActivePanel : function(){
44451         return this.activePanel;
44452     },
44453
44454     validateVisibility : function(){
44455         if(this.panels.getCount() < 1){
44456             this.updateTitle("&#160;");
44457             this.closeBtn.hide();
44458             this.hide();
44459         }else{
44460             if(!this.isVisible()){
44461                 this.show();
44462             }
44463         }
44464     },
44465
44466     /**
44467      * Adds the passed ContentPanel(s) to this region.
44468      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44469      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44470      */
44471     add : function(panel)
44472     {
44473         if(arguments.length > 1){
44474             for(var i = 0, len = arguments.length; i < len; i++) {
44475                 this.add(arguments[i]);
44476             }
44477             return null;
44478         }
44479         
44480         // if we have not been rendered yet, then we can not really do much of this..
44481         if (!this.bodyEl) {
44482             this.unrendered_panels.push(panel);
44483             return panel;
44484         }
44485         
44486         
44487         
44488         
44489         if(this.hasPanel(panel)){
44490             this.showPanel(panel);
44491             return panel;
44492         }
44493         panel.setRegion(this);
44494         this.panels.add(panel);
44495        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44496             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44497             // and hide them... ???
44498             this.bodyEl.dom.appendChild(panel.getEl().dom);
44499             if(panel.background !== true){
44500                 this.setActivePanel(panel);
44501             }
44502             this.fireEvent("paneladded", this, panel);
44503             return panel;
44504         }
44505         */
44506         if(!this.tabs){
44507             this.initTabs();
44508         }else{
44509             this.initPanelAsTab(panel);
44510         }
44511         
44512         
44513         if(panel.background !== true){
44514             this.tabs.activate(panel.getEl().id);
44515         }
44516         this.fireEvent("paneladded", this, panel);
44517         return panel;
44518     },
44519
44520     /**
44521      * Hides the tab for the specified panel.
44522      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44523      */
44524     hidePanel : function(panel){
44525         if(this.tabs && (panel = this.getPanel(panel))){
44526             this.tabs.hideTab(panel.getEl().id);
44527         }
44528     },
44529
44530     /**
44531      * Unhides the tab for a previously hidden panel.
44532      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44533      */
44534     unhidePanel : function(panel){
44535         if(this.tabs && (panel = this.getPanel(panel))){
44536             this.tabs.unhideTab(panel.getEl().id);
44537         }
44538     },
44539
44540     clearPanels : function(){
44541         while(this.panels.getCount() > 0){
44542              this.remove(this.panels.first());
44543         }
44544     },
44545
44546     /**
44547      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44548      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44549      * @param {Boolean} preservePanel Overrides the config preservePanel option
44550      * @return {Roo.ContentPanel} The panel that was removed
44551      */
44552     remove : function(panel, preservePanel)
44553     {
44554         panel = this.getPanel(panel);
44555         if(!panel){
44556             return null;
44557         }
44558         var e = {};
44559         this.fireEvent("beforeremove", this, panel, e);
44560         if(e.cancel === true){
44561             return null;
44562         }
44563         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44564         var panelId = panel.getId();
44565         this.panels.removeKey(panelId);
44566         if(preservePanel){
44567             document.body.appendChild(panel.getEl().dom);
44568         }
44569         if(this.tabs){
44570             this.tabs.removeTab(panel.getEl().id);
44571         }else if (!preservePanel){
44572             this.bodyEl.dom.removeChild(panel.getEl().dom);
44573         }
44574         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44575             var p = this.panels.first();
44576             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44577             tempEl.appendChild(p.getEl().dom);
44578             this.bodyEl.update("");
44579             this.bodyEl.dom.appendChild(p.getEl().dom);
44580             tempEl = null;
44581             this.updateTitle(p.getTitle());
44582             this.tabs = null;
44583             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44584             this.setActivePanel(p);
44585         }
44586         panel.setRegion(null);
44587         if(this.activePanel == panel){
44588             this.activePanel = null;
44589         }
44590         if(this.config.autoDestroy !== false && preservePanel !== true){
44591             try{panel.destroy();}catch(e){}
44592         }
44593         this.fireEvent("panelremoved", this, panel);
44594         return panel;
44595     },
44596
44597     /**
44598      * Returns the TabPanel component used by this region
44599      * @return {Roo.TabPanel}
44600      */
44601     getTabs : function(){
44602         return this.tabs;
44603     },
44604
44605     createTool : function(parentEl, className){
44606         var btn = Roo.DomHelper.append(parentEl, {
44607             tag: "div",
44608             cls: "x-layout-tools-button",
44609             children: [ {
44610                 tag: "div",
44611                 cls: "roo-layout-tools-button-inner " + className,
44612                 html: "&#160;"
44613             }]
44614         }, true);
44615         btn.addClassOnOver("roo-layout-tools-button-over");
44616         return btn;
44617     }
44618 });/*
44619  * Based on:
44620  * Ext JS Library 1.1.1
44621  * Copyright(c) 2006-2007, Ext JS, LLC.
44622  *
44623  * Originally Released Under LGPL - original licence link has changed is not relivant.
44624  *
44625  * Fork - LGPL
44626  * <script type="text/javascript">
44627  */
44628  
44629
44630
44631 /**
44632  * @class Roo.SplitLayoutRegion
44633  * @extends Roo.LayoutRegion
44634  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44635  */
44636 Roo.bootstrap.layout.Split = function(config){
44637     this.cursor = config.cursor;
44638     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44639 };
44640
44641 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44642 {
44643     splitTip : "Drag to resize.",
44644     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44645     useSplitTips : false,
44646
44647     applyConfig : function(config){
44648         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44649     },
44650     
44651     onRender : function(ctr,pos) {
44652         
44653         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44654         if(!this.config.split){
44655             return;
44656         }
44657         if(!this.split){
44658             
44659             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44660                             tag: "div",
44661                             id: this.el.id + "-split",
44662                             cls: "roo-layout-split roo-layout-split-"+this.position,
44663                             html: "&#160;"
44664             });
44665             /** The SplitBar for this region 
44666             * @type Roo.SplitBar */
44667             // does not exist yet...
44668             Roo.log([this.position, this.orientation]);
44669             
44670             this.split = new Roo.bootstrap.SplitBar({
44671                 dragElement : splitEl,
44672                 resizingElement: this.el,
44673                 orientation : this.orientation
44674             });
44675             
44676             this.split.on("moved", this.onSplitMove, this);
44677             this.split.useShim = this.config.useShim === true;
44678             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44679             if(this.useSplitTips){
44680                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44681             }
44682             //if(config.collapsible){
44683             //    this.split.el.on("dblclick", this.collapse,  this);
44684             //}
44685         }
44686         if(typeof this.config.minSize != "undefined"){
44687             this.split.minSize = this.config.minSize;
44688         }
44689         if(typeof this.config.maxSize != "undefined"){
44690             this.split.maxSize = this.config.maxSize;
44691         }
44692         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44693             this.hideSplitter();
44694         }
44695         
44696     },
44697
44698     getHMaxSize : function(){
44699          var cmax = this.config.maxSize || 10000;
44700          var center = this.mgr.getRegion("center");
44701          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44702     },
44703
44704     getVMaxSize : function(){
44705          var cmax = this.config.maxSize || 10000;
44706          var center = this.mgr.getRegion("center");
44707          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44708     },
44709
44710     onSplitMove : function(split, newSize){
44711         this.fireEvent("resized", this, newSize);
44712     },
44713     
44714     /** 
44715      * Returns the {@link Roo.SplitBar} for this region.
44716      * @return {Roo.SplitBar}
44717      */
44718     getSplitBar : function(){
44719         return this.split;
44720     },
44721     
44722     hide : function(){
44723         this.hideSplitter();
44724         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44725     },
44726
44727     hideSplitter : function(){
44728         if(this.split){
44729             this.split.el.setLocation(-2000,-2000);
44730             this.split.el.hide();
44731         }
44732     },
44733
44734     show : function(){
44735         if(this.split){
44736             this.split.el.show();
44737         }
44738         Roo.bootstrap.layout.Split.superclass.show.call(this);
44739     },
44740     
44741     beforeSlide: function(){
44742         if(Roo.isGecko){// firefox overflow auto bug workaround
44743             this.bodyEl.clip();
44744             if(this.tabs) {
44745                 this.tabs.bodyEl.clip();
44746             }
44747             if(this.activePanel){
44748                 this.activePanel.getEl().clip();
44749                 
44750                 if(this.activePanel.beforeSlide){
44751                     this.activePanel.beforeSlide();
44752                 }
44753             }
44754         }
44755     },
44756     
44757     afterSlide : function(){
44758         if(Roo.isGecko){// firefox overflow auto bug workaround
44759             this.bodyEl.unclip();
44760             if(this.tabs) {
44761                 this.tabs.bodyEl.unclip();
44762             }
44763             if(this.activePanel){
44764                 this.activePanel.getEl().unclip();
44765                 if(this.activePanel.afterSlide){
44766                     this.activePanel.afterSlide();
44767                 }
44768             }
44769         }
44770     },
44771
44772     initAutoHide : function(){
44773         if(this.autoHide !== false){
44774             if(!this.autoHideHd){
44775                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44776                 this.autoHideHd = {
44777                     "mouseout": function(e){
44778                         if(!e.within(this.el, true)){
44779                             st.delay(500);
44780                         }
44781                     },
44782                     "mouseover" : function(e){
44783                         st.cancel();
44784                     },
44785                     scope : this
44786                 };
44787             }
44788             this.el.on(this.autoHideHd);
44789         }
44790     },
44791
44792     clearAutoHide : function(){
44793         if(this.autoHide !== false){
44794             this.el.un("mouseout", this.autoHideHd.mouseout);
44795             this.el.un("mouseover", this.autoHideHd.mouseover);
44796         }
44797     },
44798
44799     clearMonitor : function(){
44800         Roo.get(document).un("click", this.slideInIf, this);
44801     },
44802
44803     // these names are backwards but not changed for compat
44804     slideOut : function(){
44805         if(this.isSlid || this.el.hasActiveFx()){
44806             return;
44807         }
44808         this.isSlid = true;
44809         if(this.collapseBtn){
44810             this.collapseBtn.hide();
44811         }
44812         this.closeBtnState = this.closeBtn.getStyle('display');
44813         this.closeBtn.hide();
44814         if(this.stickBtn){
44815             this.stickBtn.show();
44816         }
44817         this.el.show();
44818         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44819         this.beforeSlide();
44820         this.el.setStyle("z-index", 10001);
44821         this.el.slideIn(this.getSlideAnchor(), {
44822             callback: function(){
44823                 this.afterSlide();
44824                 this.initAutoHide();
44825                 Roo.get(document).on("click", this.slideInIf, this);
44826                 this.fireEvent("slideshow", this);
44827             },
44828             scope: this,
44829             block: true
44830         });
44831     },
44832
44833     afterSlideIn : function(){
44834         this.clearAutoHide();
44835         this.isSlid = false;
44836         this.clearMonitor();
44837         this.el.setStyle("z-index", "");
44838         if(this.collapseBtn){
44839             this.collapseBtn.show();
44840         }
44841         this.closeBtn.setStyle('display', this.closeBtnState);
44842         if(this.stickBtn){
44843             this.stickBtn.hide();
44844         }
44845         this.fireEvent("slidehide", this);
44846     },
44847
44848     slideIn : function(cb){
44849         if(!this.isSlid || this.el.hasActiveFx()){
44850             Roo.callback(cb);
44851             return;
44852         }
44853         this.isSlid = false;
44854         this.beforeSlide();
44855         this.el.slideOut(this.getSlideAnchor(), {
44856             callback: function(){
44857                 this.el.setLeftTop(-10000, -10000);
44858                 this.afterSlide();
44859                 this.afterSlideIn();
44860                 Roo.callback(cb);
44861             },
44862             scope: this,
44863             block: true
44864         });
44865     },
44866     
44867     slideInIf : function(e){
44868         if(!e.within(this.el)){
44869             this.slideIn();
44870         }
44871     },
44872
44873     animateCollapse : function(){
44874         this.beforeSlide();
44875         this.el.setStyle("z-index", 20000);
44876         var anchor = this.getSlideAnchor();
44877         this.el.slideOut(anchor, {
44878             callback : function(){
44879                 this.el.setStyle("z-index", "");
44880                 this.collapsedEl.slideIn(anchor, {duration:.3});
44881                 this.afterSlide();
44882                 this.el.setLocation(-10000,-10000);
44883                 this.el.hide();
44884                 this.fireEvent("collapsed", this);
44885             },
44886             scope: this,
44887             block: true
44888         });
44889     },
44890
44891     animateExpand : function(){
44892         this.beforeSlide();
44893         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44894         this.el.setStyle("z-index", 20000);
44895         this.collapsedEl.hide({
44896             duration:.1
44897         });
44898         this.el.slideIn(this.getSlideAnchor(), {
44899             callback : function(){
44900                 this.el.setStyle("z-index", "");
44901                 this.afterSlide();
44902                 if(this.split){
44903                     this.split.el.show();
44904                 }
44905                 this.fireEvent("invalidated", this);
44906                 this.fireEvent("expanded", this);
44907             },
44908             scope: this,
44909             block: true
44910         });
44911     },
44912
44913     anchors : {
44914         "west" : "left",
44915         "east" : "right",
44916         "north" : "top",
44917         "south" : "bottom"
44918     },
44919
44920     sanchors : {
44921         "west" : "l",
44922         "east" : "r",
44923         "north" : "t",
44924         "south" : "b"
44925     },
44926
44927     canchors : {
44928         "west" : "tl-tr",
44929         "east" : "tr-tl",
44930         "north" : "tl-bl",
44931         "south" : "bl-tl"
44932     },
44933
44934     getAnchor : function(){
44935         return this.anchors[this.position];
44936     },
44937
44938     getCollapseAnchor : function(){
44939         return this.canchors[this.position];
44940     },
44941
44942     getSlideAnchor : function(){
44943         return this.sanchors[this.position];
44944     },
44945
44946     getAlignAdj : function(){
44947         var cm = this.cmargins;
44948         switch(this.position){
44949             case "west":
44950                 return [0, 0];
44951             break;
44952             case "east":
44953                 return [0, 0];
44954             break;
44955             case "north":
44956                 return [0, 0];
44957             break;
44958             case "south":
44959                 return [0, 0];
44960             break;
44961         }
44962     },
44963
44964     getExpandAdj : function(){
44965         var c = this.collapsedEl, cm = this.cmargins;
44966         switch(this.position){
44967             case "west":
44968                 return [-(cm.right+c.getWidth()+cm.left), 0];
44969             break;
44970             case "east":
44971                 return [cm.right+c.getWidth()+cm.left, 0];
44972             break;
44973             case "north":
44974                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44975             break;
44976             case "south":
44977                 return [0, cm.top+cm.bottom+c.getHeight()];
44978             break;
44979         }
44980     }
44981 });/*
44982  * Based on:
44983  * Ext JS Library 1.1.1
44984  * Copyright(c) 2006-2007, Ext JS, LLC.
44985  *
44986  * Originally Released Under LGPL - original licence link has changed is not relivant.
44987  *
44988  * Fork - LGPL
44989  * <script type="text/javascript">
44990  */
44991 /*
44992  * These classes are private internal classes
44993  */
44994 Roo.bootstrap.layout.Center = function(config){
44995     config.region = "center";
44996     Roo.bootstrap.layout.Region.call(this, config);
44997     this.visible = true;
44998     this.minWidth = config.minWidth || 20;
44999     this.minHeight = config.minHeight || 20;
45000 };
45001
45002 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45003     hide : function(){
45004         // center panel can't be hidden
45005     },
45006     
45007     show : function(){
45008         // center panel can't be hidden
45009     },
45010     
45011     getMinWidth: function(){
45012         return this.minWidth;
45013     },
45014     
45015     getMinHeight: function(){
45016         return this.minHeight;
45017     }
45018 });
45019
45020
45021
45022
45023  
45024
45025
45026
45027
45028
45029
45030 Roo.bootstrap.layout.North = function(config)
45031 {
45032     config.region = 'north';
45033     config.cursor = 'n-resize';
45034     
45035     Roo.bootstrap.layout.Split.call(this, config);
45036     
45037     
45038     if(this.split){
45039         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45040         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45041         this.split.el.addClass("roo-layout-split-v");
45042     }
45043     //var size = config.initialSize || config.height;
45044     //if(this.el && typeof size != "undefined"){
45045     //    this.el.setHeight(size);
45046     //}
45047 };
45048 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45049 {
45050     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45051      
45052      
45053     onRender : function(ctr, pos)
45054     {
45055         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45056         var size = this.config.initialSize || this.config.height;
45057         if(this.el && typeof size != "undefined"){
45058             this.el.setHeight(size);
45059         }
45060     
45061     },
45062     
45063     getBox : function(){
45064         if(this.collapsed){
45065             return this.collapsedEl.getBox();
45066         }
45067         var box = this.el.getBox();
45068         if(this.split){
45069             box.height += this.split.el.getHeight();
45070         }
45071         return box;
45072     },
45073     
45074     updateBox : function(box){
45075         if(this.split && !this.collapsed){
45076             box.height -= this.split.el.getHeight();
45077             this.split.el.setLeft(box.x);
45078             this.split.el.setTop(box.y+box.height);
45079             this.split.el.setWidth(box.width);
45080         }
45081         if(this.collapsed){
45082             this.updateBody(box.width, null);
45083         }
45084         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45085     }
45086 });
45087
45088
45089
45090
45091
45092 Roo.bootstrap.layout.South = function(config){
45093     config.region = 'south';
45094     config.cursor = 's-resize';
45095     Roo.bootstrap.layout.Split.call(this, config);
45096     if(this.split){
45097         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45098         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45099         this.split.el.addClass("roo-layout-split-v");
45100     }
45101     
45102 };
45103
45104 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45105     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45106     
45107     onRender : function(ctr, pos)
45108     {
45109         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45110         var size = this.config.initialSize || this.config.height;
45111         if(this.el && typeof size != "undefined"){
45112             this.el.setHeight(size);
45113         }
45114     
45115     },
45116     
45117     getBox : function(){
45118         if(this.collapsed){
45119             return this.collapsedEl.getBox();
45120         }
45121         var box = this.el.getBox();
45122         if(this.split){
45123             var sh = this.split.el.getHeight();
45124             box.height += sh;
45125             box.y -= sh;
45126         }
45127         return box;
45128     },
45129     
45130     updateBox : function(box){
45131         if(this.split && !this.collapsed){
45132             var sh = this.split.el.getHeight();
45133             box.height -= sh;
45134             box.y += sh;
45135             this.split.el.setLeft(box.x);
45136             this.split.el.setTop(box.y-sh);
45137             this.split.el.setWidth(box.width);
45138         }
45139         if(this.collapsed){
45140             this.updateBody(box.width, null);
45141         }
45142         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45143     }
45144 });
45145
45146 Roo.bootstrap.layout.East = function(config){
45147     config.region = "east";
45148     config.cursor = "e-resize";
45149     Roo.bootstrap.layout.Split.call(this, config);
45150     if(this.split){
45151         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45152         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45153         this.split.el.addClass("roo-layout-split-h");
45154     }
45155     
45156 };
45157 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45158     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45159     
45160     onRender : function(ctr, pos)
45161     {
45162         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45163         var size = this.config.initialSize || this.config.width;
45164         if(this.el && typeof size != "undefined"){
45165             this.el.setWidth(size);
45166         }
45167     
45168     },
45169     
45170     getBox : function(){
45171         if(this.collapsed){
45172             return this.collapsedEl.getBox();
45173         }
45174         var box = this.el.getBox();
45175         if(this.split){
45176             var sw = this.split.el.getWidth();
45177             box.width += sw;
45178             box.x -= sw;
45179         }
45180         return box;
45181     },
45182
45183     updateBox : function(box){
45184         if(this.split && !this.collapsed){
45185             var sw = this.split.el.getWidth();
45186             box.width -= sw;
45187             this.split.el.setLeft(box.x);
45188             this.split.el.setTop(box.y);
45189             this.split.el.setHeight(box.height);
45190             box.x += sw;
45191         }
45192         if(this.collapsed){
45193             this.updateBody(null, box.height);
45194         }
45195         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45196     }
45197 });
45198
45199 Roo.bootstrap.layout.West = function(config){
45200     config.region = "west";
45201     config.cursor = "w-resize";
45202     
45203     Roo.bootstrap.layout.Split.call(this, config);
45204     if(this.split){
45205         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45206         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45207         this.split.el.addClass("roo-layout-split-h");
45208     }
45209     
45210 };
45211 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45212     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45213     
45214     onRender: function(ctr, pos)
45215     {
45216         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45217         var size = this.config.initialSize || this.config.width;
45218         if(typeof size != "undefined"){
45219             this.el.setWidth(size);
45220         }
45221     },
45222     
45223     getBox : function(){
45224         if(this.collapsed){
45225             return this.collapsedEl.getBox();
45226         }
45227         var box = this.el.getBox();
45228         if (box.width == 0) {
45229             box.width = this.config.width; // kludge?
45230         }
45231         if(this.split){
45232             box.width += this.split.el.getWidth();
45233         }
45234         return box;
45235     },
45236     
45237     updateBox : function(box){
45238         if(this.split && !this.collapsed){
45239             var sw = this.split.el.getWidth();
45240             box.width -= sw;
45241             this.split.el.setLeft(box.x+box.width);
45242             this.split.el.setTop(box.y);
45243             this.split.el.setHeight(box.height);
45244         }
45245         if(this.collapsed){
45246             this.updateBody(null, box.height);
45247         }
45248         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45249     }
45250 });/*
45251  * Based on:
45252  * Ext JS Library 1.1.1
45253  * Copyright(c) 2006-2007, Ext JS, LLC.
45254  *
45255  * Originally Released Under LGPL - original licence link has changed is not relivant.
45256  *
45257  * Fork - LGPL
45258  * <script type="text/javascript">
45259  */
45260 /**
45261  * @class Roo.bootstrap.paenl.Content
45262  * @extends Roo.util.Observable
45263  * @children Roo.bootstrap.Component
45264  * @parent builder Roo.bootstrap.layout.Border
45265  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45266  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45267  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45268  * @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
45269  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45270  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45271  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45272  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45273  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45274  * @cfg {String} title          The title for this panel
45275  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45276  * @cfg {String} url            Calls {@link #setUrl} with this value
45277  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45278  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45279  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45280  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45281  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45282  * @cfg {Boolean} badges render the badges
45283  * @cfg {String} cls  extra classes to use  
45284  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45285  
45286  * @constructor
45287  * Create a new ContentPanel.
45288  * @param {String/Object} config A string to set only the title or a config object
45289  
45290  */
45291 Roo.bootstrap.panel.Content = function( config){
45292     
45293     this.tpl = config.tpl || false;
45294     
45295     var el = config.el;
45296     var content = config.content;
45297
45298     if(config.autoCreate){ // xtype is available if this is called from factory
45299         el = Roo.id();
45300     }
45301     this.el = Roo.get(el);
45302     if(!this.el && config && config.autoCreate){
45303         if(typeof config.autoCreate == "object"){
45304             if(!config.autoCreate.id){
45305                 config.autoCreate.id = config.id||el;
45306             }
45307             this.el = Roo.DomHelper.append(document.body,
45308                         config.autoCreate, true);
45309         }else{
45310             var elcfg =  {
45311                 tag: "div",
45312                 cls: (config.cls || '') +
45313                     (config.background ? ' bg-' + config.background : '') +
45314                     " roo-layout-inactive-content",
45315                 id: config.id||el
45316             };
45317             if (config.iframe) {
45318                 elcfg.cn = [
45319                     {
45320                         tag : 'iframe',
45321                         style : 'border: 0px',
45322                         src : 'about:blank'
45323                     }
45324                 ];
45325             }
45326               
45327             if (config.html) {
45328                 elcfg.html = config.html;
45329                 
45330             }
45331                         
45332             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45333             if (config.iframe) {
45334                 this.iframeEl = this.el.select('iframe',true).first();
45335             }
45336             
45337         }
45338     } 
45339     this.closable = false;
45340     this.loaded = false;
45341     this.active = false;
45342    
45343       
45344     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45345         
45346         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45347         
45348         this.wrapEl = this.el; //this.el.wrap();
45349         var ti = [];
45350         if (config.toolbar.items) {
45351             ti = config.toolbar.items ;
45352             delete config.toolbar.items ;
45353         }
45354         
45355         var nitems = [];
45356         this.toolbar.render(this.wrapEl, 'before');
45357         for(var i =0;i < ti.length;i++) {
45358           //  Roo.log(['add child', items[i]]);
45359             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45360         }
45361         this.toolbar.items = nitems;
45362         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45363         delete config.toolbar;
45364         
45365     }
45366     /*
45367     // xtype created footer. - not sure if will work as we normally have to render first..
45368     if (this.footer && !this.footer.el && this.footer.xtype) {
45369         if (!this.wrapEl) {
45370             this.wrapEl = this.el.wrap();
45371         }
45372     
45373         this.footer.container = this.wrapEl.createChild();
45374          
45375         this.footer = Roo.factory(this.footer, Roo);
45376         
45377     }
45378     */
45379     
45380      if(typeof config == "string"){
45381         this.title = config;
45382     }else{
45383         Roo.apply(this, config);
45384     }
45385     
45386     if(this.resizeEl){
45387         this.resizeEl = Roo.get(this.resizeEl, true);
45388     }else{
45389         this.resizeEl = this.el;
45390     }
45391     // handle view.xtype
45392     
45393  
45394     
45395     
45396     this.addEvents({
45397         /**
45398          * @event activate
45399          * Fires when this panel is activated. 
45400          * @param {Roo.ContentPanel} this
45401          */
45402         "activate" : true,
45403         /**
45404          * @event deactivate
45405          * Fires when this panel is activated. 
45406          * @param {Roo.ContentPanel} this
45407          */
45408         "deactivate" : true,
45409
45410         /**
45411          * @event resize
45412          * Fires when this panel is resized if fitToFrame is true.
45413          * @param {Roo.ContentPanel} this
45414          * @param {Number} width The width after any component adjustments
45415          * @param {Number} height The height after any component adjustments
45416          */
45417         "resize" : true,
45418         
45419          /**
45420          * @event render
45421          * Fires when this tab is created
45422          * @param {Roo.ContentPanel} this
45423          */
45424         "render" : true,
45425         
45426           /**
45427          * @event scroll
45428          * Fires when this content is scrolled
45429          * @param {Roo.ContentPanel} this
45430          * @param {Event} scrollEvent
45431          */
45432         "scroll" : true
45433         
45434         
45435         
45436     });
45437     
45438
45439     
45440     
45441     if(this.autoScroll && !this.iframe){
45442         this.resizeEl.setStyle("overflow", "auto");
45443         this.resizeEl.on('scroll', this.onScroll, this);
45444     } else {
45445         // fix randome scrolling
45446         //this.el.on('scroll', function() {
45447         //    Roo.log('fix random scolling');
45448         //    this.scrollTo('top',0); 
45449         //});
45450     }
45451     content = content || this.content;
45452     if(content){
45453         this.setContent(content);
45454     }
45455     if(config && config.url){
45456         this.setUrl(this.url, this.params, this.loadOnce);
45457     }
45458     
45459     
45460     
45461     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45462     
45463     if (this.view && typeof(this.view.xtype) != 'undefined') {
45464         this.view.el = this.el.appendChild(document.createElement("div"));
45465         this.view = Roo.factory(this.view); 
45466         this.view.render  &&  this.view.render(false, '');  
45467     }
45468     
45469     
45470     this.fireEvent('render', this);
45471 };
45472
45473 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45474     
45475     cls : '',
45476     background : '',
45477     
45478     tabTip : '',
45479     
45480     iframe : false,
45481     iframeEl : false,
45482     
45483     /* Resize Element - use this to work out scroll etc. */
45484     resizeEl : false,
45485     
45486     setRegion : function(region){
45487         this.region = region;
45488         this.setActiveClass(region && !this.background);
45489     },
45490     
45491     
45492     setActiveClass: function(state)
45493     {
45494         if(state){
45495            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45496            this.el.setStyle('position','relative');
45497         }else{
45498            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45499            this.el.setStyle('position', 'absolute');
45500         } 
45501     },
45502     
45503     /**
45504      * Returns the toolbar for this Panel if one was configured. 
45505      * @return {Roo.Toolbar} 
45506      */
45507     getToolbar : function(){
45508         return this.toolbar;
45509     },
45510     
45511     setActiveState : function(active)
45512     {
45513         this.active = active;
45514         this.setActiveClass(active);
45515         if(!active){
45516             if(this.fireEvent("deactivate", this) === false){
45517                 return false;
45518             }
45519             return true;
45520         }
45521         this.fireEvent("activate", this);
45522         return true;
45523     },
45524     /**
45525      * Updates this panel's element (not for iframe)
45526      * @param {String} content The new content
45527      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45528     */
45529     setContent : function(content, loadScripts){
45530         if (this.iframe) {
45531             return;
45532         }
45533         
45534         this.el.update(content, loadScripts);
45535     },
45536
45537     ignoreResize : function(w, h)
45538     {
45539         //return false; // always resize?
45540         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45541             return true;
45542         }else{
45543             this.lastSize = {width: w, height: h};
45544             return false;
45545         }
45546     },
45547     /**
45548      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45549      * @return {Roo.UpdateManager} The UpdateManager
45550      */
45551     getUpdateManager : function(){
45552         if (this.iframe) {
45553             return false;
45554         }
45555         return this.el.getUpdateManager();
45556     },
45557      /**
45558      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45559      * Does not work with IFRAME contents
45560      * @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:
45561 <pre><code>
45562 panel.load({
45563     url: "your-url.php",
45564     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45565     callback: yourFunction,
45566     scope: yourObject, //(optional scope)
45567     discardUrl: false,
45568     nocache: false,
45569     text: "Loading...",
45570     timeout: 30,
45571     scripts: false
45572 });
45573 </code></pre>
45574      
45575      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45576      * 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.
45577      * @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}
45578      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45579      * @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.
45580      * @return {Roo.ContentPanel} this
45581      */
45582     load : function(){
45583         
45584         if (this.iframe) {
45585             return this;
45586         }
45587         
45588         var um = this.el.getUpdateManager();
45589         um.update.apply(um, arguments);
45590         return this;
45591     },
45592
45593
45594     /**
45595      * 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.
45596      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45597      * @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)
45598      * @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)
45599      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45600      */
45601     setUrl : function(url, params, loadOnce){
45602         if (this.iframe) {
45603             this.iframeEl.dom.src = url;
45604             return false;
45605         }
45606         
45607         if(this.refreshDelegate){
45608             this.removeListener("activate", this.refreshDelegate);
45609         }
45610         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45611         this.on("activate", this.refreshDelegate);
45612         return this.el.getUpdateManager();
45613     },
45614     
45615     _handleRefresh : function(url, params, loadOnce){
45616         if(!loadOnce || !this.loaded){
45617             var updater = this.el.getUpdateManager();
45618             updater.update(url, params, this._setLoaded.createDelegate(this));
45619         }
45620     },
45621     
45622     _setLoaded : function(){
45623         this.loaded = true;
45624     }, 
45625     
45626     /**
45627      * Returns this panel's id
45628      * @return {String} 
45629      */
45630     getId : function(){
45631         return this.el.id;
45632     },
45633     
45634     /** 
45635      * Returns this panel's element - used by regiosn to add.
45636      * @return {Roo.Element} 
45637      */
45638     getEl : function(){
45639         return this.wrapEl || this.el;
45640     },
45641     
45642    
45643     
45644     adjustForComponents : function(width, height)
45645     {
45646         //Roo.log('adjustForComponents ');
45647         if(this.resizeEl != this.el){
45648             width -= this.el.getFrameWidth('lr');
45649             height -= this.el.getFrameWidth('tb');
45650         }
45651         if(this.toolbar){
45652             var te = this.toolbar.getEl();
45653             te.setWidth(width);
45654             height -= te.getHeight();
45655         }
45656         if(this.footer){
45657             var te = this.footer.getEl();
45658             te.setWidth(width);
45659             height -= te.getHeight();
45660         }
45661         
45662         
45663         if(this.adjustments){
45664             width += this.adjustments[0];
45665             height += this.adjustments[1];
45666         }
45667         return {"width": width, "height": height};
45668     },
45669     
45670     setSize : function(width, height){
45671         if(this.fitToFrame && !this.ignoreResize(width, height)){
45672             if(this.fitContainer && this.resizeEl != this.el){
45673                 this.el.setSize(width, height);
45674             }
45675             var size = this.adjustForComponents(width, height);
45676             if (this.iframe) {
45677                 this.iframeEl.setSize(width,height);
45678             }
45679             
45680             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45681             this.fireEvent('resize', this, size.width, size.height);
45682             
45683             
45684         }
45685     },
45686     
45687     /**
45688      * Returns this panel's title
45689      * @return {String} 
45690      */
45691     getTitle : function(){
45692         
45693         if (typeof(this.title) != 'object') {
45694             return this.title;
45695         }
45696         
45697         var t = '';
45698         for (var k in this.title) {
45699             if (!this.title.hasOwnProperty(k)) {
45700                 continue;
45701             }
45702             
45703             if (k.indexOf('-') >= 0) {
45704                 var s = k.split('-');
45705                 for (var i = 0; i<s.length; i++) {
45706                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45707                 }
45708             } else {
45709                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45710             }
45711         }
45712         return t;
45713     },
45714     
45715     /**
45716      * Set this panel's title
45717      * @param {String} title
45718      */
45719     setTitle : function(title){
45720         this.title = title;
45721         if(this.region){
45722             this.region.updatePanelTitle(this, title);
45723         }
45724     },
45725     
45726     /**
45727      * Returns true is this panel was configured to be closable
45728      * @return {Boolean} 
45729      */
45730     isClosable : function(){
45731         return this.closable;
45732     },
45733     
45734     beforeSlide : function(){
45735         this.el.clip();
45736         this.resizeEl.clip();
45737     },
45738     
45739     afterSlide : function(){
45740         this.el.unclip();
45741         this.resizeEl.unclip();
45742     },
45743     
45744     /**
45745      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45746      *   Will fail silently if the {@link #setUrl} method has not been called.
45747      *   This does not activate the panel, just updates its content.
45748      */
45749     refresh : function(){
45750         if(this.refreshDelegate){
45751            this.loaded = false;
45752            this.refreshDelegate();
45753         }
45754     },
45755     
45756     /**
45757      * Destroys this panel
45758      */
45759     destroy : function(){
45760         this.el.removeAllListeners();
45761         var tempEl = document.createElement("span");
45762         tempEl.appendChild(this.el.dom);
45763         tempEl.innerHTML = "";
45764         this.el.remove();
45765         this.el = null;
45766     },
45767     
45768     /**
45769      * form - if the content panel contains a form - this is a reference to it.
45770      * @type {Roo.form.Form}
45771      */
45772     form : false,
45773     /**
45774      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45775      *    This contains a reference to it.
45776      * @type {Roo.View}
45777      */
45778     view : false,
45779     
45780       /**
45781      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45782      * <pre><code>
45783
45784 layout.addxtype({
45785        xtype : 'Form',
45786        items: [ .... ]
45787    }
45788 );
45789
45790 </code></pre>
45791      * @param {Object} cfg Xtype definition of item to add.
45792      */
45793     
45794     
45795     getChildContainer: function () {
45796         return this.getEl();
45797     },
45798     
45799     
45800     onScroll : function(e)
45801     {
45802         this.fireEvent('scroll', this, e);
45803     }
45804     
45805     
45806     /*
45807         var  ret = new Roo.factory(cfg);
45808         return ret;
45809         
45810         
45811         // add form..
45812         if (cfg.xtype.match(/^Form$/)) {
45813             
45814             var el;
45815             //if (this.footer) {
45816             //    el = this.footer.container.insertSibling(false, 'before');
45817             //} else {
45818                 el = this.el.createChild();
45819             //}
45820
45821             this.form = new  Roo.form.Form(cfg);
45822             
45823             
45824             if ( this.form.allItems.length) {
45825                 this.form.render(el.dom);
45826             }
45827             return this.form;
45828         }
45829         // should only have one of theses..
45830         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45831             // views.. should not be just added - used named prop 'view''
45832             
45833             cfg.el = this.el.appendChild(document.createElement("div"));
45834             // factory?
45835             
45836             var ret = new Roo.factory(cfg);
45837              
45838              ret.render && ret.render(false, ''); // render blank..
45839             this.view = ret;
45840             return ret;
45841         }
45842         return false;
45843     }
45844     \*/
45845 });
45846  
45847 /**
45848  * @class Roo.bootstrap.panel.Grid
45849  * @extends Roo.bootstrap.panel.Content
45850  * @constructor
45851  * Create a new GridPanel.
45852  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45853  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45854  * @param {Object} config A the config object
45855   
45856  */
45857
45858
45859
45860 Roo.bootstrap.panel.Grid = function(config)
45861 {
45862     
45863       
45864     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45865         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45866
45867     config.el = this.wrapper;
45868     //this.el = this.wrapper;
45869     
45870       if (config.container) {
45871         // ctor'ed from a Border/panel.grid
45872         
45873         
45874         this.wrapper.setStyle("overflow", "hidden");
45875         this.wrapper.addClass('roo-grid-container');
45876
45877     }
45878     
45879     
45880     if(config.toolbar){
45881         var tool_el = this.wrapper.createChild();    
45882         this.toolbar = Roo.factory(config.toolbar);
45883         var ti = [];
45884         if (config.toolbar.items) {
45885             ti = config.toolbar.items ;
45886             delete config.toolbar.items ;
45887         }
45888         
45889         var nitems = [];
45890         this.toolbar.render(tool_el);
45891         for(var i =0;i < ti.length;i++) {
45892           //  Roo.log(['add child', items[i]]);
45893             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45894         }
45895         this.toolbar.items = nitems;
45896         
45897         delete config.toolbar;
45898     }
45899     
45900     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45901     config.grid.scrollBody = true;;
45902     config.grid.monitorWindowResize = false; // turn off autosizing
45903     config.grid.autoHeight = false;
45904     config.grid.autoWidth = false;
45905     
45906     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45907     
45908     if (config.background) {
45909         // render grid on panel activation (if panel background)
45910         this.on('activate', function(gp) {
45911             if (!gp.grid.rendered) {
45912                 gp.grid.render(this.wrapper);
45913                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45914             }
45915         });
45916             
45917     } else {
45918         this.grid.render(this.wrapper);
45919         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45920
45921     }
45922     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45923     // ??? needed ??? config.el = this.wrapper;
45924     
45925     
45926     
45927   
45928     // xtype created footer. - not sure if will work as we normally have to render first..
45929     if (this.footer && !this.footer.el && this.footer.xtype) {
45930         
45931         var ctr = this.grid.getView().getFooterPanel(true);
45932         this.footer.dataSource = this.grid.dataSource;
45933         this.footer = Roo.factory(this.footer, Roo);
45934         this.footer.render(ctr);
45935         
45936     }
45937     
45938     
45939     
45940     
45941      
45942 };
45943
45944 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45945 {
45946   
45947     getId : function(){
45948         return this.grid.id;
45949     },
45950     
45951     /**
45952      * Returns the grid for this panel
45953      * @return {Roo.bootstrap.Table} 
45954      */
45955     getGrid : function(){
45956         return this.grid;    
45957     },
45958     
45959     setSize : function(width, height)
45960     {
45961      
45962         //if(!this.ignoreResize(width, height)){
45963             var grid = this.grid;
45964             var size = this.adjustForComponents(width, height);
45965             // tfoot is not a footer?
45966           
45967             
45968             var gridel = grid.getGridEl();
45969             gridel.setSize(size.width, size.height);
45970             
45971             var tbd = grid.getGridEl().select('tbody', true).first();
45972             var thd = grid.getGridEl().select('thead',true).first();
45973             var tbf= grid.getGridEl().select('tfoot', true).first();
45974
45975             if (tbf) {
45976                 size.height -= tbf.getHeight();
45977             }
45978             if (thd) {
45979                 size.height -= thd.getHeight();
45980             }
45981             
45982             tbd.setSize(size.width, size.height );
45983             // this is for the account management tab -seems to work there.
45984             var thd = grid.getGridEl().select('thead',true).first();
45985             //if (tbd) {
45986             //    tbd.setSize(size.width, size.height - thd.getHeight());
45987             //}
45988              
45989             grid.autoSize();
45990         //}
45991    
45992     },
45993      
45994     
45995     
45996     beforeSlide : function(){
45997         this.grid.getView().scroller.clip();
45998     },
45999     
46000     afterSlide : function(){
46001         this.grid.getView().scroller.unclip();
46002     },
46003     
46004     destroy : function(){
46005         this.grid.destroy();
46006         delete this.grid;
46007         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46008     }
46009 });
46010
46011 /**
46012  * @class Roo.bootstrap.panel.Nest
46013  * @extends Roo.bootstrap.panel.Content
46014  * @constructor
46015  * Create a new Panel, that can contain a layout.Border.
46016  * 
46017  * 
46018  * @param {String/Object} config A string to set only the title or a config object
46019  */
46020 Roo.bootstrap.panel.Nest = function(config)
46021 {
46022     // construct with only one argument..
46023     /* FIXME - implement nicer consturctors
46024     if (layout.layout) {
46025         config = layout;
46026         layout = config.layout;
46027         delete config.layout;
46028     }
46029     if (layout.xtype && !layout.getEl) {
46030         // then layout needs constructing..
46031         layout = Roo.factory(layout, Roo);
46032     }
46033     */
46034     
46035     config.el =  config.layout.getEl();
46036     
46037     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46038     
46039     config.layout.monitorWindowResize = false; // turn off autosizing
46040     this.layout = config.layout;
46041     this.layout.getEl().addClass("roo-layout-nested-layout");
46042     this.layout.parent = this;
46043     
46044     
46045     
46046     
46047 };
46048
46049 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46050     /**
46051     * @cfg {Roo.BorderLayout} layout The layout for this panel
46052     */
46053     layout : false,
46054
46055     setSize : function(width, height){
46056         if(!this.ignoreResize(width, height)){
46057             var size = this.adjustForComponents(width, height);
46058             var el = this.layout.getEl();
46059             if (size.height < 1) {
46060                 el.setWidth(size.width);   
46061             } else {
46062                 el.setSize(size.width, size.height);
46063             }
46064             var touch = el.dom.offsetWidth;
46065             this.layout.layout();
46066             // ie requires a double layout on the first pass
46067             if(Roo.isIE && !this.initialized){
46068                 this.initialized = true;
46069                 this.layout.layout();
46070             }
46071         }
46072     },
46073     
46074     // activate all subpanels if not currently active..
46075     
46076     setActiveState : function(active){
46077         this.active = active;
46078         this.setActiveClass(active);
46079         
46080         if(!active){
46081             this.fireEvent("deactivate", this);
46082             return;
46083         }
46084         
46085         this.fireEvent("activate", this);
46086         // not sure if this should happen before or after..
46087         if (!this.layout) {
46088             return; // should not happen..
46089         }
46090         var reg = false;
46091         for (var r in this.layout.regions) {
46092             reg = this.layout.getRegion(r);
46093             if (reg.getActivePanel()) {
46094                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46095                 reg.setActivePanel(reg.getActivePanel());
46096                 continue;
46097             }
46098             if (!reg.panels.length) {
46099                 continue;
46100             }
46101             reg.showPanel(reg.getPanel(0));
46102         }
46103         
46104         
46105         
46106         
46107     },
46108     
46109     /**
46110      * Returns the nested BorderLayout for this panel
46111      * @return {Roo.BorderLayout} 
46112      */
46113     getLayout : function(){
46114         return this.layout;
46115     },
46116     
46117      /**
46118      * Adds a xtype elements to the layout of the nested panel
46119      * <pre><code>
46120
46121 panel.addxtype({
46122        xtype : 'ContentPanel',
46123        region: 'west',
46124        items: [ .... ]
46125    }
46126 );
46127
46128 panel.addxtype({
46129         xtype : 'NestedLayoutPanel',
46130         region: 'west',
46131         layout: {
46132            center: { },
46133            west: { }   
46134         },
46135         items : [ ... list of content panels or nested layout panels.. ]
46136    }
46137 );
46138 </code></pre>
46139      * @param {Object} cfg Xtype definition of item to add.
46140      */
46141     addxtype : function(cfg) {
46142         return this.layout.addxtype(cfg);
46143     
46144     }
46145 });/*
46146  * Based on:
46147  * Ext JS Library 1.1.1
46148  * Copyright(c) 2006-2007, Ext JS, LLC.
46149  *
46150  * Originally Released Under LGPL - original licence link has changed is not relivant.
46151  *
46152  * Fork - LGPL
46153  * <script type="text/javascript">
46154  */
46155 /**
46156  * @class Roo.TabPanel
46157  * @extends Roo.util.Observable
46158  * A lightweight tab container.
46159  * <br><br>
46160  * Usage:
46161  * <pre><code>
46162 // basic tabs 1, built from existing content
46163 var tabs = new Roo.TabPanel("tabs1");
46164 tabs.addTab("script", "View Script");
46165 tabs.addTab("markup", "View Markup");
46166 tabs.activate("script");
46167
46168 // more advanced tabs, built from javascript
46169 var jtabs = new Roo.TabPanel("jtabs");
46170 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46171
46172 // set up the UpdateManager
46173 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46174 var updater = tab2.getUpdateManager();
46175 updater.setDefaultUrl("ajax1.htm");
46176 tab2.on('activate', updater.refresh, updater, true);
46177
46178 // Use setUrl for Ajax loading
46179 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46180 tab3.setUrl("ajax2.htm", null, true);
46181
46182 // Disabled tab
46183 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46184 tab4.disable();
46185
46186 jtabs.activate("jtabs-1");
46187  * </code></pre>
46188  * @constructor
46189  * Create a new TabPanel.
46190  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46191  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46192  */
46193 Roo.bootstrap.panel.Tabs = function(config){
46194     /**
46195     * The container element for this TabPanel.
46196     * @type Roo.Element
46197     */
46198     this.el = Roo.get(config.el);
46199     delete config.el;
46200     if(config){
46201         if(typeof config == "boolean"){
46202             this.tabPosition = config ? "bottom" : "top";
46203         }else{
46204             Roo.apply(this, config);
46205         }
46206     }
46207     
46208     if(this.tabPosition == "bottom"){
46209         // if tabs are at the bottom = create the body first.
46210         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46211         this.el.addClass("roo-tabs-bottom");
46212     }
46213     // next create the tabs holders
46214     
46215     if (this.tabPosition == "west"){
46216         
46217         var reg = this.region; // fake it..
46218         while (reg) {
46219             if (!reg.mgr.parent) {
46220                 break;
46221             }
46222             reg = reg.mgr.parent.region;
46223         }
46224         Roo.log("got nest?");
46225         Roo.log(reg);
46226         if (reg.mgr.getRegion('west')) {
46227             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46228             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46229             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46230             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46231             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46232         
46233             
46234         }
46235         
46236         
46237     } else {
46238      
46239         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46240         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46241         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46242         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46243     }
46244     
46245     
46246     if(Roo.isIE){
46247         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46248     }
46249     
46250     // finally - if tabs are at the top, then create the body last..
46251     if(this.tabPosition != "bottom"){
46252         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46253          * @type Roo.Element
46254          */
46255         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46256         this.el.addClass("roo-tabs-top");
46257     }
46258     this.items = [];
46259
46260     this.bodyEl.setStyle("position", "relative");
46261
46262     this.active = null;
46263     this.activateDelegate = this.activate.createDelegate(this);
46264
46265     this.addEvents({
46266         /**
46267          * @event tabchange
46268          * Fires when the active tab changes
46269          * @param {Roo.TabPanel} this
46270          * @param {Roo.TabPanelItem} activePanel The new active tab
46271          */
46272         "tabchange": true,
46273         /**
46274          * @event beforetabchange
46275          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46276          * @param {Roo.TabPanel} this
46277          * @param {Object} e Set cancel to true on this object to cancel the tab change
46278          * @param {Roo.TabPanelItem} tab The tab being changed to
46279          */
46280         "beforetabchange" : true
46281     });
46282
46283     Roo.EventManager.onWindowResize(this.onResize, this);
46284     this.cpad = this.el.getPadding("lr");
46285     this.hiddenCount = 0;
46286
46287
46288     // toolbar on the tabbar support...
46289     if (this.toolbar) {
46290         alert("no toolbar support yet");
46291         this.toolbar  = false;
46292         /*
46293         var tcfg = this.toolbar;
46294         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46295         this.toolbar = new Roo.Toolbar(tcfg);
46296         if (Roo.isSafari) {
46297             var tbl = tcfg.container.child('table', true);
46298             tbl.setAttribute('width', '100%');
46299         }
46300         */
46301         
46302     }
46303    
46304
46305
46306     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46307 };
46308
46309 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46310     /*
46311      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46312      */
46313     tabPosition : "top",
46314     /*
46315      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46316      */
46317     currentTabWidth : 0,
46318     /*
46319      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46320      */
46321     minTabWidth : 40,
46322     /*
46323      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46324      */
46325     maxTabWidth : 250,
46326     /*
46327      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46328      */
46329     preferredTabWidth : 175,
46330     /*
46331      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46332      */
46333     resizeTabs : false,
46334     /*
46335      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46336      */
46337     monitorResize : true,
46338     /*
46339      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46340      */
46341     toolbar : false,  // set by caller..
46342     
46343     region : false, /// set by caller
46344     
46345     disableTooltips : true, // not used yet...
46346
46347     /**
46348      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46349      * @param {String} id The id of the div to use <b>or create</b>
46350      * @param {String} text The text for the tab
46351      * @param {String} content (optional) Content to put in the TabPanelItem body
46352      * @param {Boolean} closable (optional) True to create a close icon on the tab
46353      * @return {Roo.TabPanelItem} The created TabPanelItem
46354      */
46355     addTab : function(id, text, content, closable, tpl)
46356     {
46357         var item = new Roo.bootstrap.panel.TabItem({
46358             panel: this,
46359             id : id,
46360             text : text,
46361             closable : closable,
46362             tpl : tpl
46363         });
46364         this.addTabItem(item);
46365         if(content){
46366             item.setContent(content);
46367         }
46368         return item;
46369     },
46370
46371     /**
46372      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46373      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46374      * @return {Roo.TabPanelItem}
46375      */
46376     getTab : function(id){
46377         return this.items[id];
46378     },
46379
46380     /**
46381      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46382      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46383      */
46384     hideTab : function(id){
46385         var t = this.items[id];
46386         if(!t.isHidden()){
46387            t.setHidden(true);
46388            this.hiddenCount++;
46389            this.autoSizeTabs();
46390         }
46391     },
46392
46393     /**
46394      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46395      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46396      */
46397     unhideTab : function(id){
46398         var t = this.items[id];
46399         if(t.isHidden()){
46400            t.setHidden(false);
46401            this.hiddenCount--;
46402            this.autoSizeTabs();
46403         }
46404     },
46405
46406     /**
46407      * Adds an existing {@link Roo.TabPanelItem}.
46408      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46409      */
46410     addTabItem : function(item)
46411     {
46412         this.items[item.id] = item;
46413         this.items.push(item);
46414         this.autoSizeTabs();
46415       //  if(this.resizeTabs){
46416     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46417   //         this.autoSizeTabs();
46418 //        }else{
46419 //            item.autoSize();
46420        // }
46421     },
46422
46423     /**
46424      * Removes a {@link Roo.TabPanelItem}.
46425      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46426      */
46427     removeTab : function(id){
46428         var items = this.items;
46429         var tab = items[id];
46430         if(!tab) { return; }
46431         var index = items.indexOf(tab);
46432         if(this.active == tab && items.length > 1){
46433             var newTab = this.getNextAvailable(index);
46434             if(newTab) {
46435                 newTab.activate();
46436             }
46437         }
46438         this.stripEl.dom.removeChild(tab.pnode.dom);
46439         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46440             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46441         }
46442         items.splice(index, 1);
46443         delete this.items[tab.id];
46444         tab.fireEvent("close", tab);
46445         tab.purgeListeners();
46446         this.autoSizeTabs();
46447     },
46448
46449     getNextAvailable : function(start){
46450         var items = this.items;
46451         var index = start;
46452         // look for a next tab that will slide over to
46453         // replace the one being removed
46454         while(index < items.length){
46455             var item = items[++index];
46456             if(item && !item.isHidden()){
46457                 return item;
46458             }
46459         }
46460         // if one isn't found select the previous tab (on the left)
46461         index = start;
46462         while(index >= 0){
46463             var item = items[--index];
46464             if(item && !item.isHidden()){
46465                 return item;
46466             }
46467         }
46468         return null;
46469     },
46470
46471     /**
46472      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46473      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46474      */
46475     disableTab : function(id){
46476         var tab = this.items[id];
46477         if(tab && this.active != tab){
46478             tab.disable();
46479         }
46480     },
46481
46482     /**
46483      * Enables a {@link Roo.TabPanelItem} that is disabled.
46484      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46485      */
46486     enableTab : function(id){
46487         var tab = this.items[id];
46488         tab.enable();
46489     },
46490
46491     /**
46492      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46493      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46494      * @return {Roo.TabPanelItem} The TabPanelItem.
46495      */
46496     activate : function(id)
46497     {
46498         //Roo.log('activite:'  + id);
46499         
46500         var tab = this.items[id];
46501         if(!tab){
46502             return null;
46503         }
46504         if(tab == this.active || tab.disabled){
46505             return tab;
46506         }
46507         var e = {};
46508         this.fireEvent("beforetabchange", this, e, tab);
46509         if(e.cancel !== true && !tab.disabled){
46510             if(this.active){
46511                 this.active.hide();
46512             }
46513             this.active = this.items[id];
46514             this.active.show();
46515             this.fireEvent("tabchange", this, this.active);
46516         }
46517         return tab;
46518     },
46519
46520     /**
46521      * Gets the active {@link Roo.TabPanelItem}.
46522      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46523      */
46524     getActiveTab : function(){
46525         return this.active;
46526     },
46527
46528     /**
46529      * Updates the tab body element to fit the height of the container element
46530      * for overflow scrolling
46531      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46532      */
46533     syncHeight : function(targetHeight){
46534         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46535         var bm = this.bodyEl.getMargins();
46536         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46537         this.bodyEl.setHeight(newHeight);
46538         return newHeight;
46539     },
46540
46541     onResize : function(){
46542         if(this.monitorResize){
46543             this.autoSizeTabs();
46544         }
46545     },
46546
46547     /**
46548      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46549      */
46550     beginUpdate : function(){
46551         this.updating = true;
46552     },
46553
46554     /**
46555      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46556      */
46557     endUpdate : function(){
46558         this.updating = false;
46559         this.autoSizeTabs();
46560     },
46561
46562     /**
46563      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46564      */
46565     autoSizeTabs : function()
46566     {
46567         var count = this.items.length;
46568         var vcount = count - this.hiddenCount;
46569         
46570         if (vcount < 2) {
46571             this.stripEl.hide();
46572         } else {
46573             this.stripEl.show();
46574         }
46575         
46576         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46577             return;
46578         }
46579         
46580         
46581         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46582         var availWidth = Math.floor(w / vcount);
46583         var b = this.stripBody;
46584         if(b.getWidth() > w){
46585             var tabs = this.items;
46586             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46587             if(availWidth < this.minTabWidth){
46588                 /*if(!this.sleft){    // incomplete scrolling code
46589                     this.createScrollButtons();
46590                 }
46591                 this.showScroll();
46592                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46593             }
46594         }else{
46595             if(this.currentTabWidth < this.preferredTabWidth){
46596                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46597             }
46598         }
46599     },
46600
46601     /**
46602      * Returns the number of tabs in this TabPanel.
46603      * @return {Number}
46604      */
46605      getCount : function(){
46606          return this.items.length;
46607      },
46608
46609     /**
46610      * Resizes all the tabs to the passed width
46611      * @param {Number} The new width
46612      */
46613     setTabWidth : function(width){
46614         this.currentTabWidth = width;
46615         for(var i = 0, len = this.items.length; i < len; i++) {
46616                 if(!this.items[i].isHidden()) {
46617                 this.items[i].setWidth(width);
46618             }
46619         }
46620     },
46621
46622     /**
46623      * Destroys this TabPanel
46624      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46625      */
46626     destroy : function(removeEl){
46627         Roo.EventManager.removeResizeListener(this.onResize, this);
46628         for(var i = 0, len = this.items.length; i < len; i++){
46629             this.items[i].purgeListeners();
46630         }
46631         if(removeEl === true){
46632             this.el.update("");
46633             this.el.remove();
46634         }
46635     },
46636     
46637     createStrip : function(container)
46638     {
46639         var strip = document.createElement("nav");
46640         strip.className = Roo.bootstrap.version == 4 ?
46641             "navbar-light bg-light" : 
46642             "navbar navbar-default"; //"x-tabs-wrap";
46643         container.appendChild(strip);
46644         return strip;
46645     },
46646     
46647     createStripList : function(strip)
46648     {
46649         // div wrapper for retard IE
46650         // returns the "tr" element.
46651         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46652         //'<div class="x-tabs-strip-wrap">'+
46653           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46654           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46655         return strip.firstChild; //.firstChild.firstChild.firstChild;
46656     },
46657     createBody : function(container)
46658     {
46659         var body = document.createElement("div");
46660         Roo.id(body, "tab-body");
46661         //Roo.fly(body).addClass("x-tabs-body");
46662         Roo.fly(body).addClass("tab-content");
46663         container.appendChild(body);
46664         return body;
46665     },
46666     createItemBody :function(bodyEl, id){
46667         var body = Roo.getDom(id);
46668         if(!body){
46669             body = document.createElement("div");
46670             body.id = id;
46671         }
46672         //Roo.fly(body).addClass("x-tabs-item-body");
46673         Roo.fly(body).addClass("tab-pane");
46674          bodyEl.insertBefore(body, bodyEl.firstChild);
46675         return body;
46676     },
46677     /** @private */
46678     createStripElements :  function(stripEl, text, closable, tpl)
46679     {
46680         var td = document.createElement("li"); // was td..
46681         td.className = 'nav-item';
46682         
46683         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46684         
46685         
46686         stripEl.appendChild(td);
46687         /*if(closable){
46688             td.className = "x-tabs-closable";
46689             if(!this.closeTpl){
46690                 this.closeTpl = new Roo.Template(
46691                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46692                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46693                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46694                 );
46695             }
46696             var el = this.closeTpl.overwrite(td, {"text": text});
46697             var close = el.getElementsByTagName("div")[0];
46698             var inner = el.getElementsByTagName("em")[0];
46699             return {"el": el, "close": close, "inner": inner};
46700         } else {
46701         */
46702         // not sure what this is..
46703 //            if(!this.tabTpl){
46704                 //this.tabTpl = new Roo.Template(
46705                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46706                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46707                 //);
46708 //                this.tabTpl = new Roo.Template(
46709 //                   '<a href="#">' +
46710 //                   '<span unselectable="on"' +
46711 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46712 //                            ' >{text}</span></a>'
46713 //                );
46714 //                
46715 //            }
46716
46717
46718             var template = tpl || this.tabTpl || false;
46719             
46720             if(!template){
46721                 template =  new Roo.Template(
46722                         Roo.bootstrap.version == 4 ? 
46723                             (
46724                                 '<a class="nav-link" href="#" unselectable="on"' +
46725                                      (this.disableTooltips ? '' : ' title="{text}"') +
46726                                      ' >{text}</a>'
46727                             ) : (
46728                                 '<a class="nav-link" href="#">' +
46729                                 '<span unselectable="on"' +
46730                                          (this.disableTooltips ? '' : ' title="{text}"') +
46731                                     ' >{text}</span></a>'
46732                             )
46733                 );
46734             }
46735             
46736             switch (typeof(template)) {
46737                 case 'object' :
46738                     break;
46739                 case 'string' :
46740                     template = new Roo.Template(template);
46741                     break;
46742                 default :
46743                     break;
46744             }
46745             
46746             var el = template.overwrite(td, {"text": text});
46747             
46748             var inner = el.getElementsByTagName("span")[0];
46749             
46750             return {"el": el, "inner": inner};
46751             
46752     }
46753         
46754     
46755 });
46756
46757 /**
46758  * @class Roo.TabPanelItem
46759  * @extends Roo.util.Observable
46760  * Represents an individual item (tab plus body) in a TabPanel.
46761  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46762  * @param {String} id The id of this TabPanelItem
46763  * @param {String} text The text for the tab of this TabPanelItem
46764  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46765  */
46766 Roo.bootstrap.panel.TabItem = function(config){
46767     /**
46768      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46769      * @type Roo.TabPanel
46770      */
46771     this.tabPanel = config.panel;
46772     /**
46773      * The id for this TabPanelItem
46774      * @type String
46775      */
46776     this.id = config.id;
46777     /** @private */
46778     this.disabled = false;
46779     /** @private */
46780     this.text = config.text;
46781     /** @private */
46782     this.loaded = false;
46783     this.closable = config.closable;
46784
46785     /**
46786      * The body element for this TabPanelItem.
46787      * @type Roo.Element
46788      */
46789     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46790     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46791     this.bodyEl.setStyle("display", "block");
46792     this.bodyEl.setStyle("zoom", "1");
46793     //this.hideAction();
46794
46795     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46796     /** @private */
46797     this.el = Roo.get(els.el);
46798     this.inner = Roo.get(els.inner, true);
46799      this.textEl = Roo.bootstrap.version == 4 ?
46800         this.el : Roo.get(this.el.dom.firstChild, true);
46801
46802     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46803     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46804
46805     
46806 //    this.el.on("mousedown", this.onTabMouseDown, this);
46807     this.el.on("click", this.onTabClick, this);
46808     /** @private */
46809     if(config.closable){
46810         var c = Roo.get(els.close, true);
46811         c.dom.title = this.closeText;
46812         c.addClassOnOver("close-over");
46813         c.on("click", this.closeClick, this);
46814      }
46815
46816     this.addEvents({
46817          /**
46818          * @event activate
46819          * Fires when this tab becomes the active tab.
46820          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46821          * @param {Roo.TabPanelItem} this
46822          */
46823         "activate": true,
46824         /**
46825          * @event beforeclose
46826          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46827          * @param {Roo.TabPanelItem} this
46828          * @param {Object} e Set cancel to true on this object to cancel the close.
46829          */
46830         "beforeclose": true,
46831         /**
46832          * @event close
46833          * Fires when this tab is closed.
46834          * @param {Roo.TabPanelItem} this
46835          */
46836          "close": true,
46837         /**
46838          * @event deactivate
46839          * Fires when this tab is no longer the active tab.
46840          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46841          * @param {Roo.TabPanelItem} this
46842          */
46843          "deactivate" : true
46844     });
46845     this.hidden = false;
46846
46847     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46848 };
46849
46850 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46851            {
46852     purgeListeners : function(){
46853        Roo.util.Observable.prototype.purgeListeners.call(this);
46854        this.el.removeAllListeners();
46855     },
46856     /**
46857      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46858      */
46859     show : function(){
46860         this.status_node.addClass("active");
46861         this.showAction();
46862         if(Roo.isOpera){
46863             this.tabPanel.stripWrap.repaint();
46864         }
46865         this.fireEvent("activate", this.tabPanel, this);
46866     },
46867
46868     /**
46869      * Returns true if this tab is the active tab.
46870      * @return {Boolean}
46871      */
46872     isActive : function(){
46873         return this.tabPanel.getActiveTab() == this;
46874     },
46875
46876     /**
46877      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46878      */
46879     hide : function(){
46880         this.status_node.removeClass("active");
46881         this.hideAction();
46882         this.fireEvent("deactivate", this.tabPanel, this);
46883     },
46884
46885     hideAction : function(){
46886         this.bodyEl.hide();
46887         this.bodyEl.setStyle("position", "absolute");
46888         this.bodyEl.setLeft("-20000px");
46889         this.bodyEl.setTop("-20000px");
46890     },
46891
46892     showAction : function(){
46893         this.bodyEl.setStyle("position", "relative");
46894         this.bodyEl.setTop("");
46895         this.bodyEl.setLeft("");
46896         this.bodyEl.show();
46897     },
46898
46899     /**
46900      * Set the tooltip for the tab.
46901      * @param {String} tooltip The tab's tooltip
46902      */
46903     setTooltip : function(text){
46904         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46905             this.textEl.dom.qtip = text;
46906             this.textEl.dom.removeAttribute('title');
46907         }else{
46908             this.textEl.dom.title = text;
46909         }
46910     },
46911
46912     onTabClick : function(e){
46913         e.preventDefault();
46914         this.tabPanel.activate(this.id);
46915     },
46916
46917     onTabMouseDown : function(e){
46918         e.preventDefault();
46919         this.tabPanel.activate(this.id);
46920     },
46921 /*
46922     getWidth : function(){
46923         return this.inner.getWidth();
46924     },
46925
46926     setWidth : function(width){
46927         var iwidth = width - this.linode.getPadding("lr");
46928         this.inner.setWidth(iwidth);
46929         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46930         this.linode.setWidth(width);
46931     },
46932 */
46933     /**
46934      * Show or hide the tab
46935      * @param {Boolean} hidden True to hide or false to show.
46936      */
46937     setHidden : function(hidden){
46938         this.hidden = hidden;
46939         this.linode.setStyle("display", hidden ? "none" : "");
46940     },
46941
46942     /**
46943      * Returns true if this tab is "hidden"
46944      * @return {Boolean}
46945      */
46946     isHidden : function(){
46947         return this.hidden;
46948     },
46949
46950     /**
46951      * Returns the text for this tab
46952      * @return {String}
46953      */
46954     getText : function(){
46955         return this.text;
46956     },
46957     /*
46958     autoSize : function(){
46959         //this.el.beginMeasure();
46960         this.textEl.setWidth(1);
46961         /*
46962          *  #2804 [new] Tabs in Roojs
46963          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46964          */
46965         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46966         //this.el.endMeasure();
46967     //},
46968
46969     /**
46970      * Sets the text for the tab (Note: this also sets the tooltip text)
46971      * @param {String} text The tab's text and tooltip
46972      */
46973     setText : function(text){
46974         this.text = text;
46975         this.textEl.update(text);
46976         this.setTooltip(text);
46977         //if(!this.tabPanel.resizeTabs){
46978         //    this.autoSize();
46979         //}
46980     },
46981     /**
46982      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46983      */
46984     activate : function(){
46985         this.tabPanel.activate(this.id);
46986     },
46987
46988     /**
46989      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46990      */
46991     disable : function(){
46992         if(this.tabPanel.active != this){
46993             this.disabled = true;
46994             this.status_node.addClass("disabled");
46995         }
46996     },
46997
46998     /**
46999      * Enables this TabPanelItem if it was previously disabled.
47000      */
47001     enable : function(){
47002         this.disabled = false;
47003         this.status_node.removeClass("disabled");
47004     },
47005
47006     /**
47007      * Sets the content for this TabPanelItem.
47008      * @param {String} content The content
47009      * @param {Boolean} loadScripts true to look for and load scripts
47010      */
47011     setContent : function(content, loadScripts){
47012         this.bodyEl.update(content, loadScripts);
47013     },
47014
47015     /**
47016      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47017      * @return {Roo.UpdateManager} The UpdateManager
47018      */
47019     getUpdateManager : function(){
47020         return this.bodyEl.getUpdateManager();
47021     },
47022
47023     /**
47024      * Set a URL to be used to load the content for this TabPanelItem.
47025      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47026      * @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)
47027      * @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)
47028      * @return {Roo.UpdateManager} The UpdateManager
47029      */
47030     setUrl : function(url, params, loadOnce){
47031         if(this.refreshDelegate){
47032             this.un('activate', this.refreshDelegate);
47033         }
47034         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47035         this.on("activate", this.refreshDelegate);
47036         return this.bodyEl.getUpdateManager();
47037     },
47038
47039     /** @private */
47040     _handleRefresh : function(url, params, loadOnce){
47041         if(!loadOnce || !this.loaded){
47042             var updater = this.bodyEl.getUpdateManager();
47043             updater.update(url, params, this._setLoaded.createDelegate(this));
47044         }
47045     },
47046
47047     /**
47048      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47049      *   Will fail silently if the setUrl method has not been called.
47050      *   This does not activate the panel, just updates its content.
47051      */
47052     refresh : function(){
47053         if(this.refreshDelegate){
47054            this.loaded = false;
47055            this.refreshDelegate();
47056         }
47057     },
47058
47059     /** @private */
47060     _setLoaded : function(){
47061         this.loaded = true;
47062     },
47063
47064     /** @private */
47065     closeClick : function(e){
47066         var o = {};
47067         e.stopEvent();
47068         this.fireEvent("beforeclose", this, o);
47069         if(o.cancel !== true){
47070             this.tabPanel.removeTab(this.id);
47071         }
47072     },
47073     /**
47074      * The text displayed in the tooltip for the close icon.
47075      * @type String
47076      */
47077     closeText : "Close this tab"
47078 });
47079 /**
47080 *    This script refer to:
47081 *    Title: International Telephone Input
47082 *    Author: Jack O'Connor
47083 *    Code version:  v12.1.12
47084 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47085 **/
47086
47087 Roo.bootstrap.form.PhoneInputData = function() {
47088     var d = [
47089       [
47090         "Afghanistan (‫افغانستان‬‎)",
47091         "af",
47092         "93"
47093       ],
47094       [
47095         "Albania (Shqipëri)",
47096         "al",
47097         "355"
47098       ],
47099       [
47100         "Algeria (‫الجزائر‬‎)",
47101         "dz",
47102         "213"
47103       ],
47104       [
47105         "American Samoa",
47106         "as",
47107         "1684"
47108       ],
47109       [
47110         "Andorra",
47111         "ad",
47112         "376"
47113       ],
47114       [
47115         "Angola",
47116         "ao",
47117         "244"
47118       ],
47119       [
47120         "Anguilla",
47121         "ai",
47122         "1264"
47123       ],
47124       [
47125         "Antigua and Barbuda",
47126         "ag",
47127         "1268"
47128       ],
47129       [
47130         "Argentina",
47131         "ar",
47132         "54"
47133       ],
47134       [
47135         "Armenia (Հայաստան)",
47136         "am",
47137         "374"
47138       ],
47139       [
47140         "Aruba",
47141         "aw",
47142         "297"
47143       ],
47144       [
47145         "Australia",
47146         "au",
47147         "61",
47148         0
47149       ],
47150       [
47151         "Austria (Österreich)",
47152         "at",
47153         "43"
47154       ],
47155       [
47156         "Azerbaijan (Azərbaycan)",
47157         "az",
47158         "994"
47159       ],
47160       [
47161         "Bahamas",
47162         "bs",
47163         "1242"
47164       ],
47165       [
47166         "Bahrain (‫البحرين‬‎)",
47167         "bh",
47168         "973"
47169       ],
47170       [
47171         "Bangladesh (বাংলাদেশ)",
47172         "bd",
47173         "880"
47174       ],
47175       [
47176         "Barbados",
47177         "bb",
47178         "1246"
47179       ],
47180       [
47181         "Belarus (Беларусь)",
47182         "by",
47183         "375"
47184       ],
47185       [
47186         "Belgium (België)",
47187         "be",
47188         "32"
47189       ],
47190       [
47191         "Belize",
47192         "bz",
47193         "501"
47194       ],
47195       [
47196         "Benin (Bénin)",
47197         "bj",
47198         "229"
47199       ],
47200       [
47201         "Bermuda",
47202         "bm",
47203         "1441"
47204       ],
47205       [
47206         "Bhutan (འབྲུག)",
47207         "bt",
47208         "975"
47209       ],
47210       [
47211         "Bolivia",
47212         "bo",
47213         "591"
47214       ],
47215       [
47216         "Bosnia and Herzegovina (Босна и Херцеговина)",
47217         "ba",
47218         "387"
47219       ],
47220       [
47221         "Botswana",
47222         "bw",
47223         "267"
47224       ],
47225       [
47226         "Brazil (Brasil)",
47227         "br",
47228         "55"
47229       ],
47230       [
47231         "British Indian Ocean Territory",
47232         "io",
47233         "246"
47234       ],
47235       [
47236         "British Virgin Islands",
47237         "vg",
47238         "1284"
47239       ],
47240       [
47241         "Brunei",
47242         "bn",
47243         "673"
47244       ],
47245       [
47246         "Bulgaria (България)",
47247         "bg",
47248         "359"
47249       ],
47250       [
47251         "Burkina Faso",
47252         "bf",
47253         "226"
47254       ],
47255       [
47256         "Burundi (Uburundi)",
47257         "bi",
47258         "257"
47259       ],
47260       [
47261         "Cambodia (កម្ពុជា)",
47262         "kh",
47263         "855"
47264       ],
47265       [
47266         "Cameroon (Cameroun)",
47267         "cm",
47268         "237"
47269       ],
47270       [
47271         "Canada",
47272         "ca",
47273         "1",
47274         1,
47275         ["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"]
47276       ],
47277       [
47278         "Cape Verde (Kabu Verdi)",
47279         "cv",
47280         "238"
47281       ],
47282       [
47283         "Caribbean Netherlands",
47284         "bq",
47285         "599",
47286         1
47287       ],
47288       [
47289         "Cayman Islands",
47290         "ky",
47291         "1345"
47292       ],
47293       [
47294         "Central African Republic (République centrafricaine)",
47295         "cf",
47296         "236"
47297       ],
47298       [
47299         "Chad (Tchad)",
47300         "td",
47301         "235"
47302       ],
47303       [
47304         "Chile",
47305         "cl",
47306         "56"
47307       ],
47308       [
47309         "China (中国)",
47310         "cn",
47311         "86"
47312       ],
47313       [
47314         "Christmas Island",
47315         "cx",
47316         "61",
47317         2
47318       ],
47319       [
47320         "Cocos (Keeling) Islands",
47321         "cc",
47322         "61",
47323         1
47324       ],
47325       [
47326         "Colombia",
47327         "co",
47328         "57"
47329       ],
47330       [
47331         "Comoros (‫جزر القمر‬‎)",
47332         "km",
47333         "269"
47334       ],
47335       [
47336         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47337         "cd",
47338         "243"
47339       ],
47340       [
47341         "Congo (Republic) (Congo-Brazzaville)",
47342         "cg",
47343         "242"
47344       ],
47345       [
47346         "Cook Islands",
47347         "ck",
47348         "682"
47349       ],
47350       [
47351         "Costa Rica",
47352         "cr",
47353         "506"
47354       ],
47355       [
47356         "Côte d’Ivoire",
47357         "ci",
47358         "225"
47359       ],
47360       [
47361         "Croatia (Hrvatska)",
47362         "hr",
47363         "385"
47364       ],
47365       [
47366         "Cuba",
47367         "cu",
47368         "53"
47369       ],
47370       [
47371         "Curaçao",
47372         "cw",
47373         "599",
47374         0
47375       ],
47376       [
47377         "Cyprus (Κύπρος)",
47378         "cy",
47379         "357"
47380       ],
47381       [
47382         "Czech Republic (Česká republika)",
47383         "cz",
47384         "420"
47385       ],
47386       [
47387         "Denmark (Danmark)",
47388         "dk",
47389         "45"
47390       ],
47391       [
47392         "Djibouti",
47393         "dj",
47394         "253"
47395       ],
47396       [
47397         "Dominica",
47398         "dm",
47399         "1767"
47400       ],
47401       [
47402         "Dominican Republic (República Dominicana)",
47403         "do",
47404         "1",
47405         2,
47406         ["809", "829", "849"]
47407       ],
47408       [
47409         "Ecuador",
47410         "ec",
47411         "593"
47412       ],
47413       [
47414         "Egypt (‫مصر‬‎)",
47415         "eg",
47416         "20"
47417       ],
47418       [
47419         "El Salvador",
47420         "sv",
47421         "503"
47422       ],
47423       [
47424         "Equatorial Guinea (Guinea Ecuatorial)",
47425         "gq",
47426         "240"
47427       ],
47428       [
47429         "Eritrea",
47430         "er",
47431         "291"
47432       ],
47433       [
47434         "Estonia (Eesti)",
47435         "ee",
47436         "372"
47437       ],
47438       [
47439         "Ethiopia",
47440         "et",
47441         "251"
47442       ],
47443       [
47444         "Falkland Islands (Islas Malvinas)",
47445         "fk",
47446         "500"
47447       ],
47448       [
47449         "Faroe Islands (Føroyar)",
47450         "fo",
47451         "298"
47452       ],
47453       [
47454         "Fiji",
47455         "fj",
47456         "679"
47457       ],
47458       [
47459         "Finland (Suomi)",
47460         "fi",
47461         "358",
47462         0
47463       ],
47464       [
47465         "France",
47466         "fr",
47467         "33"
47468       ],
47469       [
47470         "French Guiana (Guyane française)",
47471         "gf",
47472         "594"
47473       ],
47474       [
47475         "French Polynesia (Polynésie française)",
47476         "pf",
47477         "689"
47478       ],
47479       [
47480         "Gabon",
47481         "ga",
47482         "241"
47483       ],
47484       [
47485         "Gambia",
47486         "gm",
47487         "220"
47488       ],
47489       [
47490         "Georgia (საქართველო)",
47491         "ge",
47492         "995"
47493       ],
47494       [
47495         "Germany (Deutschland)",
47496         "de",
47497         "49"
47498       ],
47499       [
47500         "Ghana (Gaana)",
47501         "gh",
47502         "233"
47503       ],
47504       [
47505         "Gibraltar",
47506         "gi",
47507         "350"
47508       ],
47509       [
47510         "Greece (Ελλάδα)",
47511         "gr",
47512         "30"
47513       ],
47514       [
47515         "Greenland (Kalaallit Nunaat)",
47516         "gl",
47517         "299"
47518       ],
47519       [
47520         "Grenada",
47521         "gd",
47522         "1473"
47523       ],
47524       [
47525         "Guadeloupe",
47526         "gp",
47527         "590",
47528         0
47529       ],
47530       [
47531         "Guam",
47532         "gu",
47533         "1671"
47534       ],
47535       [
47536         "Guatemala",
47537         "gt",
47538         "502"
47539       ],
47540       [
47541         "Guernsey",
47542         "gg",
47543         "44",
47544         1
47545       ],
47546       [
47547         "Guinea (Guinée)",
47548         "gn",
47549         "224"
47550       ],
47551       [
47552         "Guinea-Bissau (Guiné Bissau)",
47553         "gw",
47554         "245"
47555       ],
47556       [
47557         "Guyana",
47558         "gy",
47559         "592"
47560       ],
47561       [
47562         "Haiti",
47563         "ht",
47564         "509"
47565       ],
47566       [
47567         "Honduras",
47568         "hn",
47569         "504"
47570       ],
47571       [
47572         "Hong Kong (香港)",
47573         "hk",
47574         "852"
47575       ],
47576       [
47577         "Hungary (Magyarország)",
47578         "hu",
47579         "36"
47580       ],
47581       [
47582         "Iceland (Ísland)",
47583         "is",
47584         "354"
47585       ],
47586       [
47587         "India (भारत)",
47588         "in",
47589         "91"
47590       ],
47591       [
47592         "Indonesia",
47593         "id",
47594         "62"
47595       ],
47596       [
47597         "Iran (‫ایران‬‎)",
47598         "ir",
47599         "98"
47600       ],
47601       [
47602         "Iraq (‫العراق‬‎)",
47603         "iq",
47604         "964"
47605       ],
47606       [
47607         "Ireland",
47608         "ie",
47609         "353"
47610       ],
47611       [
47612         "Isle of Man",
47613         "im",
47614         "44",
47615         2
47616       ],
47617       [
47618         "Israel (‫ישראל‬‎)",
47619         "il",
47620         "972"
47621       ],
47622       [
47623         "Italy (Italia)",
47624         "it",
47625         "39",
47626         0
47627       ],
47628       [
47629         "Jamaica",
47630         "jm",
47631         "1876"
47632       ],
47633       [
47634         "Japan (日本)",
47635         "jp",
47636         "81"
47637       ],
47638       [
47639         "Jersey",
47640         "je",
47641         "44",
47642         3
47643       ],
47644       [
47645         "Jordan (‫الأردن‬‎)",
47646         "jo",
47647         "962"
47648       ],
47649       [
47650         "Kazakhstan (Казахстан)",
47651         "kz",
47652         "7",
47653         1
47654       ],
47655       [
47656         "Kenya",
47657         "ke",
47658         "254"
47659       ],
47660       [
47661         "Kiribati",
47662         "ki",
47663         "686"
47664       ],
47665       [
47666         "Kosovo",
47667         "xk",
47668         "383"
47669       ],
47670       [
47671         "Kuwait (‫الكويت‬‎)",
47672         "kw",
47673         "965"
47674       ],
47675       [
47676         "Kyrgyzstan (Кыргызстан)",
47677         "kg",
47678         "996"
47679       ],
47680       [
47681         "Laos (ລາວ)",
47682         "la",
47683         "856"
47684       ],
47685       [
47686         "Latvia (Latvija)",
47687         "lv",
47688         "371"
47689       ],
47690       [
47691         "Lebanon (‫لبنان‬‎)",
47692         "lb",
47693         "961"
47694       ],
47695       [
47696         "Lesotho",
47697         "ls",
47698         "266"
47699       ],
47700       [
47701         "Liberia",
47702         "lr",
47703         "231"
47704       ],
47705       [
47706         "Libya (‫ليبيا‬‎)",
47707         "ly",
47708         "218"
47709       ],
47710       [
47711         "Liechtenstein",
47712         "li",
47713         "423"
47714       ],
47715       [
47716         "Lithuania (Lietuva)",
47717         "lt",
47718         "370"
47719       ],
47720       [
47721         "Luxembourg",
47722         "lu",
47723         "352"
47724       ],
47725       [
47726         "Macau (澳門)",
47727         "mo",
47728         "853"
47729       ],
47730       [
47731         "Macedonia (FYROM) (Македонија)",
47732         "mk",
47733         "389"
47734       ],
47735       [
47736         "Madagascar (Madagasikara)",
47737         "mg",
47738         "261"
47739       ],
47740       [
47741         "Malawi",
47742         "mw",
47743         "265"
47744       ],
47745       [
47746         "Malaysia",
47747         "my",
47748         "60"
47749       ],
47750       [
47751         "Maldives",
47752         "mv",
47753         "960"
47754       ],
47755       [
47756         "Mali",
47757         "ml",
47758         "223"
47759       ],
47760       [
47761         "Malta",
47762         "mt",
47763         "356"
47764       ],
47765       [
47766         "Marshall Islands",
47767         "mh",
47768         "692"
47769       ],
47770       [
47771         "Martinique",
47772         "mq",
47773         "596"
47774       ],
47775       [
47776         "Mauritania (‫موريتانيا‬‎)",
47777         "mr",
47778         "222"
47779       ],
47780       [
47781         "Mauritius (Moris)",
47782         "mu",
47783         "230"
47784       ],
47785       [
47786         "Mayotte",
47787         "yt",
47788         "262",
47789         1
47790       ],
47791       [
47792         "Mexico (México)",
47793         "mx",
47794         "52"
47795       ],
47796       [
47797         "Micronesia",
47798         "fm",
47799         "691"
47800       ],
47801       [
47802         "Moldova (Republica Moldova)",
47803         "md",
47804         "373"
47805       ],
47806       [
47807         "Monaco",
47808         "mc",
47809         "377"
47810       ],
47811       [
47812         "Mongolia (Монгол)",
47813         "mn",
47814         "976"
47815       ],
47816       [
47817         "Montenegro (Crna Gora)",
47818         "me",
47819         "382"
47820       ],
47821       [
47822         "Montserrat",
47823         "ms",
47824         "1664"
47825       ],
47826       [
47827         "Morocco (‫المغرب‬‎)",
47828         "ma",
47829         "212",
47830         0
47831       ],
47832       [
47833         "Mozambique (Moçambique)",
47834         "mz",
47835         "258"
47836       ],
47837       [
47838         "Myanmar (Burma) (မြန်မာ)",
47839         "mm",
47840         "95"
47841       ],
47842       [
47843         "Namibia (Namibië)",
47844         "na",
47845         "264"
47846       ],
47847       [
47848         "Nauru",
47849         "nr",
47850         "674"
47851       ],
47852       [
47853         "Nepal (नेपाल)",
47854         "np",
47855         "977"
47856       ],
47857       [
47858         "Netherlands (Nederland)",
47859         "nl",
47860         "31"
47861       ],
47862       [
47863         "New Caledonia (Nouvelle-Calédonie)",
47864         "nc",
47865         "687"
47866       ],
47867       [
47868         "New Zealand",
47869         "nz",
47870         "64"
47871       ],
47872       [
47873         "Nicaragua",
47874         "ni",
47875         "505"
47876       ],
47877       [
47878         "Niger (Nijar)",
47879         "ne",
47880         "227"
47881       ],
47882       [
47883         "Nigeria",
47884         "ng",
47885         "234"
47886       ],
47887       [
47888         "Niue",
47889         "nu",
47890         "683"
47891       ],
47892       [
47893         "Norfolk Island",
47894         "nf",
47895         "672"
47896       ],
47897       [
47898         "North Korea (조선 민주주의 인민 공화국)",
47899         "kp",
47900         "850"
47901       ],
47902       [
47903         "Northern Mariana Islands",
47904         "mp",
47905         "1670"
47906       ],
47907       [
47908         "Norway (Norge)",
47909         "no",
47910         "47",
47911         0
47912       ],
47913       [
47914         "Oman (‫عُمان‬‎)",
47915         "om",
47916         "968"
47917       ],
47918       [
47919         "Pakistan (‫پاکستان‬‎)",
47920         "pk",
47921         "92"
47922       ],
47923       [
47924         "Palau",
47925         "pw",
47926         "680"
47927       ],
47928       [
47929         "Palestine (‫فلسطين‬‎)",
47930         "ps",
47931         "970"
47932       ],
47933       [
47934         "Panama (Panamá)",
47935         "pa",
47936         "507"
47937       ],
47938       [
47939         "Papua New Guinea",
47940         "pg",
47941         "675"
47942       ],
47943       [
47944         "Paraguay",
47945         "py",
47946         "595"
47947       ],
47948       [
47949         "Peru (Perú)",
47950         "pe",
47951         "51"
47952       ],
47953       [
47954         "Philippines",
47955         "ph",
47956         "63"
47957       ],
47958       [
47959         "Poland (Polska)",
47960         "pl",
47961         "48"
47962       ],
47963       [
47964         "Portugal",
47965         "pt",
47966         "351"
47967       ],
47968       [
47969         "Puerto Rico",
47970         "pr",
47971         "1",
47972         3,
47973         ["787", "939"]
47974       ],
47975       [
47976         "Qatar (‫قطر‬‎)",
47977         "qa",
47978         "974"
47979       ],
47980       [
47981         "Réunion (La Réunion)",
47982         "re",
47983         "262",
47984         0
47985       ],
47986       [
47987         "Romania (România)",
47988         "ro",
47989         "40"
47990       ],
47991       [
47992         "Russia (Россия)",
47993         "ru",
47994         "7",
47995         0
47996       ],
47997       [
47998         "Rwanda",
47999         "rw",
48000         "250"
48001       ],
48002       [
48003         "Saint Barthélemy",
48004         "bl",
48005         "590",
48006         1
48007       ],
48008       [
48009         "Saint Helena",
48010         "sh",
48011         "290"
48012       ],
48013       [
48014         "Saint Kitts and Nevis",
48015         "kn",
48016         "1869"
48017       ],
48018       [
48019         "Saint Lucia",
48020         "lc",
48021         "1758"
48022       ],
48023       [
48024         "Saint Martin (Saint-Martin (partie française))",
48025         "mf",
48026         "590",
48027         2
48028       ],
48029       [
48030         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48031         "pm",
48032         "508"
48033       ],
48034       [
48035         "Saint Vincent and the Grenadines",
48036         "vc",
48037         "1784"
48038       ],
48039       [
48040         "Samoa",
48041         "ws",
48042         "685"
48043       ],
48044       [
48045         "San Marino",
48046         "sm",
48047         "378"
48048       ],
48049       [
48050         "São Tomé and Príncipe (São Tomé e Príncipe)",
48051         "st",
48052         "239"
48053       ],
48054       [
48055         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48056         "sa",
48057         "966"
48058       ],
48059       [
48060         "Senegal (Sénégal)",
48061         "sn",
48062         "221"
48063       ],
48064       [
48065         "Serbia (Србија)",
48066         "rs",
48067         "381"
48068       ],
48069       [
48070         "Seychelles",
48071         "sc",
48072         "248"
48073       ],
48074       [
48075         "Sierra Leone",
48076         "sl",
48077         "232"
48078       ],
48079       [
48080         "Singapore",
48081         "sg",
48082         "65"
48083       ],
48084       [
48085         "Sint Maarten",
48086         "sx",
48087         "1721"
48088       ],
48089       [
48090         "Slovakia (Slovensko)",
48091         "sk",
48092         "421"
48093       ],
48094       [
48095         "Slovenia (Slovenija)",
48096         "si",
48097         "386"
48098       ],
48099       [
48100         "Solomon Islands",
48101         "sb",
48102         "677"
48103       ],
48104       [
48105         "Somalia (Soomaaliya)",
48106         "so",
48107         "252"
48108       ],
48109       [
48110         "South Africa",
48111         "za",
48112         "27"
48113       ],
48114       [
48115         "South Korea (대한민국)",
48116         "kr",
48117         "82"
48118       ],
48119       [
48120         "South Sudan (‫جنوب السودان‬‎)",
48121         "ss",
48122         "211"
48123       ],
48124       [
48125         "Spain (España)",
48126         "es",
48127         "34"
48128       ],
48129       [
48130         "Sri Lanka (ශ්‍රී ලංකාව)",
48131         "lk",
48132         "94"
48133       ],
48134       [
48135         "Sudan (‫السودان‬‎)",
48136         "sd",
48137         "249"
48138       ],
48139       [
48140         "Suriname",
48141         "sr",
48142         "597"
48143       ],
48144       [
48145         "Svalbard and Jan Mayen",
48146         "sj",
48147         "47",
48148         1
48149       ],
48150       [
48151         "Swaziland",
48152         "sz",
48153         "268"
48154       ],
48155       [
48156         "Sweden (Sverige)",
48157         "se",
48158         "46"
48159       ],
48160       [
48161         "Switzerland (Schweiz)",
48162         "ch",
48163         "41"
48164       ],
48165       [
48166         "Syria (‫سوريا‬‎)",
48167         "sy",
48168         "963"
48169       ],
48170       [
48171         "Taiwan (台灣)",
48172         "tw",
48173         "886"
48174       ],
48175       [
48176         "Tajikistan",
48177         "tj",
48178         "992"
48179       ],
48180       [
48181         "Tanzania",
48182         "tz",
48183         "255"
48184       ],
48185       [
48186         "Thailand (ไทย)",
48187         "th",
48188         "66"
48189       ],
48190       [
48191         "Timor-Leste",
48192         "tl",
48193         "670"
48194       ],
48195       [
48196         "Togo",
48197         "tg",
48198         "228"
48199       ],
48200       [
48201         "Tokelau",
48202         "tk",
48203         "690"
48204       ],
48205       [
48206         "Tonga",
48207         "to",
48208         "676"
48209       ],
48210       [
48211         "Trinidad and Tobago",
48212         "tt",
48213         "1868"
48214       ],
48215       [
48216         "Tunisia (‫تونس‬‎)",
48217         "tn",
48218         "216"
48219       ],
48220       [
48221         "Turkey (Türkiye)",
48222         "tr",
48223         "90"
48224       ],
48225       [
48226         "Turkmenistan",
48227         "tm",
48228         "993"
48229       ],
48230       [
48231         "Turks and Caicos Islands",
48232         "tc",
48233         "1649"
48234       ],
48235       [
48236         "Tuvalu",
48237         "tv",
48238         "688"
48239       ],
48240       [
48241         "U.S. Virgin Islands",
48242         "vi",
48243         "1340"
48244       ],
48245       [
48246         "Uganda",
48247         "ug",
48248         "256"
48249       ],
48250       [
48251         "Ukraine (Україна)",
48252         "ua",
48253         "380"
48254       ],
48255       [
48256         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48257         "ae",
48258         "971"
48259       ],
48260       [
48261         "United Kingdom",
48262         "gb",
48263         "44",
48264         0
48265       ],
48266       [
48267         "United States",
48268         "us",
48269         "1",
48270         0
48271       ],
48272       [
48273         "Uruguay",
48274         "uy",
48275         "598"
48276       ],
48277       [
48278         "Uzbekistan (Oʻzbekiston)",
48279         "uz",
48280         "998"
48281       ],
48282       [
48283         "Vanuatu",
48284         "vu",
48285         "678"
48286       ],
48287       [
48288         "Vatican City (Città del Vaticano)",
48289         "va",
48290         "39",
48291         1
48292       ],
48293       [
48294         "Venezuela",
48295         "ve",
48296         "58"
48297       ],
48298       [
48299         "Vietnam (Việt Nam)",
48300         "vn",
48301         "84"
48302       ],
48303       [
48304         "Wallis and Futuna (Wallis-et-Futuna)",
48305         "wf",
48306         "681"
48307       ],
48308       [
48309         "Western Sahara (‫الصحراء الغربية‬‎)",
48310         "eh",
48311         "212",
48312         1
48313       ],
48314       [
48315         "Yemen (‫اليمن‬‎)",
48316         "ye",
48317         "967"
48318       ],
48319       [
48320         "Zambia",
48321         "zm",
48322         "260"
48323       ],
48324       [
48325         "Zimbabwe",
48326         "zw",
48327         "263"
48328       ],
48329       [
48330         "Åland Islands",
48331         "ax",
48332         "358",
48333         1
48334       ]
48335   ];
48336   
48337   return d;
48338 }/**
48339 *    This script refer to:
48340 *    Title: International Telephone Input
48341 *    Author: Jack O'Connor
48342 *    Code version:  v12.1.12
48343 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48344 **/
48345
48346 /**
48347  * @class Roo.bootstrap.form.PhoneInput
48348  * @extends Roo.bootstrap.form.TriggerField
48349  * An input with International dial-code selection
48350  
48351  * @cfg {String} defaultDialCode default '+852'
48352  * @cfg {Array} preferedCountries default []
48353   
48354  * @constructor
48355  * Create a new PhoneInput.
48356  * @param {Object} config Configuration options
48357  */
48358
48359 Roo.bootstrap.form.PhoneInput = function(config) {
48360     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48361 };
48362
48363 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48364         /**
48365         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48366         */
48367         listWidth: undefined,
48368         
48369         selectedClass: 'active',
48370         
48371         invalidClass : "has-warning",
48372         
48373         validClass: 'has-success',
48374         
48375         allowed: '0123456789',
48376         
48377         max_length: 15,
48378         
48379         /**
48380          * @cfg {String} defaultDialCode The default dial code when initializing the input
48381          */
48382         defaultDialCode: '+852',
48383         
48384         /**
48385          * @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
48386          */
48387         preferedCountries: false,
48388         
48389         getAutoCreate : function()
48390         {
48391             var data = Roo.bootstrap.form.PhoneInputData();
48392             var align = this.labelAlign || this.parentLabelAlign();
48393             var id = Roo.id();
48394             
48395             this.allCountries = [];
48396             this.dialCodeMapping = [];
48397             
48398             for (var i = 0; i < data.length; i++) {
48399               var c = data[i];
48400               this.allCountries[i] = {
48401                 name: c[0],
48402                 iso2: c[1],
48403                 dialCode: c[2],
48404                 priority: c[3] || 0,
48405                 areaCodes: c[4] || null
48406               };
48407               this.dialCodeMapping[c[2]] = {
48408                   name: c[0],
48409                   iso2: c[1],
48410                   priority: c[3] || 0,
48411                   areaCodes: c[4] || null
48412               };
48413             }
48414             
48415             var cfg = {
48416                 cls: 'form-group',
48417                 cn: []
48418             };
48419             
48420             var input =  {
48421                 tag: 'input',
48422                 id : id,
48423                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48424                 maxlength: this.max_length,
48425                 cls : 'form-control tel-input',
48426                 autocomplete: 'new-password'
48427             };
48428             
48429             var hiddenInput = {
48430                 tag: 'input',
48431                 type: 'hidden',
48432                 cls: 'hidden-tel-input'
48433             };
48434             
48435             if (this.name) {
48436                 hiddenInput.name = this.name;
48437             }
48438             
48439             if (this.disabled) {
48440                 input.disabled = true;
48441             }
48442             
48443             var flag_container = {
48444                 tag: 'div',
48445                 cls: 'flag-box',
48446                 cn: [
48447                     {
48448                         tag: 'div',
48449                         cls: 'flag'
48450                     },
48451                     {
48452                         tag: 'div',
48453                         cls: 'caret'
48454                     }
48455                 ]
48456             };
48457             
48458             var box = {
48459                 tag: 'div',
48460                 cls: this.hasFeedback ? 'has-feedback' : '',
48461                 cn: [
48462                     hiddenInput,
48463                     input,
48464                     {
48465                         tag: 'input',
48466                         cls: 'dial-code-holder',
48467                         disabled: true
48468                     }
48469                 ]
48470             };
48471             
48472             var container = {
48473                 cls: 'roo-select2-container input-group',
48474                 cn: [
48475                     flag_container,
48476                     box
48477                 ]
48478             };
48479             
48480             if (this.fieldLabel.length) {
48481                 var indicator = {
48482                     tag: 'i',
48483                     tooltip: 'This field is required'
48484                 };
48485                 
48486                 var label = {
48487                     tag: 'label',
48488                     'for':  id,
48489                     cls: 'control-label',
48490                     cn: []
48491                 };
48492                 
48493                 var label_text = {
48494                     tag: 'span',
48495                     html: this.fieldLabel
48496                 };
48497                 
48498                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48499                 label.cn = [
48500                     indicator,
48501                     label_text
48502                 ];
48503                 
48504                 if(this.indicatorpos == 'right') {
48505                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48506                     label.cn = [
48507                         label_text,
48508                         indicator
48509                     ];
48510                 }
48511                 
48512                 if(align == 'left') {
48513                     container = {
48514                         tag: 'div',
48515                         cn: [
48516                             container
48517                         ]
48518                     };
48519                     
48520                     if(this.labelWidth > 12){
48521                         label.style = "width: " + this.labelWidth + 'px';
48522                     }
48523                     if(this.labelWidth < 13 && this.labelmd == 0){
48524                         this.labelmd = this.labelWidth;
48525                     }
48526                     if(this.labellg > 0){
48527                         label.cls += ' col-lg-' + this.labellg;
48528                         input.cls += ' col-lg-' + (12 - this.labellg);
48529                     }
48530                     if(this.labelmd > 0){
48531                         label.cls += ' col-md-' + this.labelmd;
48532                         container.cls += ' col-md-' + (12 - this.labelmd);
48533                     }
48534                     if(this.labelsm > 0){
48535                         label.cls += ' col-sm-' + this.labelsm;
48536                         container.cls += ' col-sm-' + (12 - this.labelsm);
48537                     }
48538                     if(this.labelxs > 0){
48539                         label.cls += ' col-xs-' + this.labelxs;
48540                         container.cls += ' col-xs-' + (12 - this.labelxs);
48541                     }
48542                 }
48543             }
48544             
48545             cfg.cn = [
48546                 label,
48547                 container
48548             ];
48549             
48550             var settings = this;
48551             
48552             ['xs','sm','md','lg'].map(function(size){
48553                 if (settings[size]) {
48554                     cfg.cls += ' col-' + size + '-' + settings[size];
48555                 }
48556             });
48557             
48558             this.store = new Roo.data.Store({
48559                 proxy : new Roo.data.MemoryProxy({}),
48560                 reader : new Roo.data.JsonReader({
48561                     fields : [
48562                         {
48563                             'name' : 'name',
48564                             'type' : 'string'
48565                         },
48566                         {
48567                             'name' : 'iso2',
48568                             'type' : 'string'
48569                         },
48570                         {
48571                             'name' : 'dialCode',
48572                             'type' : 'string'
48573                         },
48574                         {
48575                             'name' : 'priority',
48576                             'type' : 'string'
48577                         },
48578                         {
48579                             'name' : 'areaCodes',
48580                             'type' : 'string'
48581                         }
48582                     ]
48583                 })
48584             });
48585             
48586             if(!this.preferedCountries) {
48587                 this.preferedCountries = [
48588                     'hk',
48589                     'gb',
48590                     'us'
48591                 ];
48592             }
48593             
48594             var p = this.preferedCountries.reverse();
48595             
48596             if(p) {
48597                 for (var i = 0; i < p.length; i++) {
48598                     for (var j = 0; j < this.allCountries.length; j++) {
48599                         if(this.allCountries[j].iso2 == p[i]) {
48600                             var t = this.allCountries[j];
48601                             this.allCountries.splice(j,1);
48602                             this.allCountries.unshift(t);
48603                         }
48604                     } 
48605                 }
48606             }
48607             
48608             this.store.proxy.data = {
48609                 success: true,
48610                 data: this.allCountries
48611             };
48612             
48613             return cfg;
48614         },
48615         
48616         initEvents : function()
48617         {
48618             this.createList();
48619             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48620             
48621             this.indicator = this.indicatorEl();
48622             this.flag = this.flagEl();
48623             this.dialCodeHolder = this.dialCodeHolderEl();
48624             
48625             this.trigger = this.el.select('div.flag-box',true).first();
48626             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48627             
48628             var _this = this;
48629             
48630             (function(){
48631                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48632                 _this.list.setWidth(lw);
48633             }).defer(100);
48634             
48635             this.list.on('mouseover', this.onViewOver, this);
48636             this.list.on('mousemove', this.onViewMove, this);
48637             this.inputEl().on("keyup", this.onKeyUp, this);
48638             this.inputEl().on("keypress", this.onKeyPress, this);
48639             
48640             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48641
48642             this.view = new Roo.View(this.list, this.tpl, {
48643                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48644             });
48645             
48646             this.view.on('click', this.onViewClick, this);
48647             this.setValue(this.defaultDialCode);
48648         },
48649         
48650         onTriggerClick : function(e)
48651         {
48652             Roo.log('trigger click');
48653             if(this.disabled){
48654                 return;
48655             }
48656             
48657             if(this.isExpanded()){
48658                 this.collapse();
48659                 this.hasFocus = false;
48660             }else {
48661                 this.store.load({});
48662                 this.hasFocus = true;
48663                 this.expand();
48664             }
48665         },
48666         
48667         isExpanded : function()
48668         {
48669             return this.list.isVisible();
48670         },
48671         
48672         collapse : function()
48673         {
48674             if(!this.isExpanded()){
48675                 return;
48676             }
48677             this.list.hide();
48678             Roo.get(document).un('mousedown', this.collapseIf, this);
48679             Roo.get(document).un('mousewheel', this.collapseIf, this);
48680             this.fireEvent('collapse', this);
48681             this.validate();
48682         },
48683         
48684         expand : function()
48685         {
48686             Roo.log('expand');
48687
48688             if(this.isExpanded() || !this.hasFocus){
48689                 return;
48690             }
48691             
48692             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48693             this.list.setWidth(lw);
48694             
48695             this.list.show();
48696             this.restrictHeight();
48697             
48698             Roo.get(document).on('mousedown', this.collapseIf, this);
48699             Roo.get(document).on('mousewheel', this.collapseIf, this);
48700             
48701             this.fireEvent('expand', this);
48702         },
48703         
48704         restrictHeight : function()
48705         {
48706             this.list.alignTo(this.inputEl(), this.listAlign);
48707             this.list.alignTo(this.inputEl(), this.listAlign);
48708         },
48709         
48710         onViewOver : function(e, t)
48711         {
48712             if(this.inKeyMode){
48713                 return;
48714             }
48715             var item = this.view.findItemFromChild(t);
48716             
48717             if(item){
48718                 var index = this.view.indexOf(item);
48719                 this.select(index, false);
48720             }
48721         },
48722
48723         // private
48724         onViewClick : function(view, doFocus, el, e)
48725         {
48726             var index = this.view.getSelectedIndexes()[0];
48727             
48728             var r = this.store.getAt(index);
48729             
48730             if(r){
48731                 this.onSelect(r, index);
48732             }
48733             if(doFocus !== false && !this.blockFocus){
48734                 this.inputEl().focus();
48735             }
48736         },
48737         
48738         onViewMove : function(e, t)
48739         {
48740             this.inKeyMode = false;
48741         },
48742         
48743         select : function(index, scrollIntoView)
48744         {
48745             this.selectedIndex = index;
48746             this.view.select(index);
48747             if(scrollIntoView !== false){
48748                 var el = this.view.getNode(index);
48749                 if(el){
48750                     this.list.scrollChildIntoView(el, false);
48751                 }
48752             }
48753         },
48754         
48755         createList : function()
48756         {
48757             this.list = Roo.get(document.body).createChild({
48758                 tag: 'ul',
48759                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48760                 style: 'display:none'
48761             });
48762             
48763             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48764         },
48765         
48766         collapseIf : function(e)
48767         {
48768             var in_combo  = e.within(this.el);
48769             var in_list =  e.within(this.list);
48770             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48771             
48772             if (in_combo || in_list || is_list) {
48773                 return;
48774             }
48775             this.collapse();
48776         },
48777         
48778         onSelect : function(record, index)
48779         {
48780             if(this.fireEvent('beforeselect', this, record, index) !== false){
48781                 
48782                 this.setFlagClass(record.data.iso2);
48783                 this.setDialCode(record.data.dialCode);
48784                 this.hasFocus = false;
48785                 this.collapse();
48786                 this.fireEvent('select', this, record, index);
48787             }
48788         },
48789         
48790         flagEl : function()
48791         {
48792             var flag = this.el.select('div.flag',true).first();
48793             if(!flag){
48794                 return false;
48795             }
48796             return flag;
48797         },
48798         
48799         dialCodeHolderEl : function()
48800         {
48801             var d = this.el.select('input.dial-code-holder',true).first();
48802             if(!d){
48803                 return false;
48804             }
48805             return d;
48806         },
48807         
48808         setDialCode : function(v)
48809         {
48810             this.dialCodeHolder.dom.value = '+'+v;
48811         },
48812         
48813         setFlagClass : function(n)
48814         {
48815             this.flag.dom.className = 'flag '+n;
48816         },
48817         
48818         getValue : function()
48819         {
48820             var v = this.inputEl().getValue();
48821             if(this.dialCodeHolder) {
48822                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48823             }
48824             return v;
48825         },
48826         
48827         setValue : function(v)
48828         {
48829             var d = this.getDialCode(v);
48830             
48831             //invalid dial code
48832             if(v.length == 0 || !d || d.length == 0) {
48833                 if(this.rendered){
48834                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48835                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48836                 }
48837                 return;
48838             }
48839             
48840             //valid dial code
48841             this.setFlagClass(this.dialCodeMapping[d].iso2);
48842             this.setDialCode(d);
48843             this.inputEl().dom.value = v.replace('+'+d,'');
48844             this.hiddenEl().dom.value = this.getValue();
48845             
48846             this.validate();
48847         },
48848         
48849         getDialCode : function(v)
48850         {
48851             v = v ||  '';
48852             
48853             if (v.length == 0) {
48854                 return this.dialCodeHolder.dom.value;
48855             }
48856             
48857             var dialCode = "";
48858             if (v.charAt(0) != "+") {
48859                 return false;
48860             }
48861             var numericChars = "";
48862             for (var i = 1; i < v.length; i++) {
48863               var c = v.charAt(i);
48864               if (!isNaN(c)) {
48865                 numericChars += c;
48866                 if (this.dialCodeMapping[numericChars]) {
48867                   dialCode = v.substr(1, i);
48868                 }
48869                 if (numericChars.length == 4) {
48870                   break;
48871                 }
48872               }
48873             }
48874             return dialCode;
48875         },
48876         
48877         reset : function()
48878         {
48879             this.setValue(this.defaultDialCode);
48880             this.validate();
48881         },
48882         
48883         hiddenEl : function()
48884         {
48885             return this.el.select('input.hidden-tel-input',true).first();
48886         },
48887         
48888         // after setting val
48889         onKeyUp : function(e){
48890             this.setValue(this.getValue());
48891         },
48892         
48893         onKeyPress : function(e){
48894             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48895                 e.stopEvent();
48896             }
48897         }
48898         
48899 });
48900 /**
48901  * @class Roo.bootstrap.form.MoneyField
48902  * @extends Roo.bootstrap.form.ComboBox
48903  * Bootstrap MoneyField class
48904  * 
48905  * @constructor
48906  * Create a new MoneyField.
48907  * @param {Object} config Configuration options
48908  */
48909
48910 Roo.bootstrap.form.MoneyField = function(config) {
48911     
48912     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48913     
48914 };
48915
48916 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48917     
48918     /**
48919      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48920      */
48921     allowDecimals : true,
48922     /**
48923      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48924      */
48925     decimalSeparator : ".",
48926     /**
48927      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48928      */
48929     decimalPrecision : 0,
48930     /**
48931      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48932      */
48933     allowNegative : true,
48934     /**
48935      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48936      */
48937     allowZero: true,
48938     /**
48939      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48940      */
48941     minValue : Number.NEGATIVE_INFINITY,
48942     /**
48943      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48944      */
48945     maxValue : Number.MAX_VALUE,
48946     /**
48947      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48948      */
48949     minText : "The minimum value for this field is {0}",
48950     /**
48951      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48952      */
48953     maxText : "The maximum value for this field is {0}",
48954     /**
48955      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48956      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48957      */
48958     nanText : "{0} is not a valid number",
48959     /**
48960      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48961      */
48962     castInt : true,
48963     /**
48964      * @cfg {String} defaults currency of the MoneyField
48965      * value should be in lkey
48966      */
48967     defaultCurrency : false,
48968     /**
48969      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48970      */
48971     thousandsDelimiter : false,
48972     /**
48973      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48974      */
48975     max_length: false,
48976     
48977     inputlg : 9,
48978     inputmd : 9,
48979     inputsm : 9,
48980     inputxs : 6,
48981      /**
48982      * @cfg {Roo.data.Store} store  Store to lookup currency??
48983      */
48984     store : false,
48985     
48986     getAutoCreate : function()
48987     {
48988         var align = this.labelAlign || this.parentLabelAlign();
48989         
48990         var id = Roo.id();
48991
48992         var cfg = {
48993             cls: 'form-group',
48994             cn: []
48995         };
48996
48997         var input =  {
48998             tag: 'input',
48999             id : id,
49000             cls : 'form-control roo-money-amount-input',
49001             autocomplete: 'new-password'
49002         };
49003         
49004         var hiddenInput = {
49005             tag: 'input',
49006             type: 'hidden',
49007             id: Roo.id(),
49008             cls: 'hidden-number-input'
49009         };
49010         
49011         if(this.max_length) {
49012             input.maxlength = this.max_length; 
49013         }
49014         
49015         if (this.name) {
49016             hiddenInput.name = this.name;
49017         }
49018
49019         if (this.disabled) {
49020             input.disabled = true;
49021         }
49022
49023         var clg = 12 - this.inputlg;
49024         var cmd = 12 - this.inputmd;
49025         var csm = 12 - this.inputsm;
49026         var cxs = 12 - this.inputxs;
49027         
49028         var container = {
49029             tag : 'div',
49030             cls : 'row roo-money-field',
49031             cn : [
49032                 {
49033                     tag : 'div',
49034                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49035                     cn : [
49036                         {
49037                             tag : 'div',
49038                             cls: 'roo-select2-container input-group',
49039                             cn: [
49040                                 {
49041                                     tag : 'input',
49042                                     cls : 'form-control roo-money-currency-input',
49043                                     autocomplete: 'new-password',
49044                                     readOnly : 1,
49045                                     name : this.currencyName
49046                                 },
49047                                 {
49048                                     tag :'span',
49049                                     cls : 'input-group-addon',
49050                                     cn : [
49051                                         {
49052                                             tag: 'span',
49053                                             cls: 'caret'
49054                                         }
49055                                     ]
49056                                 }
49057                             ]
49058                         }
49059                     ]
49060                 },
49061                 {
49062                     tag : 'div',
49063                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49064                     cn : [
49065                         {
49066                             tag: 'div',
49067                             cls: this.hasFeedback ? 'has-feedback' : '',
49068                             cn: [
49069                                 input
49070                             ]
49071                         }
49072                     ]
49073                 }
49074             ]
49075             
49076         };
49077         
49078         if (this.fieldLabel.length) {
49079             var indicator = {
49080                 tag: 'i',
49081                 tooltip: 'This field is required'
49082             };
49083
49084             var label = {
49085                 tag: 'label',
49086                 'for':  id,
49087                 cls: 'control-label',
49088                 cn: []
49089             };
49090
49091             var label_text = {
49092                 tag: 'span',
49093                 html: this.fieldLabel
49094             };
49095
49096             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49097             label.cn = [
49098                 indicator,
49099                 label_text
49100             ];
49101
49102             if(this.indicatorpos == 'right') {
49103                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49104                 label.cn = [
49105                     label_text,
49106                     indicator
49107                 ];
49108             }
49109
49110             if(align == 'left') {
49111                 container = {
49112                     tag: 'div',
49113                     cn: [
49114                         container
49115                     ]
49116                 };
49117
49118                 if(this.labelWidth > 12){
49119                     label.style = "width: " + this.labelWidth + 'px';
49120                 }
49121                 if(this.labelWidth < 13 && this.labelmd == 0){
49122                     this.labelmd = this.labelWidth;
49123                 }
49124                 if(this.labellg > 0){
49125                     label.cls += ' col-lg-' + this.labellg;
49126                     input.cls += ' col-lg-' + (12 - this.labellg);
49127                 }
49128                 if(this.labelmd > 0){
49129                     label.cls += ' col-md-' + this.labelmd;
49130                     container.cls += ' col-md-' + (12 - this.labelmd);
49131                 }
49132                 if(this.labelsm > 0){
49133                     label.cls += ' col-sm-' + this.labelsm;
49134                     container.cls += ' col-sm-' + (12 - this.labelsm);
49135                 }
49136                 if(this.labelxs > 0){
49137                     label.cls += ' col-xs-' + this.labelxs;
49138                     container.cls += ' col-xs-' + (12 - this.labelxs);
49139                 }
49140             }
49141         }
49142
49143         cfg.cn = [
49144             label,
49145             container,
49146             hiddenInput
49147         ];
49148         
49149         var settings = this;
49150
49151         ['xs','sm','md','lg'].map(function(size){
49152             if (settings[size]) {
49153                 cfg.cls += ' col-' + size + '-' + settings[size];
49154             }
49155         });
49156         
49157         return cfg;
49158     },
49159     
49160     initEvents : function()
49161     {
49162         this.indicator = this.indicatorEl();
49163         
49164         this.initCurrencyEvent();
49165         
49166         this.initNumberEvent();
49167     },
49168     
49169     initCurrencyEvent : function()
49170     {
49171         if (!this.store) {
49172             throw "can not find store for combo";
49173         }
49174         
49175         this.store = Roo.factory(this.store, Roo.data);
49176         this.store.parent = this;
49177         
49178         this.createList();
49179         
49180         this.triggerEl = this.el.select('.input-group-addon', true).first();
49181         
49182         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49183         
49184         var _this = this;
49185         
49186         (function(){
49187             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49188             _this.list.setWidth(lw);
49189         }).defer(100);
49190         
49191         this.list.on('mouseover', this.onViewOver, this);
49192         this.list.on('mousemove', this.onViewMove, this);
49193         this.list.on('scroll', this.onViewScroll, this);
49194         
49195         if(!this.tpl){
49196             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49197         }
49198         
49199         this.view = new Roo.View(this.list, this.tpl, {
49200             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49201         });
49202         
49203         this.view.on('click', this.onViewClick, this);
49204         
49205         this.store.on('beforeload', this.onBeforeLoad, this);
49206         this.store.on('load', this.onLoad, this);
49207         this.store.on('loadexception', this.onLoadException, this);
49208         
49209         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49210             "up" : function(e){
49211                 this.inKeyMode = true;
49212                 this.selectPrev();
49213             },
49214
49215             "down" : function(e){
49216                 if(!this.isExpanded()){
49217                     this.onTriggerClick();
49218                 }else{
49219                     this.inKeyMode = true;
49220                     this.selectNext();
49221                 }
49222             },
49223
49224             "enter" : function(e){
49225                 this.collapse();
49226                 
49227                 if(this.fireEvent("specialkey", this, e)){
49228                     this.onViewClick(false);
49229                 }
49230                 
49231                 return true;
49232             },
49233
49234             "esc" : function(e){
49235                 this.collapse();
49236             },
49237
49238             "tab" : function(e){
49239                 this.collapse();
49240                 
49241                 if(this.fireEvent("specialkey", this, e)){
49242                     this.onViewClick(false);
49243                 }
49244                 
49245                 return true;
49246             },
49247
49248             scope : this,
49249
49250             doRelay : function(foo, bar, hname){
49251                 if(hname == 'down' || this.scope.isExpanded()){
49252                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49253                 }
49254                 return true;
49255             },
49256
49257             forceKeyDown: true
49258         });
49259         
49260         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49261         
49262     },
49263     
49264     initNumberEvent : function(e)
49265     {
49266         this.inputEl().on("keydown" , this.fireKey,  this);
49267         this.inputEl().on("focus", this.onFocus,  this);
49268         this.inputEl().on("blur", this.onBlur,  this);
49269         
49270         this.inputEl().relayEvent('keyup', this);
49271         
49272         if(this.indicator){
49273             this.indicator.addClass('invisible');
49274         }
49275  
49276         this.originalValue = this.getValue();
49277         
49278         if(this.validationEvent == 'keyup'){
49279             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49280             this.inputEl().on('keyup', this.filterValidation, this);
49281         }
49282         else if(this.validationEvent !== false){
49283             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49284         }
49285         
49286         if(this.selectOnFocus){
49287             this.on("focus", this.preFocus, this);
49288             
49289         }
49290         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49291             this.inputEl().on("keypress", this.filterKeys, this);
49292         } else {
49293             this.inputEl().relayEvent('keypress', this);
49294         }
49295         
49296         var allowed = "0123456789";
49297         
49298         if(this.allowDecimals){
49299             allowed += this.decimalSeparator;
49300         }
49301         
49302         if(this.allowNegative){
49303             allowed += "-";
49304         }
49305         
49306         if(this.thousandsDelimiter) {
49307             allowed += ",";
49308         }
49309         
49310         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49311         
49312         var keyPress = function(e){
49313             
49314             var k = e.getKey();
49315             
49316             var c = e.getCharCode();
49317             
49318             if(
49319                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49320                     allowed.indexOf(String.fromCharCode(c)) === -1
49321             ){
49322                 e.stopEvent();
49323                 return;
49324             }
49325             
49326             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49327                 return;
49328             }
49329             
49330             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49331                 e.stopEvent();
49332             }
49333         };
49334         
49335         this.inputEl().on("keypress", keyPress, this);
49336         
49337     },
49338     
49339     onTriggerClick : function(e)
49340     {   
49341         if(this.disabled){
49342             return;
49343         }
49344         
49345         this.page = 0;
49346         this.loadNext = false;
49347         
49348         if(this.isExpanded()){
49349             this.collapse();
49350             return;
49351         }
49352         
49353         this.hasFocus = true;
49354         
49355         if(this.triggerAction == 'all') {
49356             this.doQuery(this.allQuery, true);
49357             return;
49358         }
49359         
49360         this.doQuery(this.getRawValue());
49361     },
49362     
49363     getCurrency : function()
49364     {   
49365         var v = this.currencyEl().getValue();
49366         
49367         return v;
49368     },
49369     
49370     restrictHeight : function()
49371     {
49372         this.list.alignTo(this.currencyEl(), this.listAlign);
49373         this.list.alignTo(this.currencyEl(), this.listAlign);
49374     },
49375     
49376     onViewClick : function(view, doFocus, el, e)
49377     {
49378         var index = this.view.getSelectedIndexes()[0];
49379         
49380         var r = this.store.getAt(index);
49381         
49382         if(r){
49383             this.onSelect(r, index);
49384         }
49385     },
49386     
49387     onSelect : function(record, index){
49388         
49389         if(this.fireEvent('beforeselect', this, record, index) !== false){
49390         
49391             this.setFromCurrencyData(index > -1 ? record.data : false);
49392             
49393             this.collapse();
49394             
49395             this.fireEvent('select', this, record, index);
49396         }
49397     },
49398     
49399     setFromCurrencyData : function(o)
49400     {
49401         var currency = '';
49402         
49403         this.lastCurrency = o;
49404         
49405         if (this.currencyField) {
49406             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49407         } else {
49408             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49409         }
49410         
49411         this.lastSelectionText = currency;
49412         
49413         //setting default currency
49414         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49415             this.setCurrency(this.defaultCurrency);
49416             return;
49417         }
49418         
49419         this.setCurrency(currency);
49420     },
49421     
49422     setFromData : function(o)
49423     {
49424         var c = {};
49425         
49426         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49427         
49428         this.setFromCurrencyData(c);
49429         
49430         var value = '';
49431         
49432         if (this.name) {
49433             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49434         } else {
49435             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49436         }
49437         
49438         this.setValue(value);
49439         
49440     },
49441     
49442     setCurrency : function(v)
49443     {   
49444         this.currencyValue = v;
49445         
49446         if(this.rendered){
49447             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49448             this.validate();
49449         }
49450     },
49451     
49452     setValue : function(v)
49453     {
49454         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49455         
49456         this.value = v;
49457         
49458         if(this.rendered){
49459             
49460             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49461             
49462             this.inputEl().dom.value = (v == '') ? '' :
49463                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49464             
49465             if(!this.allowZero && v === '0') {
49466                 this.hiddenEl().dom.value = '';
49467                 this.inputEl().dom.value = '';
49468             }
49469             
49470             this.validate();
49471         }
49472     },
49473     
49474     getRawValue : function()
49475     {
49476         var v = this.inputEl().getValue();
49477         
49478         return v;
49479     },
49480     
49481     getValue : function()
49482     {
49483         return this.fixPrecision(this.parseValue(this.getRawValue()));
49484     },
49485     
49486     parseValue : function(value)
49487     {
49488         if(this.thousandsDelimiter) {
49489             value += "";
49490             r = new RegExp(",", "g");
49491             value = value.replace(r, "");
49492         }
49493         
49494         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49495         return isNaN(value) ? '' : value;
49496         
49497     },
49498     
49499     fixPrecision : function(value)
49500     {
49501         if(this.thousandsDelimiter) {
49502             value += "";
49503             r = new RegExp(",", "g");
49504             value = value.replace(r, "");
49505         }
49506         
49507         var nan = isNaN(value);
49508         
49509         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49510             return nan ? '' : value;
49511         }
49512         return parseFloat(value).toFixed(this.decimalPrecision);
49513     },
49514     
49515     decimalPrecisionFcn : function(v)
49516     {
49517         return Math.floor(v);
49518     },
49519     
49520     validateValue : function(value)
49521     {
49522         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49523             return false;
49524         }
49525         
49526         var num = this.parseValue(value);
49527         
49528         if(isNaN(num)){
49529             this.markInvalid(String.format(this.nanText, value));
49530             return false;
49531         }
49532         
49533         if(num < this.minValue){
49534             this.markInvalid(String.format(this.minText, this.minValue));
49535             return false;
49536         }
49537         
49538         if(num > this.maxValue){
49539             this.markInvalid(String.format(this.maxText, this.maxValue));
49540             return false;
49541         }
49542         
49543         return true;
49544     },
49545     
49546     validate : function()
49547     {
49548         if(this.disabled || this.allowBlank){
49549             this.markValid();
49550             return true;
49551         }
49552         
49553         var currency = this.getCurrency();
49554         
49555         if(this.validateValue(this.getRawValue()) && currency.length){
49556             this.markValid();
49557             return true;
49558         }
49559         
49560         this.markInvalid();
49561         return false;
49562     },
49563     
49564     getName: function()
49565     {
49566         return this.name;
49567     },
49568     
49569     beforeBlur : function()
49570     {
49571         if(!this.castInt){
49572             return;
49573         }
49574         
49575         var v = this.parseValue(this.getRawValue());
49576         
49577         if(v || v == 0){
49578             this.setValue(v);
49579         }
49580     },
49581     
49582     onBlur : function()
49583     {
49584         this.beforeBlur();
49585         
49586         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49587             //this.el.removeClass(this.focusClass);
49588         }
49589         
49590         this.hasFocus = false;
49591         
49592         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49593             this.validate();
49594         }
49595         
49596         var v = this.getValue();
49597         
49598         if(String(v) !== String(this.startValue)){
49599             this.fireEvent('change', this, v, this.startValue);
49600         }
49601         
49602         this.fireEvent("blur", this);
49603     },
49604     
49605     inputEl : function()
49606     {
49607         return this.el.select('.roo-money-amount-input', true).first();
49608     },
49609     
49610     currencyEl : function()
49611     {
49612         return this.el.select('.roo-money-currency-input', true).first();
49613     },
49614     
49615     hiddenEl : function()
49616     {
49617         return this.el.select('input.hidden-number-input',true).first();
49618     }
49619     
49620 });/**
49621  * @class Roo.bootstrap.BezierSignature
49622  * @extends Roo.bootstrap.Component
49623  * Bootstrap BezierSignature class
49624  * This script refer to:
49625  *    Title: Signature Pad
49626  *    Author: szimek
49627  *    Availability: https://github.com/szimek/signature_pad
49628  *
49629  * @constructor
49630  * Create a new BezierSignature
49631  * @param {Object} config The config object
49632  */
49633
49634 Roo.bootstrap.BezierSignature = function(config){
49635     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49636     this.addEvents({
49637         "resize" : true
49638     });
49639 };
49640
49641 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49642 {
49643      
49644     curve_data: [],
49645     
49646     is_empty: true,
49647     
49648     mouse_btn_down: true,
49649     
49650     /**
49651      * @cfg {int} canvas height
49652      */
49653     canvas_height: '200px',
49654     
49655     /**
49656      * @cfg {float|function} Radius of a single dot.
49657      */ 
49658     dot_size: false,
49659     
49660     /**
49661      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49662      */
49663     min_width: 0.5,
49664     
49665     /**
49666      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49667      */
49668     max_width: 2.5,
49669     
49670     /**
49671      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49672      */
49673     throttle: 16,
49674     
49675     /**
49676      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49677      */
49678     min_distance: 5,
49679     
49680     /**
49681      * @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.
49682      */
49683     bg_color: 'rgba(0, 0, 0, 0)',
49684     
49685     /**
49686      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49687      */
49688     dot_color: 'black',
49689     
49690     /**
49691      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49692      */ 
49693     velocity_filter_weight: 0.7,
49694     
49695     /**
49696      * @cfg {function} Callback when stroke begin. 
49697      */
49698     onBegin: false,
49699     
49700     /**
49701      * @cfg {function} Callback when stroke end.
49702      */
49703     onEnd: false,
49704     
49705     getAutoCreate : function()
49706     {
49707         var cls = 'roo-signature column';
49708         
49709         if(this.cls){
49710             cls += ' ' + this.cls;
49711         }
49712         
49713         var col_sizes = [
49714             'lg',
49715             'md',
49716             'sm',
49717             'xs'
49718         ];
49719         
49720         for(var i = 0; i < col_sizes.length; i++) {
49721             if(this[col_sizes[i]]) {
49722                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49723             }
49724         }
49725         
49726         var cfg = {
49727             tag: 'div',
49728             cls: cls,
49729             cn: [
49730                 {
49731                     tag: 'div',
49732                     cls: 'roo-signature-body',
49733                     cn: [
49734                         {
49735                             tag: 'canvas',
49736                             cls: 'roo-signature-body-canvas',
49737                             height: this.canvas_height,
49738                             width: this.canvas_width
49739                         }
49740                     ]
49741                 },
49742                 {
49743                     tag: 'input',
49744                     type: 'file',
49745                     style: 'display: none'
49746                 }
49747             ]
49748         };
49749         
49750         return cfg;
49751     },
49752     
49753     initEvents: function() 
49754     {
49755         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49756         
49757         var canvas = this.canvasEl();
49758         
49759         // mouse && touch event swapping...
49760         canvas.dom.style.touchAction = 'none';
49761         canvas.dom.style.msTouchAction = 'none';
49762         
49763         this.mouse_btn_down = false;
49764         canvas.on('mousedown', this._handleMouseDown, this);
49765         canvas.on('mousemove', this._handleMouseMove, this);
49766         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49767         
49768         if (window.PointerEvent) {
49769             canvas.on('pointerdown', this._handleMouseDown, this);
49770             canvas.on('pointermove', this._handleMouseMove, this);
49771             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49772         }
49773         
49774         if ('ontouchstart' in window) {
49775             canvas.on('touchstart', this._handleTouchStart, this);
49776             canvas.on('touchmove', this._handleTouchMove, this);
49777             canvas.on('touchend', this._handleTouchEnd, this);
49778         }
49779         
49780         Roo.EventManager.onWindowResize(this.resize, this, true);
49781         
49782         // file input event
49783         this.fileEl().on('change', this.uploadImage, this);
49784         
49785         this.clear();
49786         
49787         this.resize();
49788     },
49789     
49790     resize: function(){
49791         
49792         var canvas = this.canvasEl().dom;
49793         var ctx = this.canvasElCtx();
49794         var img_data = false;
49795         
49796         if(canvas.width > 0) {
49797             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49798         }
49799         // setting canvas width will clean img data
49800         canvas.width = 0;
49801         
49802         var style = window.getComputedStyle ? 
49803             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49804             
49805         var padding_left = parseInt(style.paddingLeft) || 0;
49806         var padding_right = parseInt(style.paddingRight) || 0;
49807         
49808         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49809         
49810         if(img_data) {
49811             ctx.putImageData(img_data, 0, 0);
49812         }
49813     },
49814     
49815     _handleMouseDown: function(e)
49816     {
49817         if (e.browserEvent.which === 1) {
49818             this.mouse_btn_down = true;
49819             this.strokeBegin(e);
49820         }
49821     },
49822     
49823     _handleMouseMove: function (e)
49824     {
49825         if (this.mouse_btn_down) {
49826             this.strokeMoveUpdate(e);
49827         }
49828     },
49829     
49830     _handleMouseUp: function (e)
49831     {
49832         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49833             this.mouse_btn_down = false;
49834             this.strokeEnd(e);
49835         }
49836     },
49837     
49838     _handleTouchStart: function (e) {
49839         
49840         e.preventDefault();
49841         if (e.browserEvent.targetTouches.length === 1) {
49842             // var touch = e.browserEvent.changedTouches[0];
49843             // this.strokeBegin(touch);
49844             
49845              this.strokeBegin(e); // assume e catching the correct xy...
49846         }
49847     },
49848     
49849     _handleTouchMove: function (e) {
49850         e.preventDefault();
49851         // var touch = event.targetTouches[0];
49852         // _this._strokeMoveUpdate(touch);
49853         this.strokeMoveUpdate(e);
49854     },
49855     
49856     _handleTouchEnd: function (e) {
49857         var wasCanvasTouched = e.target === this.canvasEl().dom;
49858         if (wasCanvasTouched) {
49859             e.preventDefault();
49860             // var touch = event.changedTouches[0];
49861             // _this._strokeEnd(touch);
49862             this.strokeEnd(e);
49863         }
49864     },
49865     
49866     reset: function () {
49867         this._lastPoints = [];
49868         this._lastVelocity = 0;
49869         this._lastWidth = (this.min_width + this.max_width) / 2;
49870         this.canvasElCtx().fillStyle = this.dot_color;
49871     },
49872     
49873     strokeMoveUpdate: function(e)
49874     {
49875         this.strokeUpdate(e);
49876         
49877         if (this.throttle) {
49878             this.throttleStroke(this.strokeUpdate, this.throttle);
49879         }
49880         else {
49881             this.strokeUpdate(e);
49882         }
49883     },
49884     
49885     strokeBegin: function(e)
49886     {
49887         var newPointGroup = {
49888             color: this.dot_color,
49889             points: []
49890         };
49891         
49892         if (typeof this.onBegin === 'function') {
49893             this.onBegin(e);
49894         }
49895         
49896         this.curve_data.push(newPointGroup);
49897         this.reset();
49898         this.strokeUpdate(e);
49899     },
49900     
49901     strokeUpdate: function(e)
49902     {
49903         var rect = this.canvasEl().dom.getBoundingClientRect();
49904         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49905         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49906         var lastPoints = lastPointGroup.points;
49907         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49908         var isLastPointTooClose = lastPoint
49909             ? point.distanceTo(lastPoint) <= this.min_distance
49910             : false;
49911         var color = lastPointGroup.color;
49912         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49913             var curve = this.addPoint(point);
49914             if (!lastPoint) {
49915                 this.drawDot({color: color, point: point});
49916             }
49917             else if (curve) {
49918                 this.drawCurve({color: color, curve: curve});
49919             }
49920             lastPoints.push({
49921                 time: point.time,
49922                 x: point.x,
49923                 y: point.y
49924             });
49925         }
49926     },
49927     
49928     strokeEnd: function(e)
49929     {
49930         this.strokeUpdate(e);
49931         if (typeof this.onEnd === 'function') {
49932             this.onEnd(e);
49933         }
49934     },
49935     
49936     addPoint:  function (point) {
49937         var _lastPoints = this._lastPoints;
49938         _lastPoints.push(point);
49939         if (_lastPoints.length > 2) {
49940             if (_lastPoints.length === 3) {
49941                 _lastPoints.unshift(_lastPoints[0]);
49942             }
49943             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49944             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49945             _lastPoints.shift();
49946             return curve;
49947         }
49948         return null;
49949     },
49950     
49951     calculateCurveWidths: function (startPoint, endPoint) {
49952         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49953             (1 - this.velocity_filter_weight) * this._lastVelocity;
49954
49955         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49956         var widths = {
49957             end: newWidth,
49958             start: this._lastWidth
49959         };
49960         
49961         this._lastVelocity = velocity;
49962         this._lastWidth = newWidth;
49963         return widths;
49964     },
49965     
49966     drawDot: function (_a) {
49967         var color = _a.color, point = _a.point;
49968         var ctx = this.canvasElCtx();
49969         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49970         ctx.beginPath();
49971         this.drawCurveSegment(point.x, point.y, width);
49972         ctx.closePath();
49973         ctx.fillStyle = color;
49974         ctx.fill();
49975     },
49976     
49977     drawCurve: function (_a) {
49978         var color = _a.color, curve = _a.curve;
49979         var ctx = this.canvasElCtx();
49980         var widthDelta = curve.endWidth - curve.startWidth;
49981         var drawSteps = Math.floor(curve.length()) * 2;
49982         ctx.beginPath();
49983         ctx.fillStyle = color;
49984         for (var i = 0; i < drawSteps; i += 1) {
49985         var t = i / drawSteps;
49986         var tt = t * t;
49987         var ttt = tt * t;
49988         var u = 1 - t;
49989         var uu = u * u;
49990         var uuu = uu * u;
49991         var x = uuu * curve.startPoint.x;
49992         x += 3 * uu * t * curve.control1.x;
49993         x += 3 * u * tt * curve.control2.x;
49994         x += ttt * curve.endPoint.x;
49995         var y = uuu * curve.startPoint.y;
49996         y += 3 * uu * t * curve.control1.y;
49997         y += 3 * u * tt * curve.control2.y;
49998         y += ttt * curve.endPoint.y;
49999         var width = curve.startWidth + ttt * widthDelta;
50000         this.drawCurveSegment(x, y, width);
50001         }
50002         ctx.closePath();
50003         ctx.fill();
50004     },
50005     
50006     drawCurveSegment: function (x, y, width) {
50007         var ctx = this.canvasElCtx();
50008         ctx.moveTo(x, y);
50009         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50010         this.is_empty = false;
50011     },
50012     
50013     clear: function()
50014     {
50015         var ctx = this.canvasElCtx();
50016         var canvas = this.canvasEl().dom;
50017         ctx.fillStyle = this.bg_color;
50018         ctx.clearRect(0, 0, canvas.width, canvas.height);
50019         ctx.fillRect(0, 0, canvas.width, canvas.height);
50020         this.curve_data = [];
50021         this.reset();
50022         this.is_empty = true;
50023     },
50024     
50025     fileEl: function()
50026     {
50027         return  this.el.select('input',true).first();
50028     },
50029     
50030     canvasEl: function()
50031     {
50032         return this.el.select('canvas',true).first();
50033     },
50034     
50035     canvasElCtx: function()
50036     {
50037         return this.el.select('canvas',true).first().dom.getContext('2d');
50038     },
50039     
50040     getImage: function(type)
50041     {
50042         if(this.is_empty) {
50043             return false;
50044         }
50045         
50046         // encryption ?
50047         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50048     },
50049     
50050     drawFromImage: function(img_src)
50051     {
50052         var img = new Image();
50053         
50054         img.onload = function(){
50055             this.canvasElCtx().drawImage(img, 0, 0);
50056         }.bind(this);
50057         
50058         img.src = img_src;
50059         
50060         this.is_empty = false;
50061     },
50062     
50063     selectImage: function()
50064     {
50065         this.fileEl().dom.click();
50066     },
50067     
50068     uploadImage: function(e)
50069     {
50070         var reader = new FileReader();
50071         
50072         reader.onload = function(e){
50073             var img = new Image();
50074             img.onload = function(){
50075                 this.reset();
50076                 this.canvasElCtx().drawImage(img, 0, 0);
50077             }.bind(this);
50078             img.src = e.target.result;
50079         }.bind(this);
50080         
50081         reader.readAsDataURL(e.target.files[0]);
50082     },
50083     
50084     // Bezier Point Constructor
50085     Point: (function () {
50086         function Point(x, y, time) {
50087             this.x = x;
50088             this.y = y;
50089             this.time = time || Date.now();
50090         }
50091         Point.prototype.distanceTo = function (start) {
50092             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50093         };
50094         Point.prototype.equals = function (other) {
50095             return this.x === other.x && this.y === other.y && this.time === other.time;
50096         };
50097         Point.prototype.velocityFrom = function (start) {
50098             return this.time !== start.time
50099             ? this.distanceTo(start) / (this.time - start.time)
50100             : 0;
50101         };
50102         return Point;
50103     }()),
50104     
50105     
50106     // Bezier Constructor
50107     Bezier: (function () {
50108         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50109             this.startPoint = startPoint;
50110             this.control2 = control2;
50111             this.control1 = control1;
50112             this.endPoint = endPoint;
50113             this.startWidth = startWidth;
50114             this.endWidth = endWidth;
50115         }
50116         Bezier.fromPoints = function (points, widths, scope) {
50117             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50118             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50119             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50120         };
50121         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50122             var dx1 = s1.x - s2.x;
50123             var dy1 = s1.y - s2.y;
50124             var dx2 = s2.x - s3.x;
50125             var dy2 = s2.y - s3.y;
50126             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50127             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50128             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50129             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50130             var dxm = m1.x - m2.x;
50131             var dym = m1.y - m2.y;
50132             var k = l2 / (l1 + l2);
50133             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50134             var tx = s2.x - cm.x;
50135             var ty = s2.y - cm.y;
50136             return {
50137                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50138                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50139             };
50140         };
50141         Bezier.prototype.length = function () {
50142             var steps = 10;
50143             var length = 0;
50144             var px;
50145             var py;
50146             for (var i = 0; i <= steps; i += 1) {
50147                 var t = i / steps;
50148                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50149                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50150                 if (i > 0) {
50151                     var xdiff = cx - px;
50152                     var ydiff = cy - py;
50153                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50154                 }
50155                 px = cx;
50156                 py = cy;
50157             }
50158             return length;
50159         };
50160         Bezier.prototype.point = function (t, start, c1, c2, end) {
50161             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50162             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50163             + (3.0 * c2 * (1.0 - t) * t * t)
50164             + (end * t * t * t);
50165         };
50166         return Bezier;
50167     }()),
50168     
50169     throttleStroke: function(fn, wait) {
50170       if (wait === void 0) { wait = 250; }
50171       var previous = 0;
50172       var timeout = null;
50173       var result;
50174       var storedContext;
50175       var storedArgs;
50176       var later = function () {
50177           previous = Date.now();
50178           timeout = null;
50179           result = fn.apply(storedContext, storedArgs);
50180           if (!timeout) {
50181               storedContext = null;
50182               storedArgs = [];
50183           }
50184       };
50185       return function wrapper() {
50186           var args = [];
50187           for (var _i = 0; _i < arguments.length; _i++) {
50188               args[_i] = arguments[_i];
50189           }
50190           var now = Date.now();
50191           var remaining = wait - (now - previous);
50192           storedContext = this;
50193           storedArgs = args;
50194           if (remaining <= 0 || remaining > wait) {
50195               if (timeout) {
50196                   clearTimeout(timeout);
50197                   timeout = null;
50198               }
50199               previous = now;
50200               result = fn.apply(storedContext, storedArgs);
50201               if (!timeout) {
50202                   storedContext = null;
50203                   storedArgs = [];
50204               }
50205           }
50206           else if (!timeout) {
50207               timeout = window.setTimeout(later, remaining);
50208           }
50209           return result;
50210       };
50211   }
50212   
50213 });
50214
50215  
50216
50217  // old names for form elements
50218 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50219 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50220 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50221 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50222 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50223 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50224 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50225 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50226 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50227 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50228 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50229 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50230 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50231 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50232 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50233 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50234 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50235 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50236 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50237 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50238 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50239 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50240 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50241 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50242 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50243 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50244
50245 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50246 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50247
50248 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50249 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50250
50251 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50252 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50253 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50254 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50255