Roo/bootstrap/Modal.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, false, false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.el.select('.modal-footer div').first());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.el.select('.modal-footer div').first());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             //dlg.footer.dom.style.display = 'none';
3328             return width;
3329         }
3330         dlg.footerEl.dom.style.display = '';
3331         for(var k in buttons){
3332             if(typeof buttons[k] != "function"){
3333                 if(b[k]){
3334                     buttons[k].show();
3335                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336                     width += buttons[k].el.getWidth()+15;
3337                 }else{
3338                     buttons[k].hide();
3339                 }
3340             }
3341         }
3342         return width;
3343     };
3344
3345     // private
3346     var handleEsc = function(d, k, e){
3347         if(opt && opt.closable !== false){
3348             dlg.hide();
3349         }
3350         if(e){
3351             e.stopEvent();
3352         }
3353     };
3354
3355     return {
3356         /**
3357          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358          * @return {Roo.BasicDialog} The BasicDialog element
3359          */
3360         getDialog : function(){
3361            if(!dlg){
3362                 dlg = new Roo.bootstrap.Modal( {
3363                     //draggable: true,
3364                     //resizable:false,
3365                     //constraintoviewport:false,
3366                     //fixedcenter:true,
3367                     //collapsible : false,
3368                     //shim:true,
3369                     //modal: true,
3370                 //    width: 'auto',
3371                   //  height:100,
3372                     //buttonAlign:"center",
3373                     closeClick : function(){
3374                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3375                             handleButton("no");
3376                         }else{
3377                             handleButton("cancel");
3378                         }
3379                     }
3380                 });
3381                 dlg.render();
3382                 dlg.on("hide", handleHide);
3383                 mask = dlg.mask;
3384                 //dlg.addKeyListener(27, handleEsc);
3385                 buttons = {};
3386                 this.buttons = buttons;
3387                 var bt = this.buttonText;
3388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3392                 //Roo.log(buttons);
3393                 bodyEl = dlg.bodyEl.createChild({
3394
3395                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396                         '<textarea class="roo-mb-textarea"></textarea>' +
3397                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3398                 });
3399                 msgEl = bodyEl.dom.firstChild;
3400                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401                 textboxEl.enableDisplayMode();
3402                 textboxEl.addKeyListener([10,13], function(){
3403                     if(dlg.isVisible() && opt && opt.buttons){
3404                         if(opt.buttons.ok){
3405                             handleButton("ok");
3406                         }else if(opt.buttons.yes){
3407                             handleButton("yes");
3408                         }
3409                     }
3410                 });
3411                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412                 textareaEl.enableDisplayMode();
3413                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414                 progressEl.enableDisplayMode();
3415                 
3416                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417                 var pf = progressEl.dom.firstChild;
3418                 if (pf) {
3419                     pp = Roo.get(pf.firstChild);
3420                     pp.setHeight(pf.offsetHeight);
3421                 }
3422                 
3423             }
3424             return dlg;
3425         },
3426
3427         /**
3428          * Updates the message box body text
3429          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430          * the XHTML-compliant non-breaking space character '&amp;#160;')
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         updateText : function(text)
3434         {
3435             if(!dlg.isVisible() && !opt.width){
3436                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3438             }
3439             msgEl.innerHTML = text || '&#160;';
3440       
3441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3443             var w = Math.max(
3444                     Math.min(opt.width || cw , this.maxWidth), 
3445                     Math.max(opt.minWidth || this.minWidth, bwidth)
3446             );
3447             if(opt.prompt){
3448                 activeTextEl.setWidth(w);
3449             }
3450             if(dlg.isVisible()){
3451                 dlg.fixedcenter = false;
3452             }
3453             // to big, make it scroll. = But as usual stupid IE does not support
3454             // !important..
3455             
3456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3459             } else {
3460                 bodyEl.dom.style.height = '';
3461                 bodyEl.dom.style.overflowY = '';
3462             }
3463             if (cw > w) {
3464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3465             } else {
3466                 bodyEl.dom.style.overflowX = '';
3467             }
3468             
3469             dlg.setContentSize(w, bodyEl.getHeight());
3470             if(dlg.isVisible()){
3471                 dlg.fixedcenter = true;
3472             }
3473             return this;
3474         },
3475
3476         /**
3477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         updateProgress : function(value, text){
3484             if(text){
3485                 this.updateText(text);
3486             }
3487             
3488             if (pp) { // weird bug on my firefox - for some reason this is not defined
3489                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3491             }
3492             return this;
3493         },        
3494
3495         /**
3496          * Returns true if the message box is currently displayed
3497          * @return {Boolean} True if the message box is visible, else false
3498          */
3499         isVisible : function(){
3500             return dlg && dlg.isVisible();  
3501         },
3502
3503         /**
3504          * Hides the message box if it is displayed
3505          */
3506         hide : function(){
3507             if(this.isVisible()){
3508                 dlg.hide();
3509             }  
3510         },
3511
3512         /**
3513          * Displays a new message box, or reinitializes an existing message box, based on the config options
3514          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515          * The following config object properties are supported:
3516          * <pre>
3517 Property    Type             Description
3518 ----------  ---------------  ------------------------------------------------------------------------------------
3519 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3520                                    closes (defaults to undefined)
3521 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3524                                    progress and wait dialogs will ignore this property and always hide the
3525                                    close button as they can only be closed programmatically.
3526 cls               String           A custom CSS class to apply to the message box element
3527 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3528                                    displayed (defaults to 75)
3529 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3530                                    function will be btn (the name of the button that was clicked, if applicable,
3531                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3532                                    Progress and wait dialogs will ignore this option since they do not respond to
3533                                    user actions and can only be closed programmatically, so any required function
3534                                    should be called by the same code after it closes the dialog.
3535 icon              String           A CSS class that provides a background image to be used as an icon for
3536                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3538 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3539 modal             Boolean          False to allow user interaction with the page while the message box is
3540                                    displayed (defaults to true)
3541 msg               String           A string that will replace the existing message box body text (defaults
3542                                    to the XHTML-compliant non-breaking space character '&#160;')
3543 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3544 progress          Boolean          True to display a progress bar (defaults to false)
3545 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3548 title             String           The title text
3549 value             String           The string value to set into the active textbox element if displayed
3550 wait              Boolean          True to display a progress bar (defaults to false)
3551 width             Number           The width of the dialog in pixels
3552 </pre>
3553          *
3554          * Example usage:
3555          * <pre><code>
3556 Roo.Msg.show({
3557    title: 'Address',
3558    msg: 'Please enter your address:',
3559    width: 300,
3560    buttons: Roo.MessageBox.OKCANCEL,
3561    multiline: true,
3562    fn: saveAddress,
3563    animEl: 'addAddressBtn'
3564 });
3565 </code></pre>
3566          * @param {Object} config Configuration options
3567          * @return {Roo.MessageBox} This message box
3568          */
3569         show : function(options)
3570         {
3571             
3572             // this causes nightmares if you show one dialog after another
3573             // especially on callbacks..
3574              
3575             if(this.isVisible()){
3576                 
3577                 this.hide();
3578                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3580                 Roo.log("New Dialog Message:" +  options.msg )
3581                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3583                 
3584             }
3585             var d = this.getDialog();
3586             opt = options;
3587             d.setTitle(opt.title || "&#160;");
3588             d.closeEl.setDisplayed(opt.closable !== false);
3589             activeTextEl = textboxEl;
3590             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3591             if(opt.prompt){
3592                 if(opt.multiline){
3593                     textboxEl.hide();
3594                     textareaEl.show();
3595                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3596                         opt.multiline : this.defaultTextHeight);
3597                     activeTextEl = textareaEl;
3598                 }else{
3599                     textboxEl.show();
3600                     textareaEl.hide();
3601                 }
3602             }else{
3603                 textboxEl.hide();
3604                 textareaEl.hide();
3605             }
3606             progressEl.setDisplayed(opt.progress === true);
3607             this.updateProgress(0);
3608             activeTextEl.dom.value = opt.value || "";
3609             if(opt.prompt){
3610                 dlg.setDefaultButton(activeTextEl);
3611             }else{
3612                 var bs = opt.buttons;
3613                 var db = null;
3614                 if(bs && bs.ok){
3615                     db = buttons["ok"];
3616                 }else if(bs && bs.yes){
3617                     db = buttons["yes"];
3618                 }
3619                 dlg.setDefaultButton(db);
3620             }
3621             bwidth = updateButtons(opt.buttons);
3622             this.updateText(opt.msg);
3623             if(opt.cls){
3624                 d.el.addClass(opt.cls);
3625             }
3626             d.proxyDrag = opt.proxyDrag === true;
3627             d.modal = opt.modal !== false;
3628             d.mask = opt.modal !== false ? mask : false;
3629             if(!d.isVisible()){
3630                 // force it to the end of the z-index stack so it gets a cursor in FF
3631                 document.body.appendChild(dlg.el.dom);
3632                 d.animateTarget = null;
3633                 d.show(options.animEl);
3634             }
3635             return this;
3636         },
3637
3638         /**
3639          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3640          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3641          * and closing the message box when the process is complete.
3642          * @param {String} title The title bar text
3643          * @param {String} msg The message box body text
3644          * @return {Roo.MessageBox} This message box
3645          */
3646         progress : function(title, msg){
3647             this.show({
3648                 title : title,
3649                 msg : msg,
3650                 buttons: false,
3651                 progress:true,
3652                 closable:false,
3653                 minWidth: this.minProgressWidth,
3654                 modal : true
3655             });
3656             return this;
3657         },
3658
3659         /**
3660          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3661          * If a callback function is passed it will be called after the user clicks the button, and the
3662          * id of the button that was clicked will be passed as the only parameter to the callback
3663          * (could also be the top-right close button).
3664          * @param {String} title The title bar text
3665          * @param {String} msg The message box body text
3666          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3667          * @param {Object} scope (optional) The scope of the callback function
3668          * @return {Roo.MessageBox} This message box
3669          */
3670         alert : function(title, msg, fn, scope)
3671         {
3672             this.show({
3673                 title : title,
3674                 msg : msg,
3675                 buttons: this.OK,
3676                 fn: fn,
3677                 closable : false,
3678                 scope : scope,
3679                 modal : true
3680             });
3681             return this;
3682         },
3683
3684         /**
3685          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3686          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3687          * You are responsible for closing the message box when the process is complete.
3688          * @param {String} msg The message box body text
3689          * @param {String} title (optional) The title bar text
3690          * @return {Roo.MessageBox} This message box
3691          */
3692         wait : function(msg, title){
3693             this.show({
3694                 title : title,
3695                 msg : msg,
3696                 buttons: false,
3697                 closable:false,
3698                 progress:true,
3699                 modal:true,
3700                 width:300,
3701                 wait:true
3702             });
3703             waitTimer = Roo.TaskMgr.start({
3704                 run: function(i){
3705                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3706                 },
3707                 interval: 1000
3708             });
3709             return this;
3710         },
3711
3712         /**
3713          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3714          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3715          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3716          * @param {String} title The title bar text
3717          * @param {String} msg The message box body text
3718          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3719          * @param {Object} scope (optional) The scope of the callback function
3720          * @return {Roo.MessageBox} This message box
3721          */
3722         confirm : function(title, msg, fn, scope){
3723             this.show({
3724                 title : title,
3725                 msg : msg,
3726                 buttons: this.YESNO,
3727                 fn: fn,
3728                 scope : scope,
3729                 modal : true
3730             });
3731             return this;
3732         },
3733
3734         /**
3735          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3736          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3737          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3738          * (could also be the top-right close button) and the text that was entered will be passed as the two
3739          * parameters to the callback.
3740          * @param {String} title The title bar text
3741          * @param {String} msg The message box body text
3742          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3743          * @param {Object} scope (optional) The scope of the callback function
3744          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3745          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3746          * @return {Roo.MessageBox} This message box
3747          */
3748         prompt : function(title, msg, fn, scope, multiline){
3749             this.show({
3750                 title : title,
3751                 msg : msg,
3752                 buttons: this.OKCANCEL,
3753                 fn: fn,
3754                 minWidth:250,
3755                 scope : scope,
3756                 prompt:true,
3757                 multiline: multiline,
3758                 modal : true
3759             });
3760             return this;
3761         },
3762
3763         /**
3764          * Button config that displays a single OK button
3765          * @type Object
3766          */
3767         OK : {ok:true},
3768         /**
3769          * Button config that displays Yes and No buttons
3770          * @type Object
3771          */
3772         YESNO : {yes:true, no:true},
3773         /**
3774          * Button config that displays OK and Cancel buttons
3775          * @type Object
3776          */
3777         OKCANCEL : {ok:true, cancel:true},
3778         /**
3779          * Button config that displays Yes, No and Cancel buttons
3780          * @type Object
3781          */
3782         YESNOCANCEL : {yes:true, no:true, cancel:true},
3783
3784         /**
3785          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3786          * @type Number
3787          */
3788         defaultTextHeight : 75,
3789         /**
3790          * The maximum width in pixels of the message box (defaults to 600)
3791          * @type Number
3792          */
3793         maxWidth : 600,
3794         /**
3795          * The minimum width in pixels of the message box (defaults to 100)
3796          * @type Number
3797          */
3798         minWidth : 100,
3799         /**
3800          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3801          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3802          * @type Number
3803          */
3804         minProgressWidth : 250,
3805         /**
3806          * An object containing the default button text strings that can be overriden for localized language support.
3807          * Supported properties are: ok, cancel, yes and no.
3808          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3809          * @type Object
3810          */
3811         buttonText : {
3812             ok : "OK",
3813             cancel : "Cancel",
3814             yes : "Yes",
3815             no : "No"
3816         }
3817     };
3818 }();
3819
3820 /**
3821  * Shorthand for {@link Roo.MessageBox}
3822  */
3823 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3824 Roo.Msg = Roo.Msg || Roo.MessageBox;
3825 /*
3826  * - LGPL
3827  *
3828  * navbar
3829  * 
3830  */
3831
3832 /**
3833  * @class Roo.bootstrap.Navbar
3834  * @extends Roo.bootstrap.Component
3835  * Bootstrap Navbar class
3836
3837  * @constructor
3838  * Create a new Navbar
3839  * @param {Object} config The config object
3840  */
3841
3842
3843 Roo.bootstrap.Navbar = function(config){
3844     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3845     this.addEvents({
3846         // raw events
3847         /**
3848          * @event beforetoggle
3849          * Fire before toggle the menu
3850          * @param {Roo.EventObject} e
3851          */
3852         "beforetoggle" : true
3853     });
3854 };
3855
3856 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3857     
3858     
3859    
3860     // private
3861     navItems : false,
3862     loadMask : false,
3863     
3864     
3865     getAutoCreate : function(){
3866         
3867         
3868         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3869         
3870     },
3871     
3872     initEvents :function ()
3873     {
3874         //Roo.log(this.el.select('.navbar-toggle',true));
3875         this.el.select('.navbar-toggle',true).on('click', function() {
3876             if(this.fireEvent('beforetoggle', this) !== false){
3877                 var ce = this.el.select('.navbar-collapse',true).first();
3878                 ce.toggleClass('in'); // old...
3879                 if (ce.hasClass('collapse')) {
3880                     // show it...
3881                     ce.removeClass('collapse');
3882                     ce.addClass('show');
3883                     var h = ce.getHeight();
3884                     Roo.log(h);
3885                     ce.removeClass('show');
3886                     // at this point we should be able to see it..
3887                     ce.addClass('collapsing');
3888                     
3889                     ce.setHeight(0); // resize it ...
3890                     ce.on('transitionend', function() {
3891                         Roo.log('done transition');
3892                         ce.removeClass('collapsing');
3893                         ce.addClass('show');
3894                         ce.removeClass('collapse');
3895
3896                         ce.dom.style.height = '';
3897                     }, this, { single: true} );
3898                     ce.setHeight(h);
3899                     
3900                 } else {
3901                     ce.setHeight(ce.getHeight());
3902                     ce.removeClass('show');
3903                     ce.addClass('collapsing');
3904                     
3905                     ce.on('transitionend', function() {
3906                         ce.dom.style.height = '';
3907                         ce.removeClass('collapsing');
3908                         ce.addClass('collapse');
3909                     }, this, { single: true} );
3910                     ce.setHeight(0);
3911                 }
3912             }
3913             
3914         }, this);
3915         
3916         var mark = {
3917             tag: "div",
3918             cls:"x-dlg-mask"
3919         };
3920         
3921         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3922         
3923         var size = this.el.getSize();
3924         this.maskEl.setSize(size.width, size.height);
3925         this.maskEl.enableDisplayMode("block");
3926         this.maskEl.hide();
3927         
3928         if(this.loadMask){
3929             this.maskEl.show();
3930         }
3931     },
3932     
3933     
3934     getChildContainer : function()
3935     {
3936         if (this.el.select('.collapse').getCount()) {
3937             return this.el.select('.collapse',true).first();
3938         }
3939         
3940         return this.el;
3941     },
3942     
3943     mask : function()
3944     {
3945         this.maskEl.show();
3946     },
3947     
3948     unmask : function()
3949     {
3950         this.maskEl.hide();
3951     } 
3952     
3953     
3954     
3955     
3956 });
3957
3958
3959
3960  
3961
3962  /*
3963  * - LGPL
3964  *
3965  * navbar
3966  * 
3967  */
3968
3969 /**
3970  * @class Roo.bootstrap.NavSimplebar
3971  * @extends Roo.bootstrap.Navbar
3972  * Bootstrap Sidebar class
3973  *
3974  * @cfg {Boolean} inverse is inverted color
3975  * 
3976  * @cfg {String} type (nav | pills | tabs)
3977  * @cfg {Boolean} arrangement stacked | justified
3978  * @cfg {String} align (left | right) alignment
3979  * 
3980  * @cfg {Boolean} main (true|false) main nav bar? default false
3981  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3982  * 
3983  * @cfg {String} tag (header|footer|nav|div) default is nav 
3984
3985  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3986  * 
3987  * 
3988  * @constructor
3989  * Create a new Sidebar
3990  * @param {Object} config The config object
3991  */
3992
3993
3994 Roo.bootstrap.NavSimplebar = function(config){
3995     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3999     
4000     inverse: false,
4001     
4002     type: false,
4003     arrangement: '',
4004     align : false,
4005     
4006     weight : 'light',
4007     
4008     main : false,
4009     
4010     
4011     tag : false,
4012     
4013     
4014     getAutoCreate : function(){
4015         
4016         
4017         var cfg = {
4018             tag : this.tag || 'div',
4019             cls : 'navbar navbar-expand-lg'
4020         };
4021         if (['light','white'].indexOf(this.weight) > -1) {
4022             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4023         }
4024         cfg.cls += ' bg-' + this.weight;
4025         
4026           
4027         
4028         cfg.cn = [
4029             {
4030                 cls: 'nav',
4031                 tag : 'ul'
4032             }
4033         ];
4034         
4035          
4036         this.type = this.type || 'nav';
4037         if (['tabs','pills'].indexOf(this.type)!==-1) {
4038             cfg.cn[0].cls += ' nav-' + this.type
4039         
4040         
4041         } else {
4042             if (this.type!=='nav') {
4043                 Roo.log('nav type must be nav/tabs/pills')
4044             }
4045             cfg.cn[0].cls += ' navbar-nav'
4046         }
4047         
4048         
4049         
4050         
4051         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4052             cfg.cn[0].cls += ' nav-' + this.arrangement;
4053         }
4054         
4055         
4056         if (this.align === 'right') {
4057             cfg.cn[0].cls += ' navbar-right';
4058         }
4059         
4060         if (this.inverse) {
4061             cfg.cls += ' navbar-inverse';
4062             
4063         }
4064         
4065         
4066         return cfg;
4067     
4068         
4069     }
4070     
4071     
4072     
4073 });
4074
4075
4076
4077  
4078
4079  
4080        /*
4081  * - LGPL
4082  *
4083  * navbar
4084  * navbar-fixed-top
4085  * navbar-expand-md  fixed-top 
4086  */
4087
4088 /**
4089  * @class Roo.bootstrap.NavHeaderbar
4090  * @extends Roo.bootstrap.NavSimplebar
4091  * Bootstrap Sidebar class
4092  *
4093  * @cfg {String} brand what is brand
4094  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4095  * @cfg {String} brand_href href of the brand
4096  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4097  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4098  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4099  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4100  * 
4101  * @constructor
4102  * Create a new Sidebar
4103  * @param {Object} config The config object
4104  */
4105
4106
4107 Roo.bootstrap.NavHeaderbar = function(config){
4108     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4109       
4110 };
4111
4112 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4113     
4114     position: '',
4115     brand: '',
4116     brand_href: false,
4117     srButton : true,
4118     autohide : false,
4119     desktopCenter : false,
4120    
4121     
4122     getAutoCreate : function(){
4123         
4124         var   cfg = {
4125             tag: this.nav || 'nav',
4126             cls: 'navbar navbar-expand-md',
4127             role: 'navigation',
4128             cn: []
4129         };
4130         
4131         var cn = cfg.cn;
4132         if (this.desktopCenter) {
4133             cn.push({cls : 'container', cn : []});
4134             cn = cn[0].cn;
4135         }
4136         
4137         if(this.srButton){
4138             var btn = {
4139                 tag: 'button',
4140                 type: 'button',
4141                 cls: 'navbar-toggle navbar-toggler',
4142                 'data-toggle': 'collapse',
4143                 cn: [
4144                     {
4145                         tag: 'span',
4146                         cls: 'sr-only',
4147                         html: 'Toggle navigation'
4148                     },
4149                     {
4150                         tag: 'span',
4151                         cls: 'icon-bar navbar-toggler-icon'
4152                     },
4153                     {
4154                         tag: 'span',
4155                         cls: 'icon-bar'
4156                     },
4157                     {
4158                         tag: 'span',
4159                         cls: 'icon-bar'
4160                     }
4161                 ]
4162             };
4163             
4164             cn.push( Roo.bootstrap.version == 4 ? btn : {
4165                 tag: 'div',
4166                 cls: 'navbar-header',
4167                 cn: [
4168                     btn
4169                 ]
4170             });
4171         }
4172         
4173         cn.push({
4174             tag: 'div',
4175             cls: 'collapse navbar-collapse',
4176             cn : []
4177         });
4178         
4179         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4180         
4181         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4182             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4183             
4184             // tag can override this..
4185             
4186             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4187         }
4188         
4189         if (this.brand !== '') {
4190             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4191             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4192                 tag: 'a',
4193                 href: this.brand_href ? this.brand_href : '#',
4194                 cls: 'navbar-brand',
4195                 cn: [
4196                 this.brand
4197                 ]
4198             });
4199         }
4200         
4201         if(this.main){
4202             cfg.cls += ' main-nav';
4203         }
4204         
4205         
4206         return cfg;
4207
4208         
4209     },
4210     getHeaderChildContainer : function()
4211     {
4212         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4213             return this.el.select('.navbar-header',true).first();
4214         }
4215         
4216         return this.getChildContainer();
4217     },
4218     
4219     
4220     initEvents : function()
4221     {
4222         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4223         
4224         if (this.autohide) {
4225             
4226             var prevScroll = 0;
4227             var ft = this.el;
4228             
4229             Roo.get(document).on('scroll',function(e) {
4230                 var ns = Roo.get(document).getScroll().top;
4231                 var os = prevScroll;
4232                 prevScroll = ns;
4233                 
4234                 if(ns > os){
4235                     ft.removeClass('slideDown');
4236                     ft.addClass('slideUp');
4237                     return;
4238                 }
4239                 ft.removeClass('slideUp');
4240                 ft.addClass('slideDown');
4241                  
4242               
4243           },this);
4244         }
4245     }    
4246     
4247 });
4248
4249
4250
4251  
4252
4253  /*
4254  * - LGPL
4255  *
4256  * navbar
4257  * 
4258  */
4259
4260 /**
4261  * @class Roo.bootstrap.NavSidebar
4262  * @extends Roo.bootstrap.Navbar
4263  * Bootstrap Sidebar class
4264  * 
4265  * @constructor
4266  * Create a new Sidebar
4267  * @param {Object} config The config object
4268  */
4269
4270
4271 Roo.bootstrap.NavSidebar = function(config){
4272     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4273 };
4274
4275 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4276     
4277     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4278     
4279     getAutoCreate : function(){
4280         
4281         
4282         return  {
4283             tag: 'div',
4284             cls: 'sidebar sidebar-nav'
4285         };
4286     
4287         
4288     }
4289     
4290     
4291     
4292 });
4293
4294
4295
4296  
4297
4298  /*
4299  * - LGPL
4300  *
4301  * nav group
4302  * 
4303  */
4304
4305 /**
4306  * @class Roo.bootstrap.NavGroup
4307  * @extends Roo.bootstrap.Component
4308  * Bootstrap NavGroup class
4309  * @cfg {String} align (left|right)
4310  * @cfg {Boolean} inverse
4311  * @cfg {String} type (nav|pills|tab) default nav
4312  * @cfg {String} navId - reference Id for navbar.
4313
4314  * 
4315  * @constructor
4316  * Create a new nav group
4317  * @param {Object} config The config object
4318  */
4319
4320 Roo.bootstrap.NavGroup = function(config){
4321     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4322     this.navItems = [];
4323    
4324     Roo.bootstrap.NavGroup.register(this);
4325      this.addEvents({
4326         /**
4327              * @event changed
4328              * Fires when the active item changes
4329              * @param {Roo.bootstrap.NavGroup} this
4330              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4331              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4332          */
4333         'changed': true
4334      });
4335     
4336 };
4337
4338 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4339     
4340     align: '',
4341     inverse: false,
4342     form: false,
4343     type: 'nav',
4344     navId : '',
4345     // private
4346     
4347     navItems : false, 
4348     
4349     getAutoCreate : function()
4350     {
4351         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4352         
4353         cfg = {
4354             tag : 'ul',
4355             cls: 'nav' 
4356         };
4357         
4358         if (['tabs','pills'].indexOf(this.type)!==-1) {
4359             cfg.cls += ' nav-' + this.type
4360         } else {
4361             if (this.type!=='nav') {
4362                 Roo.log('nav type must be nav/tabs/pills')
4363             }
4364             cfg.cls += ' navbar-nav'
4365         }
4366         
4367         if (this.parent() && this.parent().sidebar) {
4368             cfg = {
4369                 tag: 'ul',
4370                 cls: 'dashboard-menu sidebar-menu'
4371             };
4372             
4373             return cfg;
4374         }
4375         
4376         if (this.form === true) {
4377             cfg = {
4378                 tag: 'form',
4379                 cls: 'navbar-form'
4380             };
4381             
4382             if (this.align === 'right') {
4383                 cfg.cls += ' navbar-right ml-md-auto';
4384             } else {
4385                 cfg.cls += ' navbar-left';
4386             }
4387         }
4388         
4389         if (this.align === 'right') {
4390             cfg.cls += ' navbar-right ml-md-auto';
4391         } else {
4392             cfg.cls += ' mr-auto';
4393         }
4394         
4395         if (this.inverse) {
4396             cfg.cls += ' navbar-inverse';
4397             
4398         }
4399         
4400         
4401         return cfg;
4402     },
4403     /**
4404     * sets the active Navigation item
4405     * @param {Roo.bootstrap.NavItem} the new current navitem
4406     */
4407     setActiveItem : function(item)
4408     {
4409         var prev = false;
4410         Roo.each(this.navItems, function(v){
4411             if (v == item) {
4412                 return ;
4413             }
4414             if (v.isActive()) {
4415                 v.setActive(false, true);
4416                 prev = v;
4417                 
4418             }
4419             
4420         });
4421
4422         item.setActive(true, true);
4423         this.fireEvent('changed', this, item, prev);
4424         
4425         
4426     },
4427     /**
4428     * gets the active Navigation item
4429     * @return {Roo.bootstrap.NavItem} the current navitem
4430     */
4431     getActive : function()
4432     {
4433         
4434         var prev = false;
4435         Roo.each(this.navItems, function(v){
4436             
4437             if (v.isActive()) {
4438                 prev = v;
4439                 
4440             }
4441             
4442         });
4443         return prev;
4444     },
4445     
4446     indexOfNav : function()
4447     {
4448         
4449         var prev = false;
4450         Roo.each(this.navItems, function(v,i){
4451             
4452             if (v.isActive()) {
4453                 prev = i;
4454                 
4455             }
4456             
4457         });
4458         return prev;
4459     },
4460     /**
4461     * adds a Navigation item
4462     * @param {Roo.bootstrap.NavItem} the navitem to add
4463     */
4464     addItem : function(cfg)
4465     {
4466         var cn = new Roo.bootstrap.NavItem(cfg);
4467         this.register(cn);
4468         cn.parentId = this.id;
4469         cn.onRender(this.el, null);
4470         return cn;
4471     },
4472     /**
4473     * register a Navigation item
4474     * @param {Roo.bootstrap.NavItem} the navitem to add
4475     */
4476     register : function(item)
4477     {
4478         this.navItems.push( item);
4479         item.navId = this.navId;
4480     
4481     },
4482     
4483     /**
4484     * clear all the Navigation item
4485     */
4486    
4487     clearAll : function()
4488     {
4489         this.navItems = [];
4490         this.el.dom.innerHTML = '';
4491     },
4492     
4493     getNavItem: function(tabId)
4494     {
4495         var ret = false;
4496         Roo.each(this.navItems, function(e) {
4497             if (e.tabId == tabId) {
4498                ret =  e;
4499                return false;
4500             }
4501             return true;
4502             
4503         });
4504         return ret;
4505     },
4506     
4507     setActiveNext : function()
4508     {
4509         var i = this.indexOfNav(this.getActive());
4510         if (i > this.navItems.length) {
4511             return;
4512         }
4513         this.setActiveItem(this.navItems[i+1]);
4514     },
4515     setActivePrev : function()
4516     {
4517         var i = this.indexOfNav(this.getActive());
4518         if (i  < 1) {
4519             return;
4520         }
4521         this.setActiveItem(this.navItems[i-1]);
4522     },
4523     clearWasActive : function(except) {
4524         Roo.each(this.navItems, function(e) {
4525             if (e.tabId != except.tabId && e.was_active) {
4526                e.was_active = false;
4527                return false;
4528             }
4529             return true;
4530             
4531         });
4532     },
4533     getWasActive : function ()
4534     {
4535         var r = false;
4536         Roo.each(this.navItems, function(e) {
4537             if (e.was_active) {
4538                r = e;
4539                return false;
4540             }
4541             return true;
4542             
4543         });
4544         return r;
4545     }
4546     
4547     
4548 });
4549
4550  
4551 Roo.apply(Roo.bootstrap.NavGroup, {
4552     
4553     groups: {},
4554      /**
4555     * register a Navigation Group
4556     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4557     */
4558     register : function(navgrp)
4559     {
4560         this.groups[navgrp.navId] = navgrp;
4561         
4562     },
4563     /**
4564     * fetch a Navigation Group based on the navigation ID
4565     * @param {string} the navgroup to add
4566     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4567     */
4568     get: function(navId) {
4569         if (typeof(this.groups[navId]) == 'undefined') {
4570             return false;
4571             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4572         }
4573         return this.groups[navId] ;
4574     }
4575     
4576     
4577     
4578 });
4579
4580  /*
4581  * - LGPL
4582  *
4583  * row
4584  * 
4585  */
4586
4587 /**
4588  * @class Roo.bootstrap.NavItem
4589  * @extends Roo.bootstrap.Component
4590  * Bootstrap Navbar.NavItem class
4591  * @cfg {String} href  link to
4592  * @cfg {String} html content of button
4593  * @cfg {String} badge text inside badge
4594  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4595  * @cfg {String} glyphicon DEPRICATED - use fa
4596  * @cfg {String} icon DEPRICATED - use fa
4597  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4598  * @cfg {Boolean} active Is item active
4599  * @cfg {Boolean} disabled Is item disabled
4600  
4601  * @cfg {Boolean} preventDefault (true | false) default false
4602  * @cfg {String} tabId the tab that this item activates.
4603  * @cfg {String} tagtype (a|span) render as a href or span?
4604  * @cfg {Boolean} animateRef (true|false) link to element default false  
4605   
4606  * @constructor
4607  * Create a new Navbar Item
4608  * @param {Object} config The config object
4609  */
4610 Roo.bootstrap.NavItem = function(config){
4611     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4612     this.addEvents({
4613         // raw events
4614         /**
4615          * @event click
4616          * The raw click event for the entire grid.
4617          * @param {Roo.EventObject} e
4618          */
4619         "click" : true,
4620          /**
4621             * @event changed
4622             * Fires when the active item active state changes
4623             * @param {Roo.bootstrap.NavItem} this
4624             * @param {boolean} state the new state
4625              
4626          */
4627         'changed': true,
4628         /**
4629             * @event scrollto
4630             * Fires when scroll to element
4631             * @param {Roo.bootstrap.NavItem} this
4632             * @param {Object} options
4633             * @param {Roo.EventObject} e
4634              
4635          */
4636         'scrollto': true
4637     });
4638    
4639 };
4640
4641 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4642     
4643     href: false,
4644     html: '',
4645     badge: '',
4646     icon: false,
4647     fa : false,
4648     glyphicon: false,
4649     active: false,
4650     preventDefault : false,
4651     tabId : false,
4652     tagtype : 'a',
4653     disabled : false,
4654     animateRef : false,
4655     was_active : false,
4656     
4657     getAutoCreate : function(){
4658          
4659         var cfg = {
4660             tag: 'li',
4661             cls: 'nav-item'
4662             
4663         };
4664         
4665         if (this.active) {
4666             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4667         }
4668         if (this.disabled) {
4669             cfg.cls += ' disabled';
4670         }
4671         
4672         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4673             cfg.cn = [
4674                 {
4675                     tag: this.tagtype,
4676                     href : this.href || "#",
4677                     html: this.html || ''
4678                 }
4679             ];
4680             if (this.tagtype == 'a') {
4681                 cfg.cn[0].cls = 'nav-link';
4682             }
4683             if (this.icon) {
4684                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4685             }
4686             if (this.fa) {
4687                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4688             }
4689             if(this.glyphicon) {
4690                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4691             }
4692             
4693             if (this.menu) {
4694                 
4695                 cfg.cn[0].html += " <span class='caret'></span>";
4696              
4697             }
4698             
4699             if (this.badge !== '') {
4700                  
4701                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4702             }
4703         }
4704         
4705         
4706         
4707         return cfg;
4708     },
4709     initEvents: function() 
4710     {
4711         if (typeof (this.menu) != 'undefined') {
4712             this.menu.parentType = this.xtype;
4713             this.menu.triggerEl = this.el;
4714             this.menu = this.addxtype(Roo.apply({}, this.menu));
4715         }
4716         
4717         this.el.select('a',true).on('click', this.onClick, this);
4718         
4719         if(this.tagtype == 'span'){
4720             this.el.select('span',true).on('click', this.onClick, this);
4721         }
4722        
4723         // at this point parent should be available..
4724         this.parent().register(this);
4725     },
4726     
4727     onClick : function(e)
4728     {
4729         if (e.getTarget('.dropdown-menu-item')) {
4730             // did you click on a menu itemm.... - then don't trigger onclick..
4731             return;
4732         }
4733         
4734         if(
4735                 this.preventDefault || 
4736                 this.href == '#' 
4737         ){
4738             Roo.log("NavItem - prevent Default?");
4739             e.preventDefault();
4740         }
4741         
4742         if (this.disabled) {
4743             return;
4744         }
4745         
4746         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4747         if (tg && tg.transition) {
4748             Roo.log("waiting for the transitionend");
4749             return;
4750         }
4751         
4752         
4753         
4754         //Roo.log("fire event clicked");
4755         if(this.fireEvent('click', this, e) === false){
4756             return;
4757         };
4758         
4759         if(this.tagtype == 'span'){
4760             return;
4761         }
4762         
4763         //Roo.log(this.href);
4764         var ael = this.el.select('a',true).first();
4765         //Roo.log(ael);
4766         
4767         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4768             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4769             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4770                 return; // ignore... - it's a 'hash' to another page.
4771             }
4772             Roo.log("NavItem - prevent Default?");
4773             e.preventDefault();
4774             this.scrollToElement(e);
4775         }
4776         
4777         
4778         var p =  this.parent();
4779    
4780         if (['tabs','pills'].indexOf(p.type)!==-1) {
4781             if (typeof(p.setActiveItem) !== 'undefined') {
4782                 p.setActiveItem(this);
4783             }
4784         }
4785         
4786         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4787         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4788             // remove the collapsed menu expand...
4789             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4790         }
4791     },
4792     
4793     isActive: function () {
4794         return this.active
4795     },
4796     setActive : function(state, fire, is_was_active)
4797     {
4798         if (this.active && !state && this.navId) {
4799             this.was_active = true;
4800             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4801             if (nv) {
4802                 nv.clearWasActive(this);
4803             }
4804             
4805         }
4806         this.active = state;
4807         
4808         if (!state ) {
4809             this.el.removeClass('active');
4810         } else if (!this.el.hasClass('active')) {
4811             this.el.addClass('active');
4812         }
4813         if (fire) {
4814             this.fireEvent('changed', this, state);
4815         }
4816         
4817         // show a panel if it's registered and related..
4818         
4819         if (!this.navId || !this.tabId || !state || is_was_active) {
4820             return;
4821         }
4822         
4823         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4824         if (!tg) {
4825             return;
4826         }
4827         var pan = tg.getPanelByName(this.tabId);
4828         if (!pan) {
4829             return;
4830         }
4831         // if we can not flip to new panel - go back to old nav highlight..
4832         if (false == tg.showPanel(pan)) {
4833             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4834             if (nv) {
4835                 var onav = nv.getWasActive();
4836                 if (onav) {
4837                     onav.setActive(true, false, true);
4838                 }
4839             }
4840             
4841         }
4842         
4843         
4844         
4845     },
4846      // this should not be here...
4847     setDisabled : function(state)
4848     {
4849         this.disabled = state;
4850         if (!state ) {
4851             this.el.removeClass('disabled');
4852         } else if (!this.el.hasClass('disabled')) {
4853             this.el.addClass('disabled');
4854         }
4855         
4856     },
4857     
4858     /**
4859      * Fetch the element to display the tooltip on.
4860      * @return {Roo.Element} defaults to this.el
4861      */
4862     tooltipEl : function()
4863     {
4864         return this.el.select('' + this.tagtype + '', true).first();
4865     },
4866     
4867     scrollToElement : function(e)
4868     {
4869         var c = document.body;
4870         
4871         /*
4872          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4873          */
4874         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4875             c = document.documentElement;
4876         }
4877         
4878         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4879         
4880         if(!target){
4881             return;
4882         }
4883
4884         var o = target.calcOffsetsTo(c);
4885         
4886         var options = {
4887             target : target,
4888             value : o[1]
4889         };
4890         
4891         this.fireEvent('scrollto', this, options, e);
4892         
4893         Roo.get(c).scrollTo('top', options.value, true);
4894         
4895         return;
4896     }
4897 });
4898  
4899
4900  /*
4901  * - LGPL
4902  *
4903  * sidebar item
4904  *
4905  *  li
4906  *    <span> icon </span>
4907  *    <span> text </span>
4908  *    <span>badge </span>
4909  */
4910
4911 /**
4912  * @class Roo.bootstrap.NavSidebarItem
4913  * @extends Roo.bootstrap.NavItem
4914  * Bootstrap Navbar.NavSidebarItem class
4915  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4916  * {Boolean} open is the menu open
4917  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4918  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4919  * {String} buttonSize (sm|md|lg)the extra classes for the button
4920  * {Boolean} showArrow show arrow next to the text (default true)
4921  * @constructor
4922  * Create a new Navbar Button
4923  * @param {Object} config The config object
4924  */
4925 Roo.bootstrap.NavSidebarItem = function(config){
4926     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4927     this.addEvents({
4928         // raw events
4929         /**
4930          * @event click
4931          * The raw click event for the entire grid.
4932          * @param {Roo.EventObject} e
4933          */
4934         "click" : true,
4935          /**
4936             * @event changed
4937             * Fires when the active item active state changes
4938             * @param {Roo.bootstrap.NavSidebarItem} this
4939             * @param {boolean} state the new state
4940              
4941          */
4942         'changed': true
4943     });
4944    
4945 };
4946
4947 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4948     
4949     badgeWeight : 'default',
4950     
4951     open: false,
4952     
4953     buttonView : false,
4954     
4955     buttonWeight : 'default',
4956     
4957     buttonSize : 'md',
4958     
4959     showArrow : true,
4960     
4961     getAutoCreate : function(){
4962         
4963         
4964         var a = {
4965                 tag: 'a',
4966                 href : this.href || '#',
4967                 cls: '',
4968                 html : '',
4969                 cn : []
4970         };
4971         
4972         if(this.buttonView){
4973             a = {
4974                 tag: 'button',
4975                 href : this.href || '#',
4976                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4977                 html : this.html,
4978                 cn : []
4979             };
4980         }
4981         
4982         var cfg = {
4983             tag: 'li',
4984             cls: '',
4985             cn: [ a ]
4986         };
4987         
4988         if (this.active) {
4989             cfg.cls += ' active';
4990         }
4991         
4992         if (this.disabled) {
4993             cfg.cls += ' disabled';
4994         }
4995         if (this.open) {
4996             cfg.cls += ' open x-open';
4997         }
4998         // left icon..
4999         if (this.glyphicon || this.icon) {
5000             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5001             a.cn.push({ tag : 'i', cls : c }) ;
5002         }
5003         
5004         if(!this.buttonView){
5005             var span = {
5006                 tag: 'span',
5007                 html : this.html || ''
5008             };
5009
5010             a.cn.push(span);
5011             
5012         }
5013         
5014         if (this.badge !== '') {
5015             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5016         }
5017         
5018         if (this.menu) {
5019             
5020             if(this.showArrow){
5021                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5022             }
5023             
5024             a.cls += ' dropdown-toggle treeview' ;
5025         }
5026         
5027         return cfg;
5028     },
5029     
5030     initEvents : function()
5031     { 
5032         if (typeof (this.menu) != 'undefined') {
5033             this.menu.parentType = this.xtype;
5034             this.menu.triggerEl = this.el;
5035             this.menu = this.addxtype(Roo.apply({}, this.menu));
5036         }
5037         
5038         this.el.on('click', this.onClick, this);
5039         
5040         if(this.badge !== ''){
5041             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5042         }
5043         
5044     },
5045     
5046     onClick : function(e)
5047     {
5048         if(this.disabled){
5049             e.preventDefault();
5050             return;
5051         }
5052         
5053         if(this.preventDefault){
5054             e.preventDefault();
5055         }
5056         
5057         this.fireEvent('click', this);
5058     },
5059     
5060     disable : function()
5061     {
5062         this.setDisabled(true);
5063     },
5064     
5065     enable : function()
5066     {
5067         this.setDisabled(false);
5068     },
5069     
5070     setDisabled : function(state)
5071     {
5072         if(this.disabled == state){
5073             return;
5074         }
5075         
5076         this.disabled = state;
5077         
5078         if (state) {
5079             this.el.addClass('disabled');
5080             return;
5081         }
5082         
5083         this.el.removeClass('disabled');
5084         
5085         return;
5086     },
5087     
5088     setActive : function(state)
5089     {
5090         if(this.active == state){
5091             return;
5092         }
5093         
5094         this.active = state;
5095         
5096         if (state) {
5097             this.el.addClass('active');
5098             return;
5099         }
5100         
5101         this.el.removeClass('active');
5102         
5103         return;
5104     },
5105     
5106     isActive: function () 
5107     {
5108         return this.active;
5109     },
5110     
5111     setBadge : function(str)
5112     {
5113         if(!this.badgeEl){
5114             return;
5115         }
5116         
5117         this.badgeEl.dom.innerHTML = str;
5118     }
5119     
5120    
5121      
5122  
5123 });
5124  
5125
5126  /*
5127  * - LGPL
5128  *
5129  * row
5130  * 
5131  */
5132
5133 /**
5134  * @class Roo.bootstrap.Row
5135  * @extends Roo.bootstrap.Component
5136  * Bootstrap Row class (contains columns...)
5137  * 
5138  * @constructor
5139  * Create a new Row
5140  * @param {Object} config The config object
5141  */
5142
5143 Roo.bootstrap.Row = function(config){
5144     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5145 };
5146
5147 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5148     
5149     getAutoCreate : function(){
5150        return {
5151             cls: 'row clearfix'
5152        };
5153     }
5154     
5155     
5156 });
5157
5158  
5159
5160  /*
5161  * - LGPL
5162  *
5163  * element
5164  * 
5165  */
5166
5167 /**
5168  * @class Roo.bootstrap.Element
5169  * @extends Roo.bootstrap.Component
5170  * Bootstrap Element class
5171  * @cfg {String} html contents of the element
5172  * @cfg {String} tag tag of the element
5173  * @cfg {String} cls class of the element
5174  * @cfg {Boolean} preventDefault (true|false) default false
5175  * @cfg {Boolean} clickable (true|false) default false
5176  * 
5177  * @constructor
5178  * Create a new Element
5179  * @param {Object} config The config object
5180  */
5181
5182 Roo.bootstrap.Element = function(config){
5183     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5184     
5185     this.addEvents({
5186         // raw events
5187         /**
5188          * @event click
5189          * When a element is chick
5190          * @param {Roo.bootstrap.Element} this
5191          * @param {Roo.EventObject} e
5192          */
5193         "click" : true
5194     });
5195 };
5196
5197 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5198     
5199     tag: 'div',
5200     cls: '',
5201     html: '',
5202     preventDefault: false, 
5203     clickable: false,
5204     
5205     getAutoCreate : function(){
5206         
5207         var cfg = {
5208             tag: this.tag,
5209             // cls: this.cls, double assign in parent class Component.js :: onRender
5210             html: this.html
5211         };
5212         
5213         return cfg;
5214     },
5215     
5216     initEvents: function() 
5217     {
5218         Roo.bootstrap.Element.superclass.initEvents.call(this);
5219         
5220         if(this.clickable){
5221             this.el.on('click', this.onClick, this);
5222         }
5223         
5224     },
5225     
5226     onClick : function(e)
5227     {
5228         if(this.preventDefault){
5229             e.preventDefault();
5230         }
5231         
5232         this.fireEvent('click', this, e);
5233     },
5234     
5235     getValue : function()
5236     {
5237         return this.el.dom.innerHTML;
5238     },
5239     
5240     setValue : function(value)
5241     {
5242         this.el.dom.innerHTML = value;
5243     }
5244    
5245 });
5246
5247  
5248
5249  /*
5250  * - LGPL
5251  *
5252  * pagination
5253  * 
5254  */
5255
5256 /**
5257  * @class Roo.bootstrap.Pagination
5258  * @extends Roo.bootstrap.Component
5259  * Bootstrap Pagination class
5260  * @cfg {String} size xs | sm | md | lg
5261  * @cfg {Boolean} inverse false | true
5262  * 
5263  * @constructor
5264  * Create a new Pagination
5265  * @param {Object} config The config object
5266  */
5267
5268 Roo.bootstrap.Pagination = function(config){
5269     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5270 };
5271
5272 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5273     
5274     cls: false,
5275     size: false,
5276     inverse: false,
5277     
5278     getAutoCreate : function(){
5279         var cfg = {
5280             tag: 'ul',
5281                 cls: 'pagination'
5282         };
5283         if (this.inverse) {
5284             cfg.cls += ' inverse';
5285         }
5286         if (this.html) {
5287             cfg.html=this.html;
5288         }
5289         if (this.cls) {
5290             cfg.cls += " " + this.cls;
5291         }
5292         return cfg;
5293     }
5294    
5295 });
5296
5297  
5298
5299  /*
5300  * - LGPL
5301  *
5302  * Pagination item
5303  * 
5304  */
5305
5306
5307 /**
5308  * @class Roo.bootstrap.PaginationItem
5309  * @extends Roo.bootstrap.Component
5310  * Bootstrap PaginationItem class
5311  * @cfg {String} html text
5312  * @cfg {String} href the link
5313  * @cfg {Boolean} preventDefault (true | false) default true
5314  * @cfg {Boolean} active (true | false) default false
5315  * @cfg {Boolean} disabled default false
5316  * 
5317  * 
5318  * @constructor
5319  * Create a new PaginationItem
5320  * @param {Object} config The config object
5321  */
5322
5323
5324 Roo.bootstrap.PaginationItem = function(config){
5325     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5326     this.addEvents({
5327         // raw events
5328         /**
5329          * @event click
5330          * The raw click event for the entire grid.
5331          * @param {Roo.EventObject} e
5332          */
5333         "click" : true
5334     });
5335 };
5336
5337 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5338     
5339     href : false,
5340     html : false,
5341     preventDefault: true,
5342     active : false,
5343     cls : false,
5344     disabled: false,
5345     
5346     getAutoCreate : function(){
5347         var cfg= {
5348             tag: 'li',
5349             cn: [
5350                 {
5351                     tag : 'a',
5352                     href : this.href ? this.href : '#',
5353                     html : this.html ? this.html : ''
5354                 }
5355             ]
5356         };
5357         
5358         if(this.cls){
5359             cfg.cls = this.cls;
5360         }
5361         
5362         if(this.disabled){
5363             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5364         }
5365         
5366         if(this.active){
5367             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5368         }
5369         
5370         return cfg;
5371     },
5372     
5373     initEvents: function() {
5374         
5375         this.el.on('click', this.onClick, this);
5376         
5377     },
5378     onClick : function(e)
5379     {
5380         Roo.log('PaginationItem on click ');
5381         if(this.preventDefault){
5382             e.preventDefault();
5383         }
5384         
5385         if(this.disabled){
5386             return;
5387         }
5388         
5389         this.fireEvent('click', this, e);
5390     }
5391    
5392 });
5393
5394  
5395
5396  /*
5397  * - LGPL
5398  *
5399  * slider
5400  * 
5401  */
5402
5403
5404 /**
5405  * @class Roo.bootstrap.Slider
5406  * @extends Roo.bootstrap.Component
5407  * Bootstrap Slider class
5408  *    
5409  * @constructor
5410  * Create a new Slider
5411  * @param {Object} config The config object
5412  */
5413
5414 Roo.bootstrap.Slider = function(config){
5415     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5416 };
5417
5418 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5419     
5420     getAutoCreate : function(){
5421         
5422         var cfg = {
5423             tag: 'div',
5424             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5425             cn: [
5426                 {
5427                     tag: 'a',
5428                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5429                 }
5430             ]
5431         };
5432         
5433         return cfg;
5434     }
5435    
5436 });
5437
5438  /*
5439  * Based on:
5440  * Ext JS Library 1.1.1
5441  * Copyright(c) 2006-2007, Ext JS, LLC.
5442  *
5443  * Originally Released Under LGPL - original licence link has changed is not relivant.
5444  *
5445  * Fork - LGPL
5446  * <script type="text/javascript">
5447  */
5448  
5449
5450 /**
5451  * @class Roo.grid.ColumnModel
5452  * @extends Roo.util.Observable
5453  * This is the default implementation of a ColumnModel used by the Grid. It defines
5454  * the columns in the grid.
5455  * <br>Usage:<br>
5456  <pre><code>
5457  var colModel = new Roo.grid.ColumnModel([
5458         {header: "Ticker", width: 60, sortable: true, locked: true},
5459         {header: "Company Name", width: 150, sortable: true},
5460         {header: "Market Cap.", width: 100, sortable: true},
5461         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5462         {header: "Employees", width: 100, sortable: true, resizable: false}
5463  ]);
5464  </code></pre>
5465  * <p>
5466  
5467  * The config options listed for this class are options which may appear in each
5468  * individual column definition.
5469  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5470  * @constructor
5471  * @param {Object} config An Array of column config objects. See this class's
5472  * config objects for details.
5473 */
5474 Roo.grid.ColumnModel = function(config){
5475         /**
5476      * The config passed into the constructor
5477      */
5478     this.config = config;
5479     this.lookup = {};
5480
5481     // if no id, create one
5482     // if the column does not have a dataIndex mapping,
5483     // map it to the order it is in the config
5484     for(var i = 0, len = config.length; i < len; i++){
5485         var c = config[i];
5486         if(typeof c.dataIndex == "undefined"){
5487             c.dataIndex = i;
5488         }
5489         if(typeof c.renderer == "string"){
5490             c.renderer = Roo.util.Format[c.renderer];
5491         }
5492         if(typeof c.id == "undefined"){
5493             c.id = Roo.id();
5494         }
5495         if(c.editor && c.editor.xtype){
5496             c.editor  = Roo.factory(c.editor, Roo.grid);
5497         }
5498         if(c.editor && c.editor.isFormField){
5499             c.editor = new Roo.grid.GridEditor(c.editor);
5500         }
5501         this.lookup[c.id] = c;
5502     }
5503
5504     /**
5505      * The width of columns which have no width specified (defaults to 100)
5506      * @type Number
5507      */
5508     this.defaultWidth = 100;
5509
5510     /**
5511      * Default sortable of columns which have no sortable specified (defaults to false)
5512      * @type Boolean
5513      */
5514     this.defaultSortable = false;
5515
5516     this.addEvents({
5517         /**
5518              * @event widthchange
5519              * Fires when the width of a column changes.
5520              * @param {ColumnModel} this
5521              * @param {Number} columnIndex The column index
5522              * @param {Number} newWidth The new width
5523              */
5524             "widthchange": true,
5525         /**
5526              * @event headerchange
5527              * Fires when the text of a header changes.
5528              * @param {ColumnModel} this
5529              * @param {Number} columnIndex The column index
5530              * @param {Number} newText The new header text
5531              */
5532             "headerchange": true,
5533         /**
5534              * @event hiddenchange
5535              * Fires when a column is hidden or "unhidden".
5536              * @param {ColumnModel} this
5537              * @param {Number} columnIndex The column index
5538              * @param {Boolean} hidden true if hidden, false otherwise
5539              */
5540             "hiddenchange": true,
5541             /**
5542          * @event columnmoved
5543          * Fires when a column is moved.
5544          * @param {ColumnModel} this
5545          * @param {Number} oldIndex
5546          * @param {Number} newIndex
5547          */
5548         "columnmoved" : true,
5549         /**
5550          * @event columlockchange
5551          * Fires when a column's locked state is changed
5552          * @param {ColumnModel} this
5553          * @param {Number} colIndex
5554          * @param {Boolean} locked true if locked
5555          */
5556         "columnlockchange" : true
5557     });
5558     Roo.grid.ColumnModel.superclass.constructor.call(this);
5559 };
5560 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5561     /**
5562      * @cfg {String} header The header text to display in the Grid view.
5563      */
5564     /**
5565      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5566      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5567      * specified, the column's index is used as an index into the Record's data Array.
5568      */
5569     /**
5570      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5571      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5572      */
5573     /**
5574      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5575      * Defaults to the value of the {@link #defaultSortable} property.
5576      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5577      */
5578     /**
5579      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5580      */
5581     /**
5582      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5583      */
5584     /**
5585      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5586      */
5587     /**
5588      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5589      */
5590     /**
5591      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5592      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5593      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5594      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5595      */
5596        /**
5597      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5598      */
5599     /**
5600      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5601      */
5602     /**
5603      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5604      */
5605     /**
5606      * @cfg {String} cursor (Optional)
5607      */
5608     /**
5609      * @cfg {String} tooltip (Optional)
5610      */
5611     /**
5612      * @cfg {Number} xs (Optional)
5613      */
5614     /**
5615      * @cfg {Number} sm (Optional)
5616      */
5617     /**
5618      * @cfg {Number} md (Optional)
5619      */
5620     /**
5621      * @cfg {Number} lg (Optional)
5622      */
5623     /**
5624      * Returns the id of the column at the specified index.
5625      * @param {Number} index The column index
5626      * @return {String} the id
5627      */
5628     getColumnId : function(index){
5629         return this.config[index].id;
5630     },
5631
5632     /**
5633      * Returns the column for a specified id.
5634      * @param {String} id The column id
5635      * @return {Object} the column
5636      */
5637     getColumnById : function(id){
5638         return this.lookup[id];
5639     },
5640
5641     
5642     /**
5643      * Returns the column for a specified dataIndex.
5644      * @param {String} dataIndex The column dataIndex
5645      * @return {Object|Boolean} the column or false if not found
5646      */
5647     getColumnByDataIndex: function(dataIndex){
5648         var index = this.findColumnIndex(dataIndex);
5649         return index > -1 ? this.config[index] : false;
5650     },
5651     
5652     /**
5653      * Returns the index for a specified column id.
5654      * @param {String} id The column id
5655      * @return {Number} the index, or -1 if not found
5656      */
5657     getIndexById : function(id){
5658         for(var i = 0, len = this.config.length; i < len; i++){
5659             if(this.config[i].id == id){
5660                 return i;
5661             }
5662         }
5663         return -1;
5664     },
5665     
5666     /**
5667      * Returns the index for a specified column dataIndex.
5668      * @param {String} dataIndex The column dataIndex
5669      * @return {Number} the index, or -1 if not found
5670      */
5671     
5672     findColumnIndex : function(dataIndex){
5673         for(var i = 0, len = this.config.length; i < len; i++){
5674             if(this.config[i].dataIndex == dataIndex){
5675                 return i;
5676             }
5677         }
5678         return -1;
5679     },
5680     
5681     
5682     moveColumn : function(oldIndex, newIndex){
5683         var c = this.config[oldIndex];
5684         this.config.splice(oldIndex, 1);
5685         this.config.splice(newIndex, 0, c);
5686         this.dataMap = null;
5687         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5688     },
5689
5690     isLocked : function(colIndex){
5691         return this.config[colIndex].locked === true;
5692     },
5693
5694     setLocked : function(colIndex, value, suppressEvent){
5695         if(this.isLocked(colIndex) == value){
5696             return;
5697         }
5698         this.config[colIndex].locked = value;
5699         if(!suppressEvent){
5700             this.fireEvent("columnlockchange", this, colIndex, value);
5701         }
5702     },
5703
5704     getTotalLockedWidth : function(){
5705         var totalWidth = 0;
5706         for(var i = 0; i < this.config.length; i++){
5707             if(this.isLocked(i) && !this.isHidden(i)){
5708                 this.totalWidth += this.getColumnWidth(i);
5709             }
5710         }
5711         return totalWidth;
5712     },
5713
5714     getLockedCount : function(){
5715         for(var i = 0, len = this.config.length; i < len; i++){
5716             if(!this.isLocked(i)){
5717                 return i;
5718             }
5719         }
5720         
5721         return this.config.length;
5722     },
5723
5724     /**
5725      * Returns the number of columns.
5726      * @return {Number}
5727      */
5728     getColumnCount : function(visibleOnly){
5729         if(visibleOnly === true){
5730             var c = 0;
5731             for(var i = 0, len = this.config.length; i < len; i++){
5732                 if(!this.isHidden(i)){
5733                     c++;
5734                 }
5735             }
5736             return c;
5737         }
5738         return this.config.length;
5739     },
5740
5741     /**
5742      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5743      * @param {Function} fn
5744      * @param {Object} scope (optional)
5745      * @return {Array} result
5746      */
5747     getColumnsBy : function(fn, scope){
5748         var r = [];
5749         for(var i = 0, len = this.config.length; i < len; i++){
5750             var c = this.config[i];
5751             if(fn.call(scope||this, c, i) === true){
5752                 r[r.length] = c;
5753             }
5754         }
5755         return r;
5756     },
5757
5758     /**
5759      * Returns true if the specified column is sortable.
5760      * @param {Number} col The column index
5761      * @return {Boolean}
5762      */
5763     isSortable : function(col){
5764         if(typeof this.config[col].sortable == "undefined"){
5765             return this.defaultSortable;
5766         }
5767         return this.config[col].sortable;
5768     },
5769
5770     /**
5771      * Returns the rendering (formatting) function defined for the column.
5772      * @param {Number} col The column index.
5773      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5774      */
5775     getRenderer : function(col){
5776         if(!this.config[col].renderer){
5777             return Roo.grid.ColumnModel.defaultRenderer;
5778         }
5779         return this.config[col].renderer;
5780     },
5781
5782     /**
5783      * Sets the rendering (formatting) function for a column.
5784      * @param {Number} col The column index
5785      * @param {Function} fn The function to use to process the cell's raw data
5786      * to return HTML markup for the grid view. The render function is called with
5787      * the following parameters:<ul>
5788      * <li>Data value.</li>
5789      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5790      * <li>css A CSS style string to apply to the table cell.</li>
5791      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5792      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5793      * <li>Row index</li>
5794      * <li>Column index</li>
5795      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5796      */
5797     setRenderer : function(col, fn){
5798         this.config[col].renderer = fn;
5799     },
5800
5801     /**
5802      * Returns the width for the specified column.
5803      * @param {Number} col The column index
5804      * @return {Number}
5805      */
5806     getColumnWidth : function(col){
5807         return this.config[col].width * 1 || this.defaultWidth;
5808     },
5809
5810     /**
5811      * Sets the width for a column.
5812      * @param {Number} col The column index
5813      * @param {Number} width The new width
5814      */
5815     setColumnWidth : function(col, width, suppressEvent){
5816         this.config[col].width = width;
5817         this.totalWidth = null;
5818         if(!suppressEvent){
5819              this.fireEvent("widthchange", this, col, width);
5820         }
5821     },
5822
5823     /**
5824      * Returns the total width of all columns.
5825      * @param {Boolean} includeHidden True to include hidden column widths
5826      * @return {Number}
5827      */
5828     getTotalWidth : function(includeHidden){
5829         if(!this.totalWidth){
5830             this.totalWidth = 0;
5831             for(var i = 0, len = this.config.length; i < len; i++){
5832                 if(includeHidden || !this.isHidden(i)){
5833                     this.totalWidth += this.getColumnWidth(i);
5834                 }
5835             }
5836         }
5837         return this.totalWidth;
5838     },
5839
5840     /**
5841      * Returns the header for the specified column.
5842      * @param {Number} col The column index
5843      * @return {String}
5844      */
5845     getColumnHeader : function(col){
5846         return this.config[col].header;
5847     },
5848
5849     /**
5850      * Sets the header for a column.
5851      * @param {Number} col The column index
5852      * @param {String} header The new header
5853      */
5854     setColumnHeader : function(col, header){
5855         this.config[col].header = header;
5856         this.fireEvent("headerchange", this, col, header);
5857     },
5858
5859     /**
5860      * Returns the tooltip for the specified column.
5861      * @param {Number} col The column index
5862      * @return {String}
5863      */
5864     getColumnTooltip : function(col){
5865             return this.config[col].tooltip;
5866     },
5867     /**
5868      * Sets the tooltip for a column.
5869      * @param {Number} col The column index
5870      * @param {String} tooltip The new tooltip
5871      */
5872     setColumnTooltip : function(col, tooltip){
5873             this.config[col].tooltip = tooltip;
5874     },
5875
5876     /**
5877      * Returns the dataIndex for the specified column.
5878      * @param {Number} col The column index
5879      * @return {Number}
5880      */
5881     getDataIndex : function(col){
5882         return this.config[col].dataIndex;
5883     },
5884
5885     /**
5886      * Sets the dataIndex for a column.
5887      * @param {Number} col The column index
5888      * @param {Number} dataIndex The new dataIndex
5889      */
5890     setDataIndex : function(col, dataIndex){
5891         this.config[col].dataIndex = dataIndex;
5892     },
5893
5894     
5895     
5896     /**
5897      * Returns true if the cell is editable.
5898      * @param {Number} colIndex The column index
5899      * @param {Number} rowIndex The row index - this is nto actually used..?
5900      * @return {Boolean}
5901      */
5902     isCellEditable : function(colIndex, rowIndex){
5903         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5904     },
5905
5906     /**
5907      * Returns the editor defined for the cell/column.
5908      * return false or null to disable editing.
5909      * @param {Number} colIndex The column index
5910      * @param {Number} rowIndex The row index
5911      * @return {Object}
5912      */
5913     getCellEditor : function(colIndex, rowIndex){
5914         return this.config[colIndex].editor;
5915     },
5916
5917     /**
5918      * Sets if a column is editable.
5919      * @param {Number} col The column index
5920      * @param {Boolean} editable True if the column is editable
5921      */
5922     setEditable : function(col, editable){
5923         this.config[col].editable = editable;
5924     },
5925
5926
5927     /**
5928      * Returns true if the column is hidden.
5929      * @param {Number} colIndex The column index
5930      * @return {Boolean}
5931      */
5932     isHidden : function(colIndex){
5933         return this.config[colIndex].hidden;
5934     },
5935
5936
5937     /**
5938      * Returns true if the column width cannot be changed
5939      */
5940     isFixed : function(colIndex){
5941         return this.config[colIndex].fixed;
5942     },
5943
5944     /**
5945      * Returns true if the column can be resized
5946      * @return {Boolean}
5947      */
5948     isResizable : function(colIndex){
5949         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5950     },
5951     /**
5952      * Sets if a column is hidden.
5953      * @param {Number} colIndex The column index
5954      * @param {Boolean} hidden True if the column is hidden
5955      */
5956     setHidden : function(colIndex, hidden){
5957         this.config[colIndex].hidden = hidden;
5958         this.totalWidth = null;
5959         this.fireEvent("hiddenchange", this, colIndex, hidden);
5960     },
5961
5962     /**
5963      * Sets the editor for a column.
5964      * @param {Number} col The column index
5965      * @param {Object} editor The editor object
5966      */
5967     setEditor : function(col, editor){
5968         this.config[col].editor = editor;
5969     }
5970 });
5971
5972 Roo.grid.ColumnModel.defaultRenderer = function(value)
5973 {
5974     if(typeof value == "object") {
5975         return value;
5976     }
5977         if(typeof value == "string" && value.length < 1){
5978             return "&#160;";
5979         }
5980     
5981         return String.format("{0}", value);
5982 };
5983
5984 // Alias for backwards compatibility
5985 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5986 /*
5987  * Based on:
5988  * Ext JS Library 1.1.1
5989  * Copyright(c) 2006-2007, Ext JS, LLC.
5990  *
5991  * Originally Released Under LGPL - original licence link has changed is not relivant.
5992  *
5993  * Fork - LGPL
5994  * <script type="text/javascript">
5995  */
5996  
5997 /**
5998  * @class Roo.LoadMask
5999  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6000  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6001  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6002  * element's UpdateManager load indicator and will be destroyed after the initial load.
6003  * @constructor
6004  * Create a new LoadMask
6005  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6006  * @param {Object} config The config object
6007  */
6008 Roo.LoadMask = function(el, config){
6009     this.el = Roo.get(el);
6010     Roo.apply(this, config);
6011     if(this.store){
6012         this.store.on('beforeload', this.onBeforeLoad, this);
6013         this.store.on('load', this.onLoad, this);
6014         this.store.on('loadexception', this.onLoadException, this);
6015         this.removeMask = false;
6016     }else{
6017         var um = this.el.getUpdateManager();
6018         um.showLoadIndicator = false; // disable the default indicator
6019         um.on('beforeupdate', this.onBeforeLoad, this);
6020         um.on('update', this.onLoad, this);
6021         um.on('failure', this.onLoad, this);
6022         this.removeMask = true;
6023     }
6024 };
6025
6026 Roo.LoadMask.prototype = {
6027     /**
6028      * @cfg {Boolean} removeMask
6029      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6030      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6031      */
6032     /**
6033      * @cfg {String} msg
6034      * The text to display in a centered loading message box (defaults to 'Loading...')
6035      */
6036     msg : 'Loading...',
6037     /**
6038      * @cfg {String} msgCls
6039      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6040      */
6041     msgCls : 'x-mask-loading',
6042
6043     /**
6044      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6045      * @type Boolean
6046      */
6047     disabled: false,
6048
6049     /**
6050      * Disables the mask to prevent it from being displayed
6051      */
6052     disable : function(){
6053        this.disabled = true;
6054     },
6055
6056     /**
6057      * Enables the mask so that it can be displayed
6058      */
6059     enable : function(){
6060         this.disabled = false;
6061     },
6062     
6063     onLoadException : function()
6064     {
6065         Roo.log(arguments);
6066         
6067         if (typeof(arguments[3]) != 'undefined') {
6068             Roo.MessageBox.alert("Error loading",arguments[3]);
6069         } 
6070         /*
6071         try {
6072             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6073                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6074             }   
6075         } catch(e) {
6076             
6077         }
6078         */
6079     
6080         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6081     },
6082     // private
6083     onLoad : function()
6084     {
6085         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6086     },
6087
6088     // private
6089     onBeforeLoad : function(){
6090         if(!this.disabled){
6091             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6092         }
6093     },
6094
6095     // private
6096     destroy : function(){
6097         if(this.store){
6098             this.store.un('beforeload', this.onBeforeLoad, this);
6099             this.store.un('load', this.onLoad, this);
6100             this.store.un('loadexception', this.onLoadException, this);
6101         }else{
6102             var um = this.el.getUpdateManager();
6103             um.un('beforeupdate', this.onBeforeLoad, this);
6104             um.un('update', this.onLoad, this);
6105             um.un('failure', this.onLoad, this);
6106         }
6107     }
6108 };/*
6109  * - LGPL
6110  *
6111  * table
6112  * 
6113  */
6114
6115 /**
6116  * @class Roo.bootstrap.Table
6117  * @extends Roo.bootstrap.Component
6118  * Bootstrap Table class
6119  * @cfg {String} cls table class
6120  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6121  * @cfg {String} bgcolor Specifies the background color for a table
6122  * @cfg {Number} border Specifies whether the table cells should have borders or not
6123  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6124  * @cfg {Number} cellspacing Specifies the space between cells
6125  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6126  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6127  * @cfg {String} sortable Specifies that the table should be sortable
6128  * @cfg {String} summary Specifies a summary of the content of a table
6129  * @cfg {Number} width Specifies the width of a table
6130  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6131  * 
6132  * @cfg {boolean} striped Should the rows be alternative striped
6133  * @cfg {boolean} bordered Add borders to the table
6134  * @cfg {boolean} hover Add hover highlighting
6135  * @cfg {boolean} condensed Format condensed
6136  * @cfg {boolean} responsive Format condensed
6137  * @cfg {Boolean} loadMask (true|false) default false
6138  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6139  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6140  * @cfg {Boolean} rowSelection (true|false) default false
6141  * @cfg {Boolean} cellSelection (true|false) default false
6142  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6143  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6144  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6145  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6146  
6147  * 
6148  * @constructor
6149  * Create a new Table
6150  * @param {Object} config The config object
6151  */
6152
6153 Roo.bootstrap.Table = function(config){
6154     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6155     
6156   
6157     
6158     // BC...
6159     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6160     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6161     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6162     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6163     
6164     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6165     if (this.sm) {
6166         this.sm.grid = this;
6167         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6168         this.sm = this.selModel;
6169         this.sm.xmodule = this.xmodule || false;
6170     }
6171     
6172     if (this.cm && typeof(this.cm.config) == 'undefined') {
6173         this.colModel = new Roo.grid.ColumnModel(this.cm);
6174         this.cm = this.colModel;
6175         this.cm.xmodule = this.xmodule || false;
6176     }
6177     if (this.store) {
6178         this.store= Roo.factory(this.store, Roo.data);
6179         this.ds = this.store;
6180         this.ds.xmodule = this.xmodule || false;
6181          
6182     }
6183     if (this.footer && this.store) {
6184         this.footer.dataSource = this.ds;
6185         this.footer = Roo.factory(this.footer);
6186     }
6187     
6188     /** @private */
6189     this.addEvents({
6190         /**
6191          * @event cellclick
6192          * Fires when a cell is clicked
6193          * @param {Roo.bootstrap.Table} this
6194          * @param {Roo.Element} el
6195          * @param {Number} rowIndex
6196          * @param {Number} columnIndex
6197          * @param {Roo.EventObject} e
6198          */
6199         "cellclick" : true,
6200         /**
6201          * @event celldblclick
6202          * Fires when a cell is double clicked
6203          * @param {Roo.bootstrap.Table} this
6204          * @param {Roo.Element} el
6205          * @param {Number} rowIndex
6206          * @param {Number} columnIndex
6207          * @param {Roo.EventObject} e
6208          */
6209         "celldblclick" : true,
6210         /**
6211          * @event rowclick
6212          * Fires when a row is clicked
6213          * @param {Roo.bootstrap.Table} this
6214          * @param {Roo.Element} el
6215          * @param {Number} rowIndex
6216          * @param {Roo.EventObject} e
6217          */
6218         "rowclick" : true,
6219         /**
6220          * @event rowdblclick
6221          * Fires when a row is double clicked
6222          * @param {Roo.bootstrap.Table} this
6223          * @param {Roo.Element} el
6224          * @param {Number} rowIndex
6225          * @param {Roo.EventObject} e
6226          */
6227         "rowdblclick" : true,
6228         /**
6229          * @event mouseover
6230          * Fires when a mouseover occur
6231          * @param {Roo.bootstrap.Table} this
6232          * @param {Roo.Element} el
6233          * @param {Number} rowIndex
6234          * @param {Number} columnIndex
6235          * @param {Roo.EventObject} e
6236          */
6237         "mouseover" : true,
6238         /**
6239          * @event mouseout
6240          * Fires when a mouseout occur
6241          * @param {Roo.bootstrap.Table} this
6242          * @param {Roo.Element} el
6243          * @param {Number} rowIndex
6244          * @param {Number} columnIndex
6245          * @param {Roo.EventObject} e
6246          */
6247         "mouseout" : true,
6248         /**
6249          * @event rowclass
6250          * Fires when a row is rendered, so you can change add a style to it.
6251          * @param {Roo.bootstrap.Table} this
6252          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6253          */
6254         'rowclass' : true,
6255           /**
6256          * @event rowsrendered
6257          * Fires when all the  rows have been rendered
6258          * @param {Roo.bootstrap.Table} this
6259          */
6260         'rowsrendered' : true,
6261         /**
6262          * @event contextmenu
6263          * The raw contextmenu event for the entire grid.
6264          * @param {Roo.EventObject} e
6265          */
6266         "contextmenu" : true,
6267         /**
6268          * @event rowcontextmenu
6269          * Fires when a row is right clicked
6270          * @param {Roo.bootstrap.Table} this
6271          * @param {Number} rowIndex
6272          * @param {Roo.EventObject} e
6273          */
6274         "rowcontextmenu" : true,
6275         /**
6276          * @event cellcontextmenu
6277          * Fires when a cell is right clicked
6278          * @param {Roo.bootstrap.Table} this
6279          * @param {Number} rowIndex
6280          * @param {Number} cellIndex
6281          * @param {Roo.EventObject} e
6282          */
6283          "cellcontextmenu" : true,
6284          /**
6285          * @event headercontextmenu
6286          * Fires when a header is right clicked
6287          * @param {Roo.bootstrap.Table} this
6288          * @param {Number} columnIndex
6289          * @param {Roo.EventObject} e
6290          */
6291         "headercontextmenu" : true
6292     });
6293 };
6294
6295 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6296     
6297     cls: false,
6298     align: false,
6299     bgcolor: false,
6300     border: false,
6301     cellpadding: false,
6302     cellspacing: false,
6303     frame: false,
6304     rules: false,
6305     sortable: false,
6306     summary: false,
6307     width: false,
6308     striped : false,
6309     scrollBody : false,
6310     bordered: false,
6311     hover:  false,
6312     condensed : false,
6313     responsive : false,
6314     sm : false,
6315     cm : false,
6316     store : false,
6317     loadMask : false,
6318     footerShow : true,
6319     headerShow : true,
6320   
6321     rowSelection : false,
6322     cellSelection : false,
6323     layout : false,
6324     
6325     // Roo.Element - the tbody
6326     mainBody: false,
6327     // Roo.Element - thead element
6328     mainHead: false,
6329     
6330     container: false, // used by gridpanel...
6331     
6332     lazyLoad : false,
6333     
6334     CSS : Roo.util.CSS,
6335     
6336     auto_hide_footer : false,
6337     
6338     getAutoCreate : function()
6339     {
6340         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6341         
6342         cfg = {
6343             tag: 'table',
6344             cls : 'table',
6345             cn : []
6346         };
6347         if (this.scrollBody) {
6348             cfg.cls += ' table-body-fixed';
6349         }    
6350         if (this.striped) {
6351             cfg.cls += ' table-striped';
6352         }
6353         
6354         if (this.hover) {
6355             cfg.cls += ' table-hover';
6356         }
6357         if (this.bordered) {
6358             cfg.cls += ' table-bordered';
6359         }
6360         if (this.condensed) {
6361             cfg.cls += ' table-condensed';
6362         }
6363         if (this.responsive) {
6364             cfg.cls += ' table-responsive';
6365         }
6366         
6367         if (this.cls) {
6368             cfg.cls+=  ' ' +this.cls;
6369         }
6370         
6371         // this lot should be simplifed...
6372         var _t = this;
6373         var cp = [
6374             'align',
6375             'bgcolor',
6376             'border',
6377             'cellpadding',
6378             'cellspacing',
6379             'frame',
6380             'rules',
6381             'sortable',
6382             'summary',
6383             'width'
6384         ].forEach(function(k) {
6385             if (_t[k]) {
6386                 cfg[k] = _t[k];
6387             }
6388         });
6389         
6390         
6391         if (this.layout) {
6392             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6393         }
6394         
6395         if(this.store || this.cm){
6396             if(this.headerShow){
6397                 cfg.cn.push(this.renderHeader());
6398             }
6399             
6400             cfg.cn.push(this.renderBody());
6401             
6402             if(this.footerShow){
6403                 cfg.cn.push(this.renderFooter());
6404             }
6405             // where does this come from?
6406             //cfg.cls+=  ' TableGrid';
6407         }
6408         
6409         return { cn : [ cfg ] };
6410     },
6411     
6412     initEvents : function()
6413     {   
6414         if(!this.store || !this.cm){
6415             return;
6416         }
6417         if (this.selModel) {
6418             this.selModel.initEvents();
6419         }
6420         
6421         
6422         //Roo.log('initEvents with ds!!!!');
6423         
6424         this.mainBody = this.el.select('tbody', true).first();
6425         this.mainHead = this.el.select('thead', true).first();
6426         this.mainFoot = this.el.select('tfoot', true).first();
6427         
6428         
6429         
6430         var _this = this;
6431         
6432         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6433             e.on('click', _this.sort, _this);
6434         });
6435         
6436         this.mainBody.on("click", this.onClick, this);
6437         this.mainBody.on("dblclick", this.onDblClick, this);
6438         
6439         // why is this done????? = it breaks dialogs??
6440         //this.parent().el.setStyle('position', 'relative');
6441         
6442         
6443         if (this.footer) {
6444             this.footer.parentId = this.id;
6445             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6446             
6447             if(this.lazyLoad){
6448                 this.el.select('tfoot tr td').first().addClass('hide');
6449             }
6450         } 
6451         
6452         if(this.loadMask) {
6453             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6454         }
6455         
6456         this.store.on('load', this.onLoad, this);
6457         this.store.on('beforeload', this.onBeforeLoad, this);
6458         this.store.on('update', this.onUpdate, this);
6459         this.store.on('add', this.onAdd, this);
6460         this.store.on("clear", this.clear, this);
6461         
6462         this.el.on("contextmenu", this.onContextMenu, this);
6463         
6464         this.mainBody.on('scroll', this.onBodyScroll, this);
6465         
6466         this.cm.on("headerchange", this.onHeaderChange, this);
6467         
6468         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6469         
6470     },
6471     
6472     onContextMenu : function(e, t)
6473     {
6474         this.processEvent("contextmenu", e);
6475     },
6476     
6477     processEvent : function(name, e)
6478     {
6479         if (name != 'touchstart' ) {
6480             this.fireEvent(name, e);    
6481         }
6482         
6483         var t = e.getTarget();
6484         
6485         var cell = Roo.get(t);
6486         
6487         if(!cell){
6488             return;
6489         }
6490         
6491         if(cell.findParent('tfoot', false, true)){
6492             return;
6493         }
6494         
6495         if(cell.findParent('thead', false, true)){
6496             
6497             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6498                 cell = Roo.get(t).findParent('th', false, true);
6499                 if (!cell) {
6500                     Roo.log("failed to find th in thead?");
6501                     Roo.log(e.getTarget());
6502                     return;
6503                 }
6504             }
6505             
6506             var cellIndex = cell.dom.cellIndex;
6507             
6508             var ename = name == 'touchstart' ? 'click' : name;
6509             this.fireEvent("header" + ename, this, cellIndex, e);
6510             
6511             return;
6512         }
6513         
6514         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6515             cell = Roo.get(t).findParent('td', false, true);
6516             if (!cell) {
6517                 Roo.log("failed to find th in tbody?");
6518                 Roo.log(e.getTarget());
6519                 return;
6520             }
6521         }
6522         
6523         var row = cell.findParent('tr', false, true);
6524         var cellIndex = cell.dom.cellIndex;
6525         var rowIndex = row.dom.rowIndex - 1;
6526         
6527         if(row !== false){
6528             
6529             this.fireEvent("row" + name, this, rowIndex, e);
6530             
6531             if(cell !== false){
6532             
6533                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6534             }
6535         }
6536         
6537     },
6538     
6539     onMouseover : function(e, el)
6540     {
6541         var cell = Roo.get(el);
6542         
6543         if(!cell){
6544             return;
6545         }
6546         
6547         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6548             cell = cell.findParent('td', false, true);
6549         }
6550         
6551         var row = cell.findParent('tr', false, true);
6552         var cellIndex = cell.dom.cellIndex;
6553         var rowIndex = row.dom.rowIndex - 1; // start from 0
6554         
6555         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6556         
6557     },
6558     
6559     onMouseout : function(e, el)
6560     {
6561         var cell = Roo.get(el);
6562         
6563         if(!cell){
6564             return;
6565         }
6566         
6567         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6568             cell = cell.findParent('td', false, true);
6569         }
6570         
6571         var row = cell.findParent('tr', false, true);
6572         var cellIndex = cell.dom.cellIndex;
6573         var rowIndex = row.dom.rowIndex - 1; // start from 0
6574         
6575         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6576         
6577     },
6578     
6579     onClick : function(e, el)
6580     {
6581         var cell = Roo.get(el);
6582         
6583         if(!cell || (!this.cellSelection && !this.rowSelection)){
6584             return;
6585         }
6586         
6587         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6588             cell = cell.findParent('td', false, true);
6589         }
6590         
6591         if(!cell || typeof(cell) == 'undefined'){
6592             return;
6593         }
6594         
6595         var row = cell.findParent('tr', false, true);
6596         
6597         if(!row || typeof(row) == 'undefined'){
6598             return;
6599         }
6600         
6601         var cellIndex = cell.dom.cellIndex;
6602         var rowIndex = this.getRowIndex(row);
6603         
6604         // why??? - should these not be based on SelectionModel?
6605         if(this.cellSelection){
6606             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6607         }
6608         
6609         if(this.rowSelection){
6610             this.fireEvent('rowclick', this, row, rowIndex, e);
6611         }
6612         
6613         
6614     },
6615         
6616     onDblClick : function(e,el)
6617     {
6618         var cell = Roo.get(el);
6619         
6620         if(!cell || (!this.cellSelection && !this.rowSelection)){
6621             return;
6622         }
6623         
6624         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6625             cell = cell.findParent('td', false, true);
6626         }
6627         
6628         if(!cell || typeof(cell) == 'undefined'){
6629             return;
6630         }
6631         
6632         var row = cell.findParent('tr', false, true);
6633         
6634         if(!row || typeof(row) == 'undefined'){
6635             return;
6636         }
6637         
6638         var cellIndex = cell.dom.cellIndex;
6639         var rowIndex = this.getRowIndex(row);
6640         
6641         if(this.cellSelection){
6642             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6643         }
6644         
6645         if(this.rowSelection){
6646             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6647         }
6648     },
6649     
6650     sort : function(e,el)
6651     {
6652         var col = Roo.get(el);
6653         
6654         if(!col.hasClass('sortable')){
6655             return;
6656         }
6657         
6658         var sort = col.attr('sort');
6659         var dir = 'ASC';
6660         
6661         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6662             dir = 'DESC';
6663         }
6664         
6665         this.store.sortInfo = {field : sort, direction : dir};
6666         
6667         if (this.footer) {
6668             Roo.log("calling footer first");
6669             this.footer.onClick('first');
6670         } else {
6671         
6672             this.store.load({ params : { start : 0 } });
6673         }
6674     },
6675     
6676     renderHeader : function()
6677     {
6678         var header = {
6679             tag: 'thead',
6680             cn : []
6681         };
6682         
6683         var cm = this.cm;
6684         this.totalWidth = 0;
6685         
6686         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6687             
6688             var config = cm.config[i];
6689             
6690             var c = {
6691                 tag: 'th',
6692                 cls : 'x-hcol-' + i,
6693                 style : '',
6694                 html: cm.getColumnHeader(i)
6695             };
6696             
6697             var hh = '';
6698             
6699             if(typeof(config.sortable) != 'undefined' && config.sortable){
6700                 c.cls = 'sortable';
6701                 c.html = '<i class="glyphicon"></i>' + c.html;
6702             }
6703             
6704             if(typeof(config.lgHeader) != 'undefined'){
6705                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6706             }
6707             
6708             if(typeof(config.mdHeader) != 'undefined'){
6709                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6710             }
6711             
6712             if(typeof(config.smHeader) != 'undefined'){
6713                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6714             }
6715             
6716             if(typeof(config.xsHeader) != 'undefined'){
6717                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6718             }
6719             
6720             if(hh.length){
6721                 c.html = hh;
6722             }
6723             
6724             if(typeof(config.tooltip) != 'undefined'){
6725                 c.tooltip = config.tooltip;
6726             }
6727             
6728             if(typeof(config.colspan) != 'undefined'){
6729                 c.colspan = config.colspan;
6730             }
6731             
6732             if(typeof(config.hidden) != 'undefined' && config.hidden){
6733                 c.style += ' display:none;';
6734             }
6735             
6736             if(typeof(config.dataIndex) != 'undefined'){
6737                 c.sort = config.dataIndex;
6738             }
6739             
6740            
6741             
6742             if(typeof(config.align) != 'undefined' && config.align.length){
6743                 c.style += ' text-align:' + config.align + ';';
6744             }
6745             
6746             if(typeof(config.width) != 'undefined'){
6747                 c.style += ' width:' + config.width + 'px;';
6748                 this.totalWidth += config.width;
6749             } else {
6750                 this.totalWidth += 100; // assume minimum of 100 per column?
6751             }
6752             
6753             if(typeof(config.cls) != 'undefined'){
6754                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6755             }
6756             
6757             ['xs','sm','md','lg'].map(function(size){
6758                 
6759                 if(typeof(config[size]) == 'undefined'){
6760                     return;
6761                 }
6762                 
6763                 if (!config[size]) { // 0 = hidden
6764                     c.cls += ' hidden-' + size;
6765                     return;
6766                 }
6767                 
6768                 c.cls += ' col-' + size + '-' + config[size];
6769
6770             });
6771             
6772             header.cn.push(c)
6773         }
6774         
6775         return header;
6776     },
6777     
6778     renderBody : function()
6779     {
6780         var body = {
6781             tag: 'tbody',
6782             cn : [
6783                 {
6784                     tag: 'tr',
6785                     cn : [
6786                         {
6787                             tag : 'td',
6788                             colspan :  this.cm.getColumnCount()
6789                         }
6790                     ]
6791                 }
6792             ]
6793         };
6794         
6795         return body;
6796     },
6797     
6798     renderFooter : function()
6799     {
6800         var footer = {
6801             tag: 'tfoot',
6802             cn : [
6803                 {
6804                     tag: 'tr',
6805                     cn : [
6806                         {
6807                             tag : 'td',
6808                             colspan :  this.cm.getColumnCount()
6809                         }
6810                     ]
6811                 }
6812             ]
6813         };
6814         
6815         return footer;
6816     },
6817     
6818     
6819     
6820     onLoad : function()
6821     {
6822 //        Roo.log('ds onload');
6823         this.clear();
6824         
6825         var _this = this;
6826         var cm = this.cm;
6827         var ds = this.store;
6828         
6829         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6830             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6831             if (_this.store.sortInfo) {
6832                     
6833                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6834                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6835                 }
6836                 
6837                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6838                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6839                 }
6840             }
6841         });
6842         
6843         var tbody =  this.mainBody;
6844               
6845         if(ds.getCount() > 0){
6846             ds.data.each(function(d,rowIndex){
6847                 var row =  this.renderRow(cm, ds, rowIndex);
6848                 
6849                 tbody.createChild(row);
6850                 
6851                 var _this = this;
6852                 
6853                 if(row.cellObjects.length){
6854                     Roo.each(row.cellObjects, function(r){
6855                         _this.renderCellObject(r);
6856                     })
6857                 }
6858                 
6859             }, this);
6860         }
6861         
6862         var tfoot = this.el.select('tfoot', true).first();
6863         
6864         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6865             
6866             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6867             
6868             var total = this.ds.getTotalCount();
6869             
6870             if(this.footer.pageSize < total){
6871                 this.mainFoot.show();
6872             }
6873         }
6874         
6875         Roo.each(this.el.select('tbody td', true).elements, function(e){
6876             e.on('mouseover', _this.onMouseover, _this);
6877         });
6878         
6879         Roo.each(this.el.select('tbody td', true).elements, function(e){
6880             e.on('mouseout', _this.onMouseout, _this);
6881         });
6882         this.fireEvent('rowsrendered', this);
6883         
6884         this.autoSize();
6885     },
6886     
6887     
6888     onUpdate : function(ds,record)
6889     {
6890         this.refreshRow(record);
6891         this.autoSize();
6892     },
6893     
6894     onRemove : function(ds, record, index, isUpdate){
6895         if(isUpdate !== true){
6896             this.fireEvent("beforerowremoved", this, index, record);
6897         }
6898         var bt = this.mainBody.dom;
6899         
6900         var rows = this.el.select('tbody > tr', true).elements;
6901         
6902         if(typeof(rows[index]) != 'undefined'){
6903             bt.removeChild(rows[index].dom);
6904         }
6905         
6906 //        if(bt.rows[index]){
6907 //            bt.removeChild(bt.rows[index]);
6908 //        }
6909         
6910         if(isUpdate !== true){
6911             //this.stripeRows(index);
6912             //this.syncRowHeights(index, index);
6913             //this.layout();
6914             this.fireEvent("rowremoved", this, index, record);
6915         }
6916     },
6917     
6918     onAdd : function(ds, records, rowIndex)
6919     {
6920         //Roo.log('on Add called');
6921         // - note this does not handle multiple adding very well..
6922         var bt = this.mainBody.dom;
6923         for (var i =0 ; i < records.length;i++) {
6924             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6925             //Roo.log(records[i]);
6926             //Roo.log(this.store.getAt(rowIndex+i));
6927             this.insertRow(this.store, rowIndex + i, false);
6928             return;
6929         }
6930         
6931     },
6932     
6933     
6934     refreshRow : function(record){
6935         var ds = this.store, index;
6936         if(typeof record == 'number'){
6937             index = record;
6938             record = ds.getAt(index);
6939         }else{
6940             index = ds.indexOf(record);
6941         }
6942         this.insertRow(ds, index, true);
6943         this.autoSize();
6944         this.onRemove(ds, record, index+1, true);
6945         this.autoSize();
6946         //this.syncRowHeights(index, index);
6947         //this.layout();
6948         this.fireEvent("rowupdated", this, index, record);
6949     },
6950     
6951     insertRow : function(dm, rowIndex, isUpdate){
6952         
6953         if(!isUpdate){
6954             this.fireEvent("beforerowsinserted", this, rowIndex);
6955         }
6956             //var s = this.getScrollState();
6957         var row = this.renderRow(this.cm, this.store, rowIndex);
6958         // insert before rowIndex..
6959         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6960         
6961         var _this = this;
6962                 
6963         if(row.cellObjects.length){
6964             Roo.each(row.cellObjects, function(r){
6965                 _this.renderCellObject(r);
6966             })
6967         }
6968             
6969         if(!isUpdate){
6970             this.fireEvent("rowsinserted", this, rowIndex);
6971             //this.syncRowHeights(firstRow, lastRow);
6972             //this.stripeRows(firstRow);
6973             //this.layout();
6974         }
6975         
6976     },
6977     
6978     
6979     getRowDom : function(rowIndex)
6980     {
6981         var rows = this.el.select('tbody > tr', true).elements;
6982         
6983         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6984         
6985     },
6986     // returns the object tree for a tr..
6987   
6988     
6989     renderRow : function(cm, ds, rowIndex) 
6990     {
6991         var d = ds.getAt(rowIndex);
6992         
6993         var row = {
6994             tag : 'tr',
6995             cls : 'x-row-' + rowIndex,
6996             cn : []
6997         };
6998             
6999         var cellObjects = [];
7000         
7001         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7002             var config = cm.config[i];
7003             
7004             var renderer = cm.getRenderer(i);
7005             var value = '';
7006             var id = false;
7007             
7008             if(typeof(renderer) !== 'undefined'){
7009                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7010             }
7011             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7012             // and are rendered into the cells after the row is rendered - using the id for the element.
7013             
7014             if(typeof(value) === 'object'){
7015                 id = Roo.id();
7016                 cellObjects.push({
7017                     container : id,
7018                     cfg : value 
7019                 })
7020             }
7021             
7022             var rowcfg = {
7023                 record: d,
7024                 rowIndex : rowIndex,
7025                 colIndex : i,
7026                 rowClass : ''
7027             };
7028
7029             this.fireEvent('rowclass', this, rowcfg);
7030             
7031             var td = {
7032                 tag: 'td',
7033                 cls : rowcfg.rowClass + ' x-col-' + i,
7034                 style: '',
7035                 html: (typeof(value) === 'object') ? '' : value
7036             };
7037             
7038             if (id) {
7039                 td.id = id;
7040             }
7041             
7042             if(typeof(config.colspan) != 'undefined'){
7043                 td.colspan = config.colspan;
7044             }
7045             
7046             if(typeof(config.hidden) != 'undefined' && config.hidden){
7047                 td.style += ' display:none;';
7048             }
7049             
7050             if(typeof(config.align) != 'undefined' && config.align.length){
7051                 td.style += ' text-align:' + config.align + ';';
7052             }
7053             if(typeof(config.valign) != 'undefined' && config.valign.length){
7054                 td.style += ' vertical-align:' + config.valign + ';';
7055             }
7056             
7057             if(typeof(config.width) != 'undefined'){
7058                 td.style += ' width:' +  config.width + 'px;';
7059             }
7060             
7061             if(typeof(config.cursor) != 'undefined'){
7062                 td.style += ' cursor:' +  config.cursor + ';';
7063             }
7064             
7065             if(typeof(config.cls) != 'undefined'){
7066                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7067             }
7068             
7069             ['xs','sm','md','lg'].map(function(size){
7070                 
7071                 if(typeof(config[size]) == 'undefined'){
7072                     return;
7073                 }
7074                 
7075                 if (!config[size]) { // 0 = hidden
7076                     td.cls += ' hidden-' + size;
7077                     return;
7078                 }
7079                 
7080                 td.cls += ' col-' + size + '-' + config[size];
7081
7082             });
7083             
7084             row.cn.push(td);
7085            
7086         }
7087         
7088         row.cellObjects = cellObjects;
7089         
7090         return row;
7091           
7092     },
7093     
7094     
7095     
7096     onBeforeLoad : function()
7097     {
7098         
7099     },
7100      /**
7101      * Remove all rows
7102      */
7103     clear : function()
7104     {
7105         this.el.select('tbody', true).first().dom.innerHTML = '';
7106     },
7107     /**
7108      * Show or hide a row.
7109      * @param {Number} rowIndex to show or hide
7110      * @param {Boolean} state hide
7111      */
7112     setRowVisibility : function(rowIndex, state)
7113     {
7114         var bt = this.mainBody.dom;
7115         
7116         var rows = this.el.select('tbody > tr', true).elements;
7117         
7118         if(typeof(rows[rowIndex]) == 'undefined'){
7119             return;
7120         }
7121         rows[rowIndex].dom.style.display = state ? '' : 'none';
7122     },
7123     
7124     
7125     getSelectionModel : function(){
7126         if(!this.selModel){
7127             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7128         }
7129         return this.selModel;
7130     },
7131     /*
7132      * Render the Roo.bootstrap object from renderder
7133      */
7134     renderCellObject : function(r)
7135     {
7136         var _this = this;
7137         
7138         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7139         
7140         var t = r.cfg.render(r.container);
7141         
7142         if(r.cfg.cn){
7143             Roo.each(r.cfg.cn, function(c){
7144                 var child = {
7145                     container: t.getChildContainer(),
7146                     cfg: c
7147                 };
7148                 _this.renderCellObject(child);
7149             })
7150         }
7151     },
7152     
7153     getRowIndex : function(row)
7154     {
7155         var rowIndex = -1;
7156         
7157         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7158             if(el != row){
7159                 return;
7160             }
7161             
7162             rowIndex = index;
7163         });
7164         
7165         return rowIndex;
7166     },
7167      /**
7168      * Returns the grid's underlying element = used by panel.Grid
7169      * @return {Element} The element
7170      */
7171     getGridEl : function(){
7172         return this.el;
7173     },
7174      /**
7175      * Forces a resize - used by panel.Grid
7176      * @return {Element} The element
7177      */
7178     autoSize : function()
7179     {
7180         //var ctr = Roo.get(this.container.dom.parentElement);
7181         var ctr = Roo.get(this.el.dom);
7182         
7183         var thd = this.getGridEl().select('thead',true).first();
7184         var tbd = this.getGridEl().select('tbody', true).first();
7185         var tfd = this.getGridEl().select('tfoot', true).first();
7186         
7187         var cw = ctr.getWidth();
7188         
7189         if (tbd) {
7190             
7191             tbd.setSize(ctr.getWidth(),
7192                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7193             );
7194             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7195             cw -= barsize;
7196         }
7197         cw = Math.max(cw, this.totalWidth);
7198         this.getGridEl().select('tr',true).setWidth(cw);
7199         // resize 'expandable coloumn?
7200         
7201         return; // we doe not have a view in this design..
7202         
7203     },
7204     onBodyScroll: function()
7205     {
7206         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7207         if(this.mainHead){
7208             this.mainHead.setStyle({
7209                 'position' : 'relative',
7210                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7211             });
7212         }
7213         
7214         if(this.lazyLoad){
7215             
7216             var scrollHeight = this.mainBody.dom.scrollHeight;
7217             
7218             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7219             
7220             var height = this.mainBody.getHeight();
7221             
7222             if(scrollHeight - height == scrollTop) {
7223                 
7224                 var total = this.ds.getTotalCount();
7225                 
7226                 if(this.footer.cursor + this.footer.pageSize < total){
7227                     
7228                     this.footer.ds.load({
7229                         params : {
7230                             start : this.footer.cursor + this.footer.pageSize,
7231                             limit : this.footer.pageSize
7232                         },
7233                         add : true
7234                     });
7235                 }
7236             }
7237             
7238         }
7239     },
7240     
7241     onHeaderChange : function()
7242     {
7243         var header = this.renderHeader();
7244         var table = this.el.select('table', true).first();
7245         
7246         this.mainHead.remove();
7247         this.mainHead = table.createChild(header, this.mainBody, false);
7248     },
7249     
7250     onHiddenChange : function(colModel, colIndex, hidden)
7251     {
7252         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7253         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7254         
7255         this.CSS.updateRule(thSelector, "display", "");
7256         this.CSS.updateRule(tdSelector, "display", "");
7257         
7258         if(hidden){
7259             this.CSS.updateRule(thSelector, "display", "none");
7260             this.CSS.updateRule(tdSelector, "display", "none");
7261         }
7262         
7263         this.onHeaderChange();
7264         this.onLoad();
7265     },
7266     
7267     setColumnWidth: function(col_index, width)
7268     {
7269         // width = "md-2 xs-2..."
7270         if(!this.colModel.config[col_index]) {
7271             return;
7272         }
7273         
7274         var w = width.split(" ");
7275         
7276         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7277         
7278         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7279         
7280         
7281         for(var j = 0; j < w.length; j++) {
7282             
7283             if(!w[j]) {
7284                 continue;
7285             }
7286             
7287             var size_cls = w[j].split("-");
7288             
7289             if(!Number.isInteger(size_cls[1] * 1)) {
7290                 continue;
7291             }
7292             
7293             if(!this.colModel.config[col_index][size_cls[0]]) {
7294                 continue;
7295             }
7296             
7297             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7298                 continue;
7299             }
7300             
7301             h_row[0].classList.replace(
7302                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7303                 "col-"+size_cls[0]+"-"+size_cls[1]
7304             );
7305             
7306             for(var i = 0; i < rows.length; i++) {
7307                 
7308                 var size_cls = w[j].split("-");
7309                 
7310                 if(!Number.isInteger(size_cls[1] * 1)) {
7311                     continue;
7312                 }
7313                 
7314                 if(!this.colModel.config[col_index][size_cls[0]]) {
7315                     continue;
7316                 }
7317                 
7318                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7319                     continue;
7320                 }
7321                 
7322                 rows[i].classList.replace(
7323                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7324                     "col-"+size_cls[0]+"-"+size_cls[1]
7325                 );
7326             }
7327             
7328             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7329         }
7330     }
7331 });
7332
7333  
7334
7335  /*
7336  * - LGPL
7337  *
7338  * table cell
7339  * 
7340  */
7341
7342 /**
7343  * @class Roo.bootstrap.TableCell
7344  * @extends Roo.bootstrap.Component
7345  * Bootstrap TableCell class
7346  * @cfg {String} html cell contain text
7347  * @cfg {String} cls cell class
7348  * @cfg {String} tag cell tag (td|th) default td
7349  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7350  * @cfg {String} align Aligns the content in a cell
7351  * @cfg {String} axis Categorizes cells
7352  * @cfg {String} bgcolor Specifies the background color of a cell
7353  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7354  * @cfg {Number} colspan Specifies the number of columns a cell should span
7355  * @cfg {String} headers Specifies one or more header cells a cell is related to
7356  * @cfg {Number} height Sets the height of a cell
7357  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7358  * @cfg {Number} rowspan Sets the number of rows a cell should span
7359  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7360  * @cfg {String} valign Vertical aligns the content in a cell
7361  * @cfg {Number} width Specifies the width of a cell
7362  * 
7363  * @constructor
7364  * Create a new TableCell
7365  * @param {Object} config The config object
7366  */
7367
7368 Roo.bootstrap.TableCell = function(config){
7369     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7370 };
7371
7372 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7373     
7374     html: false,
7375     cls: false,
7376     tag: false,
7377     abbr: false,
7378     align: false,
7379     axis: false,
7380     bgcolor: false,
7381     charoff: false,
7382     colspan: false,
7383     headers: false,
7384     height: false,
7385     nowrap: false,
7386     rowspan: false,
7387     scope: false,
7388     valign: false,
7389     width: false,
7390     
7391     
7392     getAutoCreate : function(){
7393         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7394         
7395         cfg = {
7396             tag: 'td'
7397         };
7398         
7399         if(this.tag){
7400             cfg.tag = this.tag;
7401         }
7402         
7403         if (this.html) {
7404             cfg.html=this.html
7405         }
7406         if (this.cls) {
7407             cfg.cls=this.cls
7408         }
7409         if (this.abbr) {
7410             cfg.abbr=this.abbr
7411         }
7412         if (this.align) {
7413             cfg.align=this.align
7414         }
7415         if (this.axis) {
7416             cfg.axis=this.axis
7417         }
7418         if (this.bgcolor) {
7419             cfg.bgcolor=this.bgcolor
7420         }
7421         if (this.charoff) {
7422             cfg.charoff=this.charoff
7423         }
7424         if (this.colspan) {
7425             cfg.colspan=this.colspan
7426         }
7427         if (this.headers) {
7428             cfg.headers=this.headers
7429         }
7430         if (this.height) {
7431             cfg.height=this.height
7432         }
7433         if (this.nowrap) {
7434             cfg.nowrap=this.nowrap
7435         }
7436         if (this.rowspan) {
7437             cfg.rowspan=this.rowspan
7438         }
7439         if (this.scope) {
7440             cfg.scope=this.scope
7441         }
7442         if (this.valign) {
7443             cfg.valign=this.valign
7444         }
7445         if (this.width) {
7446             cfg.width=this.width
7447         }
7448         
7449         
7450         return cfg;
7451     }
7452    
7453 });
7454
7455  
7456
7457  /*
7458  * - LGPL
7459  *
7460  * table row
7461  * 
7462  */
7463
7464 /**
7465  * @class Roo.bootstrap.TableRow
7466  * @extends Roo.bootstrap.Component
7467  * Bootstrap TableRow class
7468  * @cfg {String} cls row class
7469  * @cfg {String} align Aligns the content in a table row
7470  * @cfg {String} bgcolor Specifies a background color for a table row
7471  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7472  * @cfg {String} valign Vertical aligns the content in a table row
7473  * 
7474  * @constructor
7475  * Create a new TableRow
7476  * @param {Object} config The config object
7477  */
7478
7479 Roo.bootstrap.TableRow = function(config){
7480     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7481 };
7482
7483 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7484     
7485     cls: false,
7486     align: false,
7487     bgcolor: false,
7488     charoff: false,
7489     valign: false,
7490     
7491     getAutoCreate : function(){
7492         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7493         
7494         cfg = {
7495             tag: 'tr'
7496         };
7497             
7498         if(this.cls){
7499             cfg.cls = this.cls;
7500         }
7501         if(this.align){
7502             cfg.align = this.align;
7503         }
7504         if(this.bgcolor){
7505             cfg.bgcolor = this.bgcolor;
7506         }
7507         if(this.charoff){
7508             cfg.charoff = this.charoff;
7509         }
7510         if(this.valign){
7511             cfg.valign = this.valign;
7512         }
7513         
7514         return cfg;
7515     }
7516    
7517 });
7518
7519  
7520
7521  /*
7522  * - LGPL
7523  *
7524  * table body
7525  * 
7526  */
7527
7528 /**
7529  * @class Roo.bootstrap.TableBody
7530  * @extends Roo.bootstrap.Component
7531  * Bootstrap TableBody class
7532  * @cfg {String} cls element class
7533  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7534  * @cfg {String} align Aligns the content inside the element
7535  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7536  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7537  * 
7538  * @constructor
7539  * Create a new TableBody
7540  * @param {Object} config The config object
7541  */
7542
7543 Roo.bootstrap.TableBody = function(config){
7544     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7545 };
7546
7547 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7548     
7549     cls: false,
7550     tag: false,
7551     align: false,
7552     charoff: false,
7553     valign: false,
7554     
7555     getAutoCreate : function(){
7556         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7557         
7558         cfg = {
7559             tag: 'tbody'
7560         };
7561             
7562         if (this.cls) {
7563             cfg.cls=this.cls
7564         }
7565         if(this.tag){
7566             cfg.tag = this.tag;
7567         }
7568         
7569         if(this.align){
7570             cfg.align = this.align;
7571         }
7572         if(this.charoff){
7573             cfg.charoff = this.charoff;
7574         }
7575         if(this.valign){
7576             cfg.valign = this.valign;
7577         }
7578         
7579         return cfg;
7580     }
7581     
7582     
7583 //    initEvents : function()
7584 //    {
7585 //        
7586 //        if(!this.store){
7587 //            return;
7588 //        }
7589 //        
7590 //        this.store = Roo.factory(this.store, Roo.data);
7591 //        this.store.on('load', this.onLoad, this);
7592 //        
7593 //        this.store.load();
7594 //        
7595 //    },
7596 //    
7597 //    onLoad: function () 
7598 //    {   
7599 //        this.fireEvent('load', this);
7600 //    }
7601 //    
7602 //   
7603 });
7604
7605  
7606
7607  /*
7608  * Based on:
7609  * Ext JS Library 1.1.1
7610  * Copyright(c) 2006-2007, Ext JS, LLC.
7611  *
7612  * Originally Released Under LGPL - original licence link has changed is not relivant.
7613  *
7614  * Fork - LGPL
7615  * <script type="text/javascript">
7616  */
7617
7618 // as we use this in bootstrap.
7619 Roo.namespace('Roo.form');
7620  /**
7621  * @class Roo.form.Action
7622  * Internal Class used to handle form actions
7623  * @constructor
7624  * @param {Roo.form.BasicForm} el The form element or its id
7625  * @param {Object} config Configuration options
7626  */
7627
7628  
7629  
7630 // define the action interface
7631 Roo.form.Action = function(form, options){
7632     this.form = form;
7633     this.options = options || {};
7634 };
7635 /**
7636  * Client Validation Failed
7637  * @const 
7638  */
7639 Roo.form.Action.CLIENT_INVALID = 'client';
7640 /**
7641  * Server Validation Failed
7642  * @const 
7643  */
7644 Roo.form.Action.SERVER_INVALID = 'server';
7645  /**
7646  * Connect to Server Failed
7647  * @const 
7648  */
7649 Roo.form.Action.CONNECT_FAILURE = 'connect';
7650 /**
7651  * Reading Data from Server Failed
7652  * @const 
7653  */
7654 Roo.form.Action.LOAD_FAILURE = 'load';
7655
7656 Roo.form.Action.prototype = {
7657     type : 'default',
7658     failureType : undefined,
7659     response : undefined,
7660     result : undefined,
7661
7662     // interface method
7663     run : function(options){
7664
7665     },
7666
7667     // interface method
7668     success : function(response){
7669
7670     },
7671
7672     // interface method
7673     handleResponse : function(response){
7674
7675     },
7676
7677     // default connection failure
7678     failure : function(response){
7679         
7680         this.response = response;
7681         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7682         this.form.afterAction(this, false);
7683     },
7684
7685     processResponse : function(response){
7686         this.response = response;
7687         if(!response.responseText){
7688             return true;
7689         }
7690         this.result = this.handleResponse(response);
7691         return this.result;
7692     },
7693
7694     // utility functions used internally
7695     getUrl : function(appendParams){
7696         var url = this.options.url || this.form.url || this.form.el.dom.action;
7697         if(appendParams){
7698             var p = this.getParams();
7699             if(p){
7700                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7701             }
7702         }
7703         return url;
7704     },
7705
7706     getMethod : function(){
7707         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7708     },
7709
7710     getParams : function(){
7711         var bp = this.form.baseParams;
7712         var p = this.options.params;
7713         if(p){
7714             if(typeof p == "object"){
7715                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7716             }else if(typeof p == 'string' && bp){
7717                 p += '&' + Roo.urlEncode(bp);
7718             }
7719         }else if(bp){
7720             p = Roo.urlEncode(bp);
7721         }
7722         return p;
7723     },
7724
7725     createCallback : function(){
7726         return {
7727             success: this.success,
7728             failure: this.failure,
7729             scope: this,
7730             timeout: (this.form.timeout*1000),
7731             upload: this.form.fileUpload ? this.success : undefined
7732         };
7733     }
7734 };
7735
7736 Roo.form.Action.Submit = function(form, options){
7737     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7738 };
7739
7740 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7741     type : 'submit',
7742
7743     haveProgress : false,
7744     uploadComplete : false,
7745     
7746     // uploadProgress indicator.
7747     uploadProgress : function()
7748     {
7749         if (!this.form.progressUrl) {
7750             return;
7751         }
7752         
7753         if (!this.haveProgress) {
7754             Roo.MessageBox.progress("Uploading", "Uploading");
7755         }
7756         if (this.uploadComplete) {
7757            Roo.MessageBox.hide();
7758            return;
7759         }
7760         
7761         this.haveProgress = true;
7762    
7763         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7764         
7765         var c = new Roo.data.Connection();
7766         c.request({
7767             url : this.form.progressUrl,
7768             params: {
7769                 id : uid
7770             },
7771             method: 'GET',
7772             success : function(req){
7773                //console.log(data);
7774                 var rdata = false;
7775                 var edata;
7776                 try  {
7777                    rdata = Roo.decode(req.responseText)
7778                 } catch (e) {
7779                     Roo.log("Invalid data from server..");
7780                     Roo.log(edata);
7781                     return;
7782                 }
7783                 if (!rdata || !rdata.success) {
7784                     Roo.log(rdata);
7785                     Roo.MessageBox.alert(Roo.encode(rdata));
7786                     return;
7787                 }
7788                 var data = rdata.data;
7789                 
7790                 if (this.uploadComplete) {
7791                    Roo.MessageBox.hide();
7792                    return;
7793                 }
7794                    
7795                 if (data){
7796                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7797                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7798                     );
7799                 }
7800                 this.uploadProgress.defer(2000,this);
7801             },
7802        
7803             failure: function(data) {
7804                 Roo.log('progress url failed ');
7805                 Roo.log(data);
7806             },
7807             scope : this
7808         });
7809            
7810     },
7811     
7812     
7813     run : function()
7814     {
7815         // run get Values on the form, so it syncs any secondary forms.
7816         this.form.getValues();
7817         
7818         var o = this.options;
7819         var method = this.getMethod();
7820         var isPost = method == 'POST';
7821         if(o.clientValidation === false || this.form.isValid()){
7822             
7823             if (this.form.progressUrl) {
7824                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7825                     (new Date() * 1) + '' + Math.random());
7826                     
7827             } 
7828             
7829             
7830             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7831                 form:this.form.el.dom,
7832                 url:this.getUrl(!isPost),
7833                 method: method,
7834                 params:isPost ? this.getParams() : null,
7835                 isUpload: this.form.fileUpload
7836             }));
7837             
7838             this.uploadProgress();
7839
7840         }else if (o.clientValidation !== false){ // client validation failed
7841             this.failureType = Roo.form.Action.CLIENT_INVALID;
7842             this.form.afterAction(this, false);
7843         }
7844     },
7845
7846     success : function(response)
7847     {
7848         this.uploadComplete= true;
7849         if (this.haveProgress) {
7850             Roo.MessageBox.hide();
7851         }
7852         
7853         
7854         var result = this.processResponse(response);
7855         if(result === true || result.success){
7856             this.form.afterAction(this, true);
7857             return;
7858         }
7859         if(result.errors){
7860             this.form.markInvalid(result.errors);
7861             this.failureType = Roo.form.Action.SERVER_INVALID;
7862         }
7863         this.form.afterAction(this, false);
7864     },
7865     failure : function(response)
7866     {
7867         this.uploadComplete= true;
7868         if (this.haveProgress) {
7869             Roo.MessageBox.hide();
7870         }
7871         
7872         this.response = response;
7873         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7874         this.form.afterAction(this, false);
7875     },
7876     
7877     handleResponse : function(response){
7878         if(this.form.errorReader){
7879             var rs = this.form.errorReader.read(response);
7880             var errors = [];
7881             if(rs.records){
7882                 for(var i = 0, len = rs.records.length; i < len; i++) {
7883                     var r = rs.records[i];
7884                     errors[i] = r.data;
7885                 }
7886             }
7887             if(errors.length < 1){
7888                 errors = null;
7889             }
7890             return {
7891                 success : rs.success,
7892                 errors : errors
7893             };
7894         }
7895         var ret = false;
7896         try {
7897             ret = Roo.decode(response.responseText);
7898         } catch (e) {
7899             ret = {
7900                 success: false,
7901                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7902                 errors : []
7903             };
7904         }
7905         return ret;
7906         
7907     }
7908 });
7909
7910
7911 Roo.form.Action.Load = function(form, options){
7912     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7913     this.reader = this.form.reader;
7914 };
7915
7916 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7917     type : 'load',
7918
7919     run : function(){
7920         
7921         Roo.Ajax.request(Roo.apply(
7922                 this.createCallback(), {
7923                     method:this.getMethod(),
7924                     url:this.getUrl(false),
7925                     params:this.getParams()
7926         }));
7927     },
7928
7929     success : function(response){
7930         
7931         var result = this.processResponse(response);
7932         if(result === true || !result.success || !result.data){
7933             this.failureType = Roo.form.Action.LOAD_FAILURE;
7934             this.form.afterAction(this, false);
7935             return;
7936         }
7937         this.form.clearInvalid();
7938         this.form.setValues(result.data);
7939         this.form.afterAction(this, true);
7940     },
7941
7942     handleResponse : function(response){
7943         if(this.form.reader){
7944             var rs = this.form.reader.read(response);
7945             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7946             return {
7947                 success : rs.success,
7948                 data : data
7949             };
7950         }
7951         return Roo.decode(response.responseText);
7952     }
7953 });
7954
7955 Roo.form.Action.ACTION_TYPES = {
7956     'load' : Roo.form.Action.Load,
7957     'submit' : Roo.form.Action.Submit
7958 };/*
7959  * - LGPL
7960  *
7961  * form
7962  *
7963  */
7964
7965 /**
7966  * @class Roo.bootstrap.Form
7967  * @extends Roo.bootstrap.Component
7968  * Bootstrap Form class
7969  * @cfg {String} method  GET | POST (default POST)
7970  * @cfg {String} labelAlign top | left (default top)
7971  * @cfg {String} align left  | right - for navbars
7972  * @cfg {Boolean} loadMask load mask when submit (default true)
7973
7974  *
7975  * @constructor
7976  * Create a new Form
7977  * @param {Object} config The config object
7978  */
7979
7980
7981 Roo.bootstrap.Form = function(config){
7982     
7983     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7984     
7985     Roo.bootstrap.Form.popover.apply();
7986     
7987     this.addEvents({
7988         /**
7989          * @event clientvalidation
7990          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7991          * @param {Form} this
7992          * @param {Boolean} valid true if the form has passed client-side validation
7993          */
7994         clientvalidation: true,
7995         /**
7996          * @event beforeaction
7997          * Fires before any action is performed. Return false to cancel the action.
7998          * @param {Form} this
7999          * @param {Action} action The action to be performed
8000          */
8001         beforeaction: true,
8002         /**
8003          * @event actionfailed
8004          * Fires when an action fails.
8005          * @param {Form} this
8006          * @param {Action} action The action that failed
8007          */
8008         actionfailed : true,
8009         /**
8010          * @event actioncomplete
8011          * Fires when an action is completed.
8012          * @param {Form} this
8013          * @param {Action} action The action that completed
8014          */
8015         actioncomplete : true
8016     });
8017 };
8018
8019 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8020
8021      /**
8022      * @cfg {String} method
8023      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8024      */
8025     method : 'POST',
8026     /**
8027      * @cfg {String} url
8028      * The URL to use for form actions if one isn't supplied in the action options.
8029      */
8030     /**
8031      * @cfg {Boolean} fileUpload
8032      * Set to true if this form is a file upload.
8033      */
8034
8035     /**
8036      * @cfg {Object} baseParams
8037      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8038      */
8039
8040     /**
8041      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8042      */
8043     timeout: 30,
8044     /**
8045      * @cfg {Sting} align (left|right) for navbar forms
8046      */
8047     align : 'left',
8048
8049     // private
8050     activeAction : null,
8051
8052     /**
8053      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8054      * element by passing it or its id or mask the form itself by passing in true.
8055      * @type Mixed
8056      */
8057     waitMsgTarget : false,
8058
8059     loadMask : true,
8060     
8061     /**
8062      * @cfg {Boolean} errorMask (true|false) default false
8063      */
8064     errorMask : false,
8065     
8066     /**
8067      * @cfg {Number} maskOffset Default 100
8068      */
8069     maskOffset : 100,
8070     
8071     /**
8072      * @cfg {Boolean} maskBody
8073      */
8074     maskBody : false,
8075
8076     getAutoCreate : function(){
8077
8078         var cfg = {
8079             tag: 'form',
8080             method : this.method || 'POST',
8081             id : this.id || Roo.id(),
8082             cls : ''
8083         };
8084         if (this.parent().xtype.match(/^Nav/)) {
8085             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8086
8087         }
8088
8089         if (this.labelAlign == 'left' ) {
8090             cfg.cls += ' form-horizontal';
8091         }
8092
8093
8094         return cfg;
8095     },
8096     initEvents : function()
8097     {
8098         this.el.on('submit', this.onSubmit, this);
8099         // this was added as random key presses on the form where triggering form submit.
8100         this.el.on('keypress', function(e) {
8101             if (e.getCharCode() != 13) {
8102                 return true;
8103             }
8104             // we might need to allow it for textareas.. and some other items.
8105             // check e.getTarget().
8106
8107             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8108                 return true;
8109             }
8110
8111             Roo.log("keypress blocked");
8112
8113             e.preventDefault();
8114             return false;
8115         });
8116         
8117     },
8118     // private
8119     onSubmit : function(e){
8120         e.stopEvent();
8121     },
8122
8123      /**
8124      * Returns true if client-side validation on the form is successful.
8125      * @return Boolean
8126      */
8127     isValid : function(){
8128         var items = this.getItems();
8129         var valid = true;
8130         var target = false;
8131         
8132         items.each(function(f){
8133             
8134             if(f.validate()){
8135                 return;
8136             }
8137             
8138             Roo.log('invalid field: ' + f.name);
8139             
8140             valid = false;
8141
8142             if(!target && f.el.isVisible(true)){
8143                 target = f;
8144             }
8145            
8146         });
8147         
8148         if(this.errorMask && !valid){
8149             Roo.bootstrap.Form.popover.mask(this, target);
8150         }
8151         
8152         return valid;
8153     },
8154     
8155     /**
8156      * Returns true if any fields in this form have changed since their original load.
8157      * @return Boolean
8158      */
8159     isDirty : function(){
8160         var dirty = false;
8161         var items = this.getItems();
8162         items.each(function(f){
8163            if(f.isDirty()){
8164                dirty = true;
8165                return false;
8166            }
8167            return true;
8168         });
8169         return dirty;
8170     },
8171      /**
8172      * Performs a predefined action (submit or load) or custom actions you define on this form.
8173      * @param {String} actionName The name of the action type
8174      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8175      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8176      * accept other config options):
8177      * <pre>
8178 Property          Type             Description
8179 ----------------  ---------------  ----------------------------------------------------------------------------------
8180 url               String           The url for the action (defaults to the form's url)
8181 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8182 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8183 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8184                                    validate the form on the client (defaults to false)
8185      * </pre>
8186      * @return {BasicForm} this
8187      */
8188     doAction : function(action, options){
8189         if(typeof action == 'string'){
8190             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8191         }
8192         if(this.fireEvent('beforeaction', this, action) !== false){
8193             this.beforeAction(action);
8194             action.run.defer(100, action);
8195         }
8196         return this;
8197     },
8198
8199     // private
8200     beforeAction : function(action){
8201         var o = action.options;
8202         
8203         if(this.loadMask){
8204             
8205             if(this.maskBody){
8206                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8207             } else {
8208                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8209             }
8210         }
8211         // not really supported yet.. ??
8212
8213         //if(this.waitMsgTarget === true){
8214         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8215         //}else if(this.waitMsgTarget){
8216         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8217         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8218         //}else {
8219         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8220        // }
8221
8222     },
8223
8224     // private
8225     afterAction : function(action, success){
8226         this.activeAction = null;
8227         var o = action.options;
8228
8229         if(this.loadMask){
8230             
8231             if(this.maskBody){
8232                 Roo.get(document.body).unmask();
8233             } else {
8234                 this.el.unmask();
8235             }
8236         }
8237         
8238         //if(this.waitMsgTarget === true){
8239 //            this.el.unmask();
8240         //}else if(this.waitMsgTarget){
8241         //    this.waitMsgTarget.unmask();
8242         //}else{
8243         //    Roo.MessageBox.updateProgress(1);
8244         //    Roo.MessageBox.hide();
8245        // }
8246         //
8247         if(success){
8248             if(o.reset){
8249                 this.reset();
8250             }
8251             Roo.callback(o.success, o.scope, [this, action]);
8252             this.fireEvent('actioncomplete', this, action);
8253
8254         }else{
8255
8256             // failure condition..
8257             // we have a scenario where updates need confirming.
8258             // eg. if a locking scenario exists..
8259             // we look for { errors : { needs_confirm : true }} in the response.
8260             if (
8261                 (typeof(action.result) != 'undefined')  &&
8262                 (typeof(action.result.errors) != 'undefined')  &&
8263                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8264            ){
8265                 var _t = this;
8266                 Roo.log("not supported yet");
8267                  /*
8268
8269                 Roo.MessageBox.confirm(
8270                     "Change requires confirmation",
8271                     action.result.errorMsg,
8272                     function(r) {
8273                         if (r != 'yes') {
8274                             return;
8275                         }
8276                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8277                     }
8278
8279                 );
8280                 */
8281
8282
8283                 return;
8284             }
8285
8286             Roo.callback(o.failure, o.scope, [this, action]);
8287             // show an error message if no failed handler is set..
8288             if (!this.hasListener('actionfailed')) {
8289                 Roo.log("need to add dialog support");
8290                 /*
8291                 Roo.MessageBox.alert("Error",
8292                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8293                         action.result.errorMsg :
8294                         "Saving Failed, please check your entries or try again"
8295                 );
8296                 */
8297             }
8298
8299             this.fireEvent('actionfailed', this, action);
8300         }
8301
8302     },
8303     /**
8304      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8305      * @param {String} id The value to search for
8306      * @return Field
8307      */
8308     findField : function(id){
8309         var items = this.getItems();
8310         var field = items.get(id);
8311         if(!field){
8312              items.each(function(f){
8313                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8314                     field = f;
8315                     return false;
8316                 }
8317                 return true;
8318             });
8319         }
8320         return field || null;
8321     },
8322      /**
8323      * Mark fields in this form invalid in bulk.
8324      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8325      * @return {BasicForm} this
8326      */
8327     markInvalid : function(errors){
8328         if(errors instanceof Array){
8329             for(var i = 0, len = errors.length; i < len; i++){
8330                 var fieldError = errors[i];
8331                 var f = this.findField(fieldError.id);
8332                 if(f){
8333                     f.markInvalid(fieldError.msg);
8334                 }
8335             }
8336         }else{
8337             var field, id;
8338             for(id in errors){
8339                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8340                     field.markInvalid(errors[id]);
8341                 }
8342             }
8343         }
8344         //Roo.each(this.childForms || [], function (f) {
8345         //    f.markInvalid(errors);
8346         //});
8347
8348         return this;
8349     },
8350
8351     /**
8352      * Set values for fields in this form in bulk.
8353      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8354      * @return {BasicForm} this
8355      */
8356     setValues : function(values){
8357         if(values instanceof Array){ // array of objects
8358             for(var i = 0, len = values.length; i < len; i++){
8359                 var v = values[i];
8360                 var f = this.findField(v.id);
8361                 if(f){
8362                     f.setValue(v.value);
8363                     if(this.trackResetOnLoad){
8364                         f.originalValue = f.getValue();
8365                     }
8366                 }
8367             }
8368         }else{ // object hash
8369             var field, id;
8370             for(id in values){
8371                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8372
8373                     if (field.setFromData &&
8374                         field.valueField &&
8375                         field.displayField &&
8376                         // combos' with local stores can
8377                         // be queried via setValue()
8378                         // to set their value..
8379                         (field.store && !field.store.isLocal)
8380                         ) {
8381                         // it's a combo
8382                         var sd = { };
8383                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8384                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8385                         field.setFromData(sd);
8386
8387                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8388                         
8389                         field.setFromData(values);
8390                         
8391                     } else {
8392                         field.setValue(values[id]);
8393                     }
8394
8395
8396                     if(this.trackResetOnLoad){
8397                         field.originalValue = field.getValue();
8398                     }
8399                 }
8400             }
8401         }
8402
8403         //Roo.each(this.childForms || [], function (f) {
8404         //    f.setValues(values);
8405         //});
8406
8407         return this;
8408     },
8409
8410     /**
8411      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8412      * they are returned as an array.
8413      * @param {Boolean} asString
8414      * @return {Object}
8415      */
8416     getValues : function(asString){
8417         //if (this.childForms) {
8418             // copy values from the child forms
8419         //    Roo.each(this.childForms, function (f) {
8420         //        this.setValues(f.getValues());
8421         //    }, this);
8422         //}
8423
8424
8425
8426         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8427         if(asString === true){
8428             return fs;
8429         }
8430         return Roo.urlDecode(fs);
8431     },
8432
8433     /**
8434      * Returns the fields in this form as an object with key/value pairs.
8435      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8436      * @return {Object}
8437      */
8438     getFieldValues : function(with_hidden)
8439     {
8440         var items = this.getItems();
8441         var ret = {};
8442         items.each(function(f){
8443             
8444             if (!f.getName()) {
8445                 return;
8446             }
8447             
8448             var v = f.getValue();
8449             
8450             if (f.inputType =='radio') {
8451                 if (typeof(ret[f.getName()]) == 'undefined') {
8452                     ret[f.getName()] = ''; // empty..
8453                 }
8454
8455                 if (!f.el.dom.checked) {
8456                     return;
8457
8458                 }
8459                 v = f.el.dom.value;
8460
8461             }
8462             
8463             if(f.xtype == 'MoneyField'){
8464                 ret[f.currencyName] = f.getCurrency();
8465             }
8466
8467             // not sure if this supported any more..
8468             if ((typeof(v) == 'object') && f.getRawValue) {
8469                 v = f.getRawValue() ; // dates..
8470             }
8471             // combo boxes where name != hiddenName...
8472             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8473                 ret[f.name] = f.getRawValue();
8474             }
8475             ret[f.getName()] = v;
8476         });
8477
8478         return ret;
8479     },
8480
8481     /**
8482      * Clears all invalid messages in this form.
8483      * @return {BasicForm} this
8484      */
8485     clearInvalid : function(){
8486         var items = this.getItems();
8487
8488         items.each(function(f){
8489            f.clearInvalid();
8490         });
8491
8492         return this;
8493     },
8494
8495     /**
8496      * Resets this form.
8497      * @return {BasicForm} this
8498      */
8499     reset : function(){
8500         var items = this.getItems();
8501         items.each(function(f){
8502             f.reset();
8503         });
8504
8505         Roo.each(this.childForms || [], function (f) {
8506             f.reset();
8507         });
8508
8509
8510         return this;
8511     },
8512     
8513     getItems : function()
8514     {
8515         var r=new Roo.util.MixedCollection(false, function(o){
8516             return o.id || (o.id = Roo.id());
8517         });
8518         var iter = function(el) {
8519             if (el.inputEl) {
8520                 r.add(el);
8521             }
8522             if (!el.items) {
8523                 return;
8524             }
8525             Roo.each(el.items,function(e) {
8526                 iter(e);
8527             });
8528         };
8529
8530         iter(this);
8531         return r;
8532     },
8533     
8534     hideFields : function(items)
8535     {
8536         Roo.each(items, function(i){
8537             
8538             var f = this.findField(i);
8539             
8540             if(!f){
8541                 return;
8542             }
8543             
8544             f.hide();
8545             
8546         }, this);
8547     },
8548     
8549     showFields : function(items)
8550     {
8551         Roo.each(items, function(i){
8552             
8553             var f = this.findField(i);
8554             
8555             if(!f){
8556                 return;
8557             }
8558             
8559             f.show();
8560             
8561         }, this);
8562     }
8563
8564 });
8565
8566 Roo.apply(Roo.bootstrap.Form, {
8567     
8568     popover : {
8569         
8570         padding : 5,
8571         
8572         isApplied : false,
8573         
8574         isMasked : false,
8575         
8576         form : false,
8577         
8578         target : false,
8579         
8580         toolTip : false,
8581         
8582         intervalID : false,
8583         
8584         maskEl : false,
8585         
8586         apply : function()
8587         {
8588             if(this.isApplied){
8589                 return;
8590             }
8591             
8592             this.maskEl = {
8593                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8594                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8595                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8596                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8597             };
8598             
8599             this.maskEl.top.enableDisplayMode("block");
8600             this.maskEl.left.enableDisplayMode("block");
8601             this.maskEl.bottom.enableDisplayMode("block");
8602             this.maskEl.right.enableDisplayMode("block");
8603             
8604             this.toolTip = new Roo.bootstrap.Tooltip({
8605                 cls : 'roo-form-error-popover',
8606                 alignment : {
8607                     'left' : ['r-l', [-2,0], 'right'],
8608                     'right' : ['l-r', [2,0], 'left'],
8609                     'bottom' : ['tl-bl', [0,2], 'top'],
8610                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8611                 }
8612             });
8613             
8614             this.toolTip.render(Roo.get(document.body));
8615
8616             this.toolTip.el.enableDisplayMode("block");
8617             
8618             Roo.get(document.body).on('click', function(){
8619                 this.unmask();
8620             }, this);
8621             
8622             Roo.get(document.body).on('touchstart', function(){
8623                 this.unmask();
8624             }, this);
8625             
8626             this.isApplied = true
8627         },
8628         
8629         mask : function(form, target)
8630         {
8631             this.form = form;
8632             
8633             this.target = target;
8634             
8635             if(!this.form.errorMask || !target.el){
8636                 return;
8637             }
8638             
8639             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8640             
8641             Roo.log(scrollable);
8642             
8643             var ot = this.target.el.calcOffsetsTo(scrollable);
8644             
8645             var scrollTo = ot[1] - this.form.maskOffset;
8646             
8647             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8648             
8649             scrollable.scrollTo('top', scrollTo);
8650             
8651             var box = this.target.el.getBox();
8652             Roo.log(box);
8653             var zIndex = Roo.bootstrap.Modal.zIndex++;
8654
8655             
8656             this.maskEl.top.setStyle('position', 'absolute');
8657             this.maskEl.top.setStyle('z-index', zIndex);
8658             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8659             this.maskEl.top.setLeft(0);
8660             this.maskEl.top.setTop(0);
8661             this.maskEl.top.show();
8662             
8663             this.maskEl.left.setStyle('position', 'absolute');
8664             this.maskEl.left.setStyle('z-index', zIndex);
8665             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8666             this.maskEl.left.setLeft(0);
8667             this.maskEl.left.setTop(box.y - this.padding);
8668             this.maskEl.left.show();
8669
8670             this.maskEl.bottom.setStyle('position', 'absolute');
8671             this.maskEl.bottom.setStyle('z-index', zIndex);
8672             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8673             this.maskEl.bottom.setLeft(0);
8674             this.maskEl.bottom.setTop(box.bottom + this.padding);
8675             this.maskEl.bottom.show();
8676
8677             this.maskEl.right.setStyle('position', 'absolute');
8678             this.maskEl.right.setStyle('z-index', zIndex);
8679             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8680             this.maskEl.right.setLeft(box.right + this.padding);
8681             this.maskEl.right.setTop(box.y - this.padding);
8682             this.maskEl.right.show();
8683
8684             this.toolTip.bindEl = this.target.el;
8685
8686             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8687
8688             var tip = this.target.blankText;
8689
8690             if(this.target.getValue() !== '' ) {
8691                 
8692                 if (this.target.invalidText.length) {
8693                     tip = this.target.invalidText;
8694                 } else if (this.target.regexText.length){
8695                     tip = this.target.regexText;
8696                 }
8697             }
8698
8699             this.toolTip.show(tip);
8700
8701             this.intervalID = window.setInterval(function() {
8702                 Roo.bootstrap.Form.popover.unmask();
8703             }, 10000);
8704
8705             window.onwheel = function(){ return false;};
8706             
8707             (function(){ this.isMasked = true; }).defer(500, this);
8708             
8709         },
8710         
8711         unmask : function()
8712         {
8713             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8714                 return;
8715             }
8716             
8717             this.maskEl.top.setStyle('position', 'absolute');
8718             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8719             this.maskEl.top.hide();
8720
8721             this.maskEl.left.setStyle('position', 'absolute');
8722             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8723             this.maskEl.left.hide();
8724
8725             this.maskEl.bottom.setStyle('position', 'absolute');
8726             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8727             this.maskEl.bottom.hide();
8728
8729             this.maskEl.right.setStyle('position', 'absolute');
8730             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8731             this.maskEl.right.hide();
8732             
8733             this.toolTip.hide();
8734             
8735             this.toolTip.el.hide();
8736             
8737             window.onwheel = function(){ return true;};
8738             
8739             if(this.intervalID){
8740                 window.clearInterval(this.intervalID);
8741                 this.intervalID = false;
8742             }
8743             
8744             this.isMasked = false;
8745             
8746         }
8747         
8748     }
8749     
8750 });
8751
8752 /*
8753  * Based on:
8754  * Ext JS Library 1.1.1
8755  * Copyright(c) 2006-2007, Ext JS, LLC.
8756  *
8757  * Originally Released Under LGPL - original licence link has changed is not relivant.
8758  *
8759  * Fork - LGPL
8760  * <script type="text/javascript">
8761  */
8762 /**
8763  * @class Roo.form.VTypes
8764  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8765  * @singleton
8766  */
8767 Roo.form.VTypes = function(){
8768     // closure these in so they are only created once.
8769     var alpha = /^[a-zA-Z_]+$/;
8770     var alphanum = /^[a-zA-Z0-9_]+$/;
8771     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8772     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8773
8774     // All these messages and functions are configurable
8775     return {
8776         /**
8777          * The function used to validate email addresses
8778          * @param {String} value The email address
8779          */
8780         'email' : function(v){
8781             return email.test(v);
8782         },
8783         /**
8784          * The error text to display when the email validation function returns false
8785          * @type String
8786          */
8787         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8788         /**
8789          * The keystroke filter mask to be applied on email input
8790          * @type RegExp
8791          */
8792         'emailMask' : /[a-z0-9_\.\-@]/i,
8793
8794         /**
8795          * The function used to validate URLs
8796          * @param {String} value The URL
8797          */
8798         'url' : function(v){
8799             return url.test(v);
8800         },
8801         /**
8802          * The error text to display when the url validation function returns false
8803          * @type String
8804          */
8805         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8806         
8807         /**
8808          * The function used to validate alpha values
8809          * @param {String} value The value
8810          */
8811         'alpha' : function(v){
8812             return alpha.test(v);
8813         },
8814         /**
8815          * The error text to display when the alpha validation function returns false
8816          * @type String
8817          */
8818         'alphaText' : 'This field should only contain letters and _',
8819         /**
8820          * The keystroke filter mask to be applied on alpha input
8821          * @type RegExp
8822          */
8823         'alphaMask' : /[a-z_]/i,
8824
8825         /**
8826          * The function used to validate alphanumeric values
8827          * @param {String} value The value
8828          */
8829         'alphanum' : function(v){
8830             return alphanum.test(v);
8831         },
8832         /**
8833          * The error text to display when the alphanumeric validation function returns false
8834          * @type String
8835          */
8836         'alphanumText' : 'This field should only contain letters, numbers and _',
8837         /**
8838          * The keystroke filter mask to be applied on alphanumeric input
8839          * @type RegExp
8840          */
8841         'alphanumMask' : /[a-z0-9_]/i
8842     };
8843 }();/*
8844  * - LGPL
8845  *
8846  * Input
8847  * 
8848  */
8849
8850 /**
8851  * @class Roo.bootstrap.Input
8852  * @extends Roo.bootstrap.Component
8853  * Bootstrap Input class
8854  * @cfg {Boolean} disabled is it disabled
8855  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8856  * @cfg {String} name name of the input
8857  * @cfg {string} fieldLabel - the label associated
8858  * @cfg {string} placeholder - placeholder to put in text.
8859  * @cfg {string}  before - input group add on before
8860  * @cfg {string} after - input group add on after
8861  * @cfg {string} size - (lg|sm) or leave empty..
8862  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8863  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8864  * @cfg {Number} md colspan out of 12 for computer-sized screens
8865  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8866  * @cfg {string} value default value of the input
8867  * @cfg {Number} labelWidth set the width of label 
8868  * @cfg {Number} labellg set the width of label (1-12)
8869  * @cfg {Number} labelmd set the width of label (1-12)
8870  * @cfg {Number} labelsm set the width of label (1-12)
8871  * @cfg {Number} labelxs set the width of label (1-12)
8872  * @cfg {String} labelAlign (top|left)
8873  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8874  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8875  * @cfg {String} indicatorpos (left|right) default left
8876  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8877  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8878
8879  * @cfg {String} align (left|center|right) Default left
8880  * @cfg {Boolean} forceFeedback (true|false) Default false
8881  * 
8882  * @constructor
8883  * Create a new Input
8884  * @param {Object} config The config object
8885  */
8886
8887 Roo.bootstrap.Input = function(config){
8888     
8889     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8890     
8891     this.addEvents({
8892         /**
8893          * @event focus
8894          * Fires when this field receives input focus.
8895          * @param {Roo.form.Field} this
8896          */
8897         focus : true,
8898         /**
8899          * @event blur
8900          * Fires when this field loses input focus.
8901          * @param {Roo.form.Field} this
8902          */
8903         blur : true,
8904         /**
8905          * @event specialkey
8906          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8907          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8908          * @param {Roo.form.Field} this
8909          * @param {Roo.EventObject} e The event object
8910          */
8911         specialkey : true,
8912         /**
8913          * @event change
8914          * Fires just before the field blurs if the field value has changed.
8915          * @param {Roo.form.Field} this
8916          * @param {Mixed} newValue The new value
8917          * @param {Mixed} oldValue The original value
8918          */
8919         change : true,
8920         /**
8921          * @event invalid
8922          * Fires after the field has been marked as invalid.
8923          * @param {Roo.form.Field} this
8924          * @param {String} msg The validation message
8925          */
8926         invalid : true,
8927         /**
8928          * @event valid
8929          * Fires after the field has been validated with no errors.
8930          * @param {Roo.form.Field} this
8931          */
8932         valid : true,
8933          /**
8934          * @event keyup
8935          * Fires after the key up
8936          * @param {Roo.form.Field} this
8937          * @param {Roo.EventObject}  e The event Object
8938          */
8939         keyup : true
8940     });
8941 };
8942
8943 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8944      /**
8945      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8946       automatic validation (defaults to "keyup").
8947      */
8948     validationEvent : "keyup",
8949      /**
8950      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8951      */
8952     validateOnBlur : true,
8953     /**
8954      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8955      */
8956     validationDelay : 250,
8957      /**
8958      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8959      */
8960     focusClass : "x-form-focus",  // not needed???
8961     
8962        
8963     /**
8964      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8965      */
8966     invalidClass : "has-warning",
8967     
8968     /**
8969      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8970      */
8971     validClass : "has-success",
8972     
8973     /**
8974      * @cfg {Boolean} hasFeedback (true|false) default true
8975      */
8976     hasFeedback : true,
8977     
8978     /**
8979      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8980      */
8981     invalidFeedbackClass : "glyphicon-warning-sign",
8982     
8983     /**
8984      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8985      */
8986     validFeedbackClass : "glyphicon-ok",
8987     
8988     /**
8989      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8990      */
8991     selectOnFocus : false,
8992     
8993      /**
8994      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8995      */
8996     maskRe : null,
8997        /**
8998      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8999      */
9000     vtype : null,
9001     
9002       /**
9003      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9004      */
9005     disableKeyFilter : false,
9006     
9007        /**
9008      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9009      */
9010     disabled : false,
9011      /**
9012      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9013      */
9014     allowBlank : true,
9015     /**
9016      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9017      */
9018     blankText : "Please complete this mandatory field",
9019     
9020      /**
9021      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9022      */
9023     minLength : 0,
9024     /**
9025      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9026      */
9027     maxLength : Number.MAX_VALUE,
9028     /**
9029      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9030      */
9031     minLengthText : "The minimum length for this field is {0}",
9032     /**
9033      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9034      */
9035     maxLengthText : "The maximum length for this field is {0}",
9036   
9037     
9038     /**
9039      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9040      * If available, this function will be called only after the basic validators all return true, and will be passed the
9041      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9042      */
9043     validator : null,
9044     /**
9045      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9046      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9047      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9048      */
9049     regex : null,
9050     /**
9051      * @cfg {String} regexText -- Depricated - use Invalid Text
9052      */
9053     regexText : "",
9054     
9055     /**
9056      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9057      */
9058     invalidText : "",
9059     
9060     
9061     
9062     autocomplete: false,
9063     
9064     
9065     fieldLabel : '',
9066     inputType : 'text',
9067     
9068     name : false,
9069     placeholder: false,
9070     before : false,
9071     after : false,
9072     size : false,
9073     hasFocus : false,
9074     preventMark: false,
9075     isFormField : true,
9076     value : '',
9077     labelWidth : 2,
9078     labelAlign : false,
9079     readOnly : false,
9080     align : false,
9081     formatedValue : false,
9082     forceFeedback : false,
9083     
9084     indicatorpos : 'left',
9085     
9086     labellg : 0,
9087     labelmd : 0,
9088     labelsm : 0,
9089     labelxs : 0,
9090     
9091     capture : '',
9092     accept : '',
9093     
9094     parentLabelAlign : function()
9095     {
9096         var parent = this;
9097         while (parent.parent()) {
9098             parent = parent.parent();
9099             if (typeof(parent.labelAlign) !='undefined') {
9100                 return parent.labelAlign;
9101             }
9102         }
9103         return 'left';
9104         
9105     },
9106     
9107     getAutoCreate : function()
9108     {
9109         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9110         
9111         var id = Roo.id();
9112         
9113         var cfg = {};
9114         
9115         if(this.inputType != 'hidden'){
9116             cfg.cls = 'form-group' //input-group
9117         }
9118         
9119         var input =  {
9120             tag: 'input',
9121             id : id,
9122             type : this.inputType,
9123             value : this.value,
9124             cls : 'form-control',
9125             placeholder : this.placeholder || '',
9126             autocomplete : this.autocomplete || 'new-password'
9127         };
9128         
9129         if(this.capture.length){
9130             input.capture = this.capture;
9131         }
9132         
9133         if(this.accept.length){
9134             input.accept = this.accept + "/*";
9135         }
9136         
9137         if(this.align){
9138             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9139         }
9140         
9141         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9142             input.maxLength = this.maxLength;
9143         }
9144         
9145         if (this.disabled) {
9146             input.disabled=true;
9147         }
9148         
9149         if (this.readOnly) {
9150             input.readonly=true;
9151         }
9152         
9153         if (this.name) {
9154             input.name = this.name;
9155         }
9156         
9157         if (this.size) {
9158             input.cls += ' input-' + this.size;
9159         }
9160         
9161         var settings=this;
9162         ['xs','sm','md','lg'].map(function(size){
9163             if (settings[size]) {
9164                 cfg.cls += ' col-' + size + '-' + settings[size];
9165             }
9166         });
9167         
9168         var inputblock = input;
9169         
9170         var feedback = {
9171             tag: 'span',
9172             cls: 'glyphicon form-control-feedback'
9173         };
9174             
9175         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9176             
9177             inputblock = {
9178                 cls : 'has-feedback',
9179                 cn :  [
9180                     input,
9181                     feedback
9182                 ] 
9183             };  
9184         }
9185         
9186         if (this.before || this.after) {
9187             
9188             inputblock = {
9189                 cls : 'input-group',
9190                 cn :  [] 
9191             };
9192             
9193             if (this.before && typeof(this.before) == 'string') {
9194                 
9195                 inputblock.cn.push({
9196                     tag :'span',
9197                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9198                     html : this.before
9199                 });
9200             }
9201             if (this.before && typeof(this.before) == 'object') {
9202                 this.before = Roo.factory(this.before);
9203                 
9204                 inputblock.cn.push({
9205                     tag :'span',
9206                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9207                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9208                 });
9209             }
9210             
9211             inputblock.cn.push(input);
9212             
9213             if (this.after && typeof(this.after) == 'string') {
9214                 inputblock.cn.push({
9215                     tag :'span',
9216                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9217                     html : this.after
9218                 });
9219             }
9220             if (this.after && typeof(this.after) == 'object') {
9221                 this.after = Roo.factory(this.after);
9222                 
9223                 inputblock.cn.push({
9224                     tag :'span',
9225                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9226                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9227                 });
9228             }
9229             
9230             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9231                 inputblock.cls += ' has-feedback';
9232                 inputblock.cn.push(feedback);
9233             }
9234         };
9235         var indicator = {
9236             tag : 'i',
9237             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9238             tooltip : 'This field is required'
9239         };
9240         if (Roo.bootstrap.version == 4) {
9241             indicator = {
9242                 tag : 'i',
9243                 style : 'display-none'
9244             };
9245         }
9246         if (align ==='left' && this.fieldLabel.length) {
9247             
9248             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9249             
9250             cfg.cn = [
9251                 indicator,
9252                 {
9253                     tag: 'label',
9254                     'for' :  id,
9255                     cls : 'control-label col-form-label',
9256                     html : this.fieldLabel
9257
9258                 },
9259                 {
9260                     cls : "", 
9261                     cn: [
9262                         inputblock
9263                     ]
9264                 }
9265             ];
9266             
9267             var labelCfg = cfg.cn[1];
9268             var contentCfg = cfg.cn[2];
9269             
9270             if(this.indicatorpos == 'right'){
9271                 cfg.cn = [
9272                     {
9273                         tag: 'label',
9274                         'for' :  id,
9275                         cls : 'control-label col-form-label',
9276                         cn : [
9277                             {
9278                                 tag : 'span',
9279                                 html : this.fieldLabel
9280                             },
9281                             indicator
9282                         ]
9283                     },
9284                     {
9285                         cls : "",
9286                         cn: [
9287                             inputblock
9288                         ]
9289                     }
9290
9291                 ];
9292                 
9293                 labelCfg = cfg.cn[0];
9294                 contentCfg = cfg.cn[1];
9295             
9296             }
9297             
9298             if(this.labelWidth > 12){
9299                 labelCfg.style = "width: " + this.labelWidth + 'px';
9300             }
9301             
9302             if(this.labelWidth < 13 && this.labelmd == 0){
9303                 this.labelmd = this.labelWidth;
9304             }
9305             
9306             if(this.labellg > 0){
9307                 labelCfg.cls += ' col-lg-' + this.labellg;
9308                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9309             }
9310             
9311             if(this.labelmd > 0){
9312                 labelCfg.cls += ' col-md-' + this.labelmd;
9313                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9314             }
9315             
9316             if(this.labelsm > 0){
9317                 labelCfg.cls += ' col-sm-' + this.labelsm;
9318                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9319             }
9320             
9321             if(this.labelxs > 0){
9322                 labelCfg.cls += ' col-xs-' + this.labelxs;
9323                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9324             }
9325             
9326             
9327         } else if ( this.fieldLabel.length) {
9328                 
9329             cfg.cn = [
9330                 {
9331                     tag : 'i',
9332                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9333                     tooltip : 'This field is required'
9334                 },
9335                 {
9336                     tag: 'label',
9337                    //cls : 'input-group-addon',
9338                     html : this.fieldLabel
9339
9340                 },
9341
9342                inputblock
9343
9344            ];
9345            
9346            if(this.indicatorpos == 'right'){
9347                 
9348                 cfg.cn = [
9349                     {
9350                         tag: 'label',
9351                        //cls : 'input-group-addon',
9352                         html : this.fieldLabel
9353
9354                     },
9355                     {
9356                         tag : 'i',
9357                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9358                         tooltip : 'This field is required'
9359                     },
9360
9361                    inputblock
9362
9363                ];
9364
9365             }
9366
9367         } else {
9368             
9369             cfg.cn = [
9370
9371                     inputblock
9372
9373             ];
9374                 
9375                 
9376         };
9377         
9378         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9379            cfg.cls += ' navbar-form';
9380         }
9381         
9382         if (this.parentType === 'NavGroup') {
9383            cfg.cls += ' navbar-form';
9384            cfg.tag = 'li';
9385         }
9386         
9387         return cfg;
9388         
9389     },
9390     /**
9391      * return the real input element.
9392      */
9393     inputEl: function ()
9394     {
9395         return this.el.select('input.form-control',true).first();
9396     },
9397     
9398     tooltipEl : function()
9399     {
9400         return this.inputEl();
9401     },
9402     
9403     indicatorEl : function()
9404     {
9405         if (Roo.bootstrap.version == 4) {
9406             return false; // not enabled in v4 yet.
9407         }
9408         
9409         var indicator = this.el.select('i.roo-required-indicator',true).first();
9410         
9411         if(!indicator){
9412             return false;
9413         }
9414         
9415         return indicator;
9416         
9417     },
9418     
9419     setDisabled : function(v)
9420     {
9421         var i  = this.inputEl().dom;
9422         if (!v) {
9423             i.removeAttribute('disabled');
9424             return;
9425             
9426         }
9427         i.setAttribute('disabled','true');
9428     },
9429     initEvents : function()
9430     {
9431           
9432         this.inputEl().on("keydown" , this.fireKey,  this);
9433         this.inputEl().on("focus", this.onFocus,  this);
9434         this.inputEl().on("blur", this.onBlur,  this);
9435         
9436         this.inputEl().relayEvent('keyup', this);
9437         
9438         this.indicator = this.indicatorEl();
9439         
9440         if(this.indicator){
9441             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9442         }
9443  
9444         // reference to original value for reset
9445         this.originalValue = this.getValue();
9446         //Roo.form.TextField.superclass.initEvents.call(this);
9447         if(this.validationEvent == 'keyup'){
9448             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9449             this.inputEl().on('keyup', this.filterValidation, this);
9450         }
9451         else if(this.validationEvent !== false){
9452             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9453         }
9454         
9455         if(this.selectOnFocus){
9456             this.on("focus", this.preFocus, this);
9457             
9458         }
9459         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9460             this.inputEl().on("keypress", this.filterKeys, this);
9461         } else {
9462             this.inputEl().relayEvent('keypress', this);
9463         }
9464        /* if(this.grow){
9465             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9466             this.el.on("click", this.autoSize,  this);
9467         }
9468         */
9469         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9470             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9471         }
9472         
9473         if (typeof(this.before) == 'object') {
9474             this.before.render(this.el.select('.roo-input-before',true).first());
9475         }
9476         if (typeof(this.after) == 'object') {
9477             this.after.render(this.el.select('.roo-input-after',true).first());
9478         }
9479         
9480         this.inputEl().on('change', this.onChange, this);
9481         
9482     },
9483     filterValidation : function(e){
9484         if(!e.isNavKeyPress()){
9485             this.validationTask.delay(this.validationDelay);
9486         }
9487     },
9488      /**
9489      * Validates the field value
9490      * @return {Boolean} True if the value is valid, else false
9491      */
9492     validate : function(){
9493         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9494         if(this.disabled || this.validateValue(this.getRawValue())){
9495             this.markValid();
9496             return true;
9497         }
9498         
9499         this.markInvalid();
9500         return false;
9501     },
9502     
9503     
9504     /**
9505      * Validates a value according to the field's validation rules and marks the field as invalid
9506      * if the validation fails
9507      * @param {Mixed} value The value to validate
9508      * @return {Boolean} True if the value is valid, else false
9509      */
9510     validateValue : function(value)
9511     {
9512         if(this.getVisibilityEl().hasClass('hidden')){
9513             return true;
9514         }
9515         
9516         if(value.length < 1)  { // if it's blank
9517             if(this.allowBlank){
9518                 return true;
9519             }
9520             return false;
9521         }
9522         
9523         if(value.length < this.minLength){
9524             return false;
9525         }
9526         if(value.length > this.maxLength){
9527             return false;
9528         }
9529         if(this.vtype){
9530             var vt = Roo.form.VTypes;
9531             if(!vt[this.vtype](value, this)){
9532                 return false;
9533             }
9534         }
9535         if(typeof this.validator == "function"){
9536             var msg = this.validator(value);
9537             if(msg !== true){
9538                 return false;
9539             }
9540             if (typeof(msg) == 'string') {
9541                 this.invalidText = msg;
9542             }
9543         }
9544         
9545         if(this.regex && !this.regex.test(value)){
9546             return false;
9547         }
9548         
9549         return true;
9550     },
9551     
9552      // private
9553     fireKey : function(e){
9554         //Roo.log('field ' + e.getKey());
9555         if(e.isNavKeyPress()){
9556             this.fireEvent("specialkey", this, e);
9557         }
9558     },
9559     focus : function (selectText){
9560         if(this.rendered){
9561             this.inputEl().focus();
9562             if(selectText === true){
9563                 this.inputEl().dom.select();
9564             }
9565         }
9566         return this;
9567     } ,
9568     
9569     onFocus : function(){
9570         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9571            // this.el.addClass(this.focusClass);
9572         }
9573         if(!this.hasFocus){
9574             this.hasFocus = true;
9575             this.startValue = this.getValue();
9576             this.fireEvent("focus", this);
9577         }
9578     },
9579     
9580     beforeBlur : Roo.emptyFn,
9581
9582     
9583     // private
9584     onBlur : function(){
9585         this.beforeBlur();
9586         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9587             //this.el.removeClass(this.focusClass);
9588         }
9589         this.hasFocus = false;
9590         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9591             this.validate();
9592         }
9593         var v = this.getValue();
9594         if(String(v) !== String(this.startValue)){
9595             this.fireEvent('change', this, v, this.startValue);
9596         }
9597         this.fireEvent("blur", this);
9598     },
9599     
9600     onChange : function(e)
9601     {
9602         var v = this.getValue();
9603         if(String(v) !== String(this.startValue)){
9604             this.fireEvent('change', this, v, this.startValue);
9605         }
9606         
9607     },
9608     
9609     /**
9610      * Resets the current field value to the originally loaded value and clears any validation messages
9611      */
9612     reset : function(){
9613         this.setValue(this.originalValue);
9614         this.validate();
9615     },
9616      /**
9617      * Returns the name of the field
9618      * @return {Mixed} name The name field
9619      */
9620     getName: function(){
9621         return this.name;
9622     },
9623      /**
9624      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9625      * @return {Mixed} value The field value
9626      */
9627     getValue : function(){
9628         
9629         var v = this.inputEl().getValue();
9630         
9631         return v;
9632     },
9633     /**
9634      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9635      * @return {Mixed} value The field value
9636      */
9637     getRawValue : function(){
9638         var v = this.inputEl().getValue();
9639         
9640         return v;
9641     },
9642     
9643     /**
9644      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9645      * @param {Mixed} value The value to set
9646      */
9647     setRawValue : function(v){
9648         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9649     },
9650     
9651     selectText : function(start, end){
9652         var v = this.getRawValue();
9653         if(v.length > 0){
9654             start = start === undefined ? 0 : start;
9655             end = end === undefined ? v.length : end;
9656             var d = this.inputEl().dom;
9657             if(d.setSelectionRange){
9658                 d.setSelectionRange(start, end);
9659             }else if(d.createTextRange){
9660                 var range = d.createTextRange();
9661                 range.moveStart("character", start);
9662                 range.moveEnd("character", v.length-end);
9663                 range.select();
9664             }
9665         }
9666     },
9667     
9668     /**
9669      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9670      * @param {Mixed} value The value to set
9671      */
9672     setValue : function(v){
9673         this.value = v;
9674         if(this.rendered){
9675             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9676             this.validate();
9677         }
9678     },
9679     
9680     /*
9681     processValue : function(value){
9682         if(this.stripCharsRe){
9683             var newValue = value.replace(this.stripCharsRe, '');
9684             if(newValue !== value){
9685                 this.setRawValue(newValue);
9686                 return newValue;
9687             }
9688         }
9689         return value;
9690     },
9691   */
9692     preFocus : function(){
9693         
9694         if(this.selectOnFocus){
9695             this.inputEl().dom.select();
9696         }
9697     },
9698     filterKeys : function(e){
9699         var k = e.getKey();
9700         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9701             return;
9702         }
9703         var c = e.getCharCode(), cc = String.fromCharCode(c);
9704         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9705             return;
9706         }
9707         if(!this.maskRe.test(cc)){
9708             e.stopEvent();
9709         }
9710     },
9711      /**
9712      * Clear any invalid styles/messages for this field
9713      */
9714     clearInvalid : function(){
9715         
9716         if(!this.el || this.preventMark){ // not rendered
9717             return;
9718         }
9719         
9720      
9721         this.el.removeClass(this.invalidClass);
9722         
9723         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9724             
9725             var feedback = this.el.select('.form-control-feedback', true).first();
9726             
9727             if(feedback){
9728                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9729             }
9730             
9731         }
9732         
9733         if(this.indicator){
9734             this.indicator.removeClass('visible');
9735             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9736         }
9737         
9738         this.fireEvent('valid', this);
9739     },
9740     
9741      /**
9742      * Mark this field as valid
9743      */
9744     markValid : function()
9745     {
9746         if(!this.el  || this.preventMark){ // not rendered...
9747             return;
9748         }
9749         
9750         this.el.removeClass([this.invalidClass, this.validClass]);
9751         
9752         var feedback = this.el.select('.form-control-feedback', true).first();
9753             
9754         if(feedback){
9755             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9756         }
9757         
9758         if(this.indicator){
9759             this.indicator.removeClass('visible');
9760             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9761         }
9762         
9763         if(this.disabled){
9764             return;
9765         }
9766         
9767         if(this.allowBlank && !this.getRawValue().length){
9768             return;
9769         }
9770         
9771         this.el.addClass(this.validClass);
9772         
9773         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9774             
9775             var feedback = this.el.select('.form-control-feedback', true).first();
9776             
9777             if(feedback){
9778                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9779                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9780             }
9781             
9782         }
9783         
9784         this.fireEvent('valid', this);
9785     },
9786     
9787      /**
9788      * Mark this field as invalid
9789      * @param {String} msg The validation message
9790      */
9791     markInvalid : function(msg)
9792     {
9793         if(!this.el  || this.preventMark){ // not rendered
9794             return;
9795         }
9796         
9797         this.el.removeClass([this.invalidClass, this.validClass]);
9798         
9799         var feedback = this.el.select('.form-control-feedback', true).first();
9800             
9801         if(feedback){
9802             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9803         }
9804
9805         if(this.disabled){
9806             return;
9807         }
9808         
9809         if(this.allowBlank && !this.getRawValue().length){
9810             return;
9811         }
9812         
9813         if(this.indicator){
9814             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9815             this.indicator.addClass('visible');
9816         }
9817         
9818         this.el.addClass(this.invalidClass);
9819         
9820         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9821             
9822             var feedback = this.el.select('.form-control-feedback', true).first();
9823             
9824             if(feedback){
9825                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9826                 
9827                 if(this.getValue().length || this.forceFeedback){
9828                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9829                 }
9830                 
9831             }
9832             
9833         }
9834         
9835         this.fireEvent('invalid', this, msg);
9836     },
9837     // private
9838     SafariOnKeyDown : function(event)
9839     {
9840         // this is a workaround for a password hang bug on chrome/ webkit.
9841         if (this.inputEl().dom.type != 'password') {
9842             return;
9843         }
9844         
9845         var isSelectAll = false;
9846         
9847         if(this.inputEl().dom.selectionEnd > 0){
9848             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9849         }
9850         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9851             event.preventDefault();
9852             this.setValue('');
9853             return;
9854         }
9855         
9856         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9857             
9858             event.preventDefault();
9859             // this is very hacky as keydown always get's upper case.
9860             //
9861             var cc = String.fromCharCode(event.getCharCode());
9862             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9863             
9864         }
9865     },
9866     adjustWidth : function(tag, w){
9867         tag = tag.toLowerCase();
9868         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9869             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9870                 if(tag == 'input'){
9871                     return w + 2;
9872                 }
9873                 if(tag == 'textarea'){
9874                     return w-2;
9875                 }
9876             }else if(Roo.isOpera){
9877                 if(tag == 'input'){
9878                     return w + 2;
9879                 }
9880                 if(tag == 'textarea'){
9881                     return w-2;
9882                 }
9883             }
9884         }
9885         return w;
9886     },
9887     
9888     setFieldLabel : function(v)
9889     {
9890         if(!this.rendered){
9891             return;
9892         }
9893         
9894         if(this.indicatorEl()){
9895             var ar = this.el.select('label > span',true);
9896             
9897             if (ar.elements.length) {
9898                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9899                 this.fieldLabel = v;
9900                 return;
9901             }
9902             
9903             var br = this.el.select('label',true);
9904             
9905             if(br.elements.length) {
9906                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9907                 this.fieldLabel = v;
9908                 return;
9909             }
9910             
9911             Roo.log('Cannot Found any of label > span || label in input');
9912             return;
9913         }
9914         
9915         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9916         this.fieldLabel = v;
9917         
9918         
9919     }
9920 });
9921
9922  
9923 /*
9924  * - LGPL
9925  *
9926  * Input
9927  * 
9928  */
9929
9930 /**
9931  * @class Roo.bootstrap.TextArea
9932  * @extends Roo.bootstrap.Input
9933  * Bootstrap TextArea class
9934  * @cfg {Number} cols Specifies the visible width of a text area
9935  * @cfg {Number} rows Specifies the visible number of lines in a text area
9936  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9937  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9938  * @cfg {string} html text
9939  * 
9940  * @constructor
9941  * Create a new TextArea
9942  * @param {Object} config The config object
9943  */
9944
9945 Roo.bootstrap.TextArea = function(config){
9946     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9947    
9948 };
9949
9950 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9951      
9952     cols : false,
9953     rows : 5,
9954     readOnly : false,
9955     warp : 'soft',
9956     resize : false,
9957     value: false,
9958     html: false,
9959     
9960     getAutoCreate : function(){
9961         
9962         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9963         
9964         var id = Roo.id();
9965         
9966         var cfg = {};
9967         
9968         if(this.inputType != 'hidden'){
9969             cfg.cls = 'form-group' //input-group
9970         }
9971         
9972         var input =  {
9973             tag: 'textarea',
9974             id : id,
9975             warp : this.warp,
9976             rows : this.rows,
9977             value : this.value || '',
9978             html: this.html || '',
9979             cls : 'form-control',
9980             placeholder : this.placeholder || '' 
9981             
9982         };
9983         
9984         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9985             input.maxLength = this.maxLength;
9986         }
9987         
9988         if(this.resize){
9989             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9990         }
9991         
9992         if(this.cols){
9993             input.cols = this.cols;
9994         }
9995         
9996         if (this.readOnly) {
9997             input.readonly = true;
9998         }
9999         
10000         if (this.name) {
10001             input.name = this.name;
10002         }
10003         
10004         if (this.size) {
10005             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10006         }
10007         
10008         var settings=this;
10009         ['xs','sm','md','lg'].map(function(size){
10010             if (settings[size]) {
10011                 cfg.cls += ' col-' + size + '-' + settings[size];
10012             }
10013         });
10014         
10015         var inputblock = input;
10016         
10017         if(this.hasFeedback && !this.allowBlank){
10018             
10019             var feedback = {
10020                 tag: 'span',
10021                 cls: 'glyphicon form-control-feedback'
10022             };
10023
10024             inputblock = {
10025                 cls : 'has-feedback',
10026                 cn :  [
10027                     input,
10028                     feedback
10029                 ] 
10030             };  
10031         }
10032         
10033         
10034         if (this.before || this.after) {
10035             
10036             inputblock = {
10037                 cls : 'input-group',
10038                 cn :  [] 
10039             };
10040             if (this.before) {
10041                 inputblock.cn.push({
10042                     tag :'span',
10043                     cls : 'input-group-addon',
10044                     html : this.before
10045                 });
10046             }
10047             
10048             inputblock.cn.push(input);
10049             
10050             if(this.hasFeedback && !this.allowBlank){
10051                 inputblock.cls += ' has-feedback';
10052                 inputblock.cn.push(feedback);
10053             }
10054             
10055             if (this.after) {
10056                 inputblock.cn.push({
10057                     tag :'span',
10058                     cls : 'input-group-addon',
10059                     html : this.after
10060                 });
10061             }
10062             
10063         }
10064         
10065         if (align ==='left' && this.fieldLabel.length) {
10066             cfg.cn = [
10067                 {
10068                     tag: 'label',
10069                     'for' :  id,
10070                     cls : 'control-label',
10071                     html : this.fieldLabel
10072                 },
10073                 {
10074                     cls : "",
10075                     cn: [
10076                         inputblock
10077                     ]
10078                 }
10079
10080             ];
10081             
10082             if(this.labelWidth > 12){
10083                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10084             }
10085
10086             if(this.labelWidth < 13 && this.labelmd == 0){
10087                 this.labelmd = this.labelWidth;
10088             }
10089
10090             if(this.labellg > 0){
10091                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10092                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10093             }
10094
10095             if(this.labelmd > 0){
10096                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10097                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10098             }
10099
10100             if(this.labelsm > 0){
10101                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10102                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10103             }
10104
10105             if(this.labelxs > 0){
10106                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10107                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10108             }
10109             
10110         } else if ( this.fieldLabel.length) {
10111             cfg.cn = [
10112
10113                {
10114                    tag: 'label',
10115                    //cls : 'input-group-addon',
10116                    html : this.fieldLabel
10117
10118                },
10119
10120                inputblock
10121
10122            ];
10123
10124         } else {
10125
10126             cfg.cn = [
10127
10128                 inputblock
10129
10130             ];
10131                 
10132         }
10133         
10134         if (this.disabled) {
10135             input.disabled=true;
10136         }
10137         
10138         return cfg;
10139         
10140     },
10141     /**
10142      * return the real textarea element.
10143      */
10144     inputEl: function ()
10145     {
10146         return this.el.select('textarea.form-control',true).first();
10147     },
10148     
10149     /**
10150      * Clear any invalid styles/messages for this field
10151      */
10152     clearInvalid : function()
10153     {
10154         
10155         if(!this.el || this.preventMark){ // not rendered
10156             return;
10157         }
10158         
10159         var label = this.el.select('label', true).first();
10160         var icon = this.el.select('i.fa-star', true).first();
10161         
10162         if(label && icon){
10163             icon.remove();
10164         }
10165         
10166         this.el.removeClass(this.invalidClass);
10167         
10168         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10169             
10170             var feedback = this.el.select('.form-control-feedback', true).first();
10171             
10172             if(feedback){
10173                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10174             }
10175             
10176         }
10177         
10178         this.fireEvent('valid', this);
10179     },
10180     
10181      /**
10182      * Mark this field as valid
10183      */
10184     markValid : function()
10185     {
10186         if(!this.el  || this.preventMark){ // not rendered
10187             return;
10188         }
10189         
10190         this.el.removeClass([this.invalidClass, this.validClass]);
10191         
10192         var feedback = this.el.select('.form-control-feedback', true).first();
10193             
10194         if(feedback){
10195             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10196         }
10197
10198         if(this.disabled || this.allowBlank){
10199             return;
10200         }
10201         
10202         var label = this.el.select('label', true).first();
10203         var icon = this.el.select('i.fa-star', true).first();
10204         
10205         if(label && icon){
10206             icon.remove();
10207         }
10208         
10209         this.el.addClass(this.validClass);
10210         
10211         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10212             
10213             var feedback = this.el.select('.form-control-feedback', true).first();
10214             
10215             if(feedback){
10216                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10217                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10218             }
10219             
10220         }
10221         
10222         this.fireEvent('valid', this);
10223     },
10224     
10225      /**
10226      * Mark this field as invalid
10227      * @param {String} msg The validation message
10228      */
10229     markInvalid : function(msg)
10230     {
10231         if(!this.el  || this.preventMark){ // not rendered
10232             return;
10233         }
10234         
10235         this.el.removeClass([this.invalidClass, this.validClass]);
10236         
10237         var feedback = this.el.select('.form-control-feedback', true).first();
10238             
10239         if(feedback){
10240             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10241         }
10242
10243         if(this.disabled || this.allowBlank){
10244             return;
10245         }
10246         
10247         var label = this.el.select('label', true).first();
10248         var icon = this.el.select('i.fa-star', true).first();
10249         
10250         if(!this.getValue().length && label && !icon){
10251             this.el.createChild({
10252                 tag : 'i',
10253                 cls : 'text-danger fa fa-lg fa-star',
10254                 tooltip : 'This field is required',
10255                 style : 'margin-right:5px;'
10256             }, label, true);
10257         }
10258
10259         this.el.addClass(this.invalidClass);
10260         
10261         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10262             
10263             var feedback = this.el.select('.form-control-feedback', true).first();
10264             
10265             if(feedback){
10266                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10267                 
10268                 if(this.getValue().length || this.forceFeedback){
10269                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10270                 }
10271                 
10272             }
10273             
10274         }
10275         
10276         this.fireEvent('invalid', this, msg);
10277     }
10278 });
10279
10280  
10281 /*
10282  * - LGPL
10283  *
10284  * trigger field - base class for combo..
10285  * 
10286  */
10287  
10288 /**
10289  * @class Roo.bootstrap.TriggerField
10290  * @extends Roo.bootstrap.Input
10291  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10292  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10293  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10294  * for which you can provide a custom implementation.  For example:
10295  * <pre><code>
10296 var trigger = new Roo.bootstrap.TriggerField();
10297 trigger.onTriggerClick = myTriggerFn;
10298 trigger.applyTo('my-field');
10299 </code></pre>
10300  *
10301  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10302  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10303  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10304  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10305  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10306
10307  * @constructor
10308  * Create a new TriggerField.
10309  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10310  * to the base TextField)
10311  */
10312 Roo.bootstrap.TriggerField = function(config){
10313     this.mimicing = false;
10314     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10315 };
10316
10317 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10318     /**
10319      * @cfg {String} triggerClass A CSS class to apply to the trigger
10320      */
10321      /**
10322      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10323      */
10324     hideTrigger:false,
10325
10326     /**
10327      * @cfg {Boolean} removable (true|false) special filter default false
10328      */
10329     removable : false,
10330     
10331     /** @cfg {Boolean} grow @hide */
10332     /** @cfg {Number} growMin @hide */
10333     /** @cfg {Number} growMax @hide */
10334
10335     /**
10336      * @hide 
10337      * @method
10338      */
10339     autoSize: Roo.emptyFn,
10340     // private
10341     monitorTab : true,
10342     // private
10343     deferHeight : true,
10344
10345     
10346     actionMode : 'wrap',
10347     
10348     caret : false,
10349     
10350     
10351     getAutoCreate : function(){
10352        
10353         var align = this.labelAlign || this.parentLabelAlign();
10354         
10355         var id = Roo.id();
10356         
10357         var cfg = {
10358             cls: 'form-group' //input-group
10359         };
10360         
10361         
10362         var input =  {
10363             tag: 'input',
10364             id : id,
10365             type : this.inputType,
10366             cls : 'form-control',
10367             autocomplete: 'new-password',
10368             placeholder : this.placeholder || '' 
10369             
10370         };
10371         if (this.name) {
10372             input.name = this.name;
10373         }
10374         if (this.size) {
10375             input.cls += ' input-' + this.size;
10376         }
10377         
10378         if (this.disabled) {
10379             input.disabled=true;
10380         }
10381         
10382         var inputblock = input;
10383         
10384         if(this.hasFeedback && !this.allowBlank){
10385             
10386             var feedback = {
10387                 tag: 'span',
10388                 cls: 'glyphicon form-control-feedback'
10389             };
10390             
10391             if(this.removable && !this.editable && !this.tickable){
10392                 inputblock = {
10393                     cls : 'has-feedback',
10394                     cn :  [
10395                         inputblock,
10396                         {
10397                             tag: 'button',
10398                             html : 'x',
10399                             cls : 'roo-combo-removable-btn close'
10400                         },
10401                         feedback
10402                     ] 
10403                 };
10404             } else {
10405                 inputblock = {
10406                     cls : 'has-feedback',
10407                     cn :  [
10408                         inputblock,
10409                         feedback
10410                     ] 
10411                 };
10412             }
10413
10414         } else {
10415             if(this.removable && !this.editable && !this.tickable){
10416                 inputblock = {
10417                     cls : 'roo-removable',
10418                     cn :  [
10419                         inputblock,
10420                         {
10421                             tag: 'button',
10422                             html : 'x',
10423                             cls : 'roo-combo-removable-btn close'
10424                         }
10425                     ] 
10426                 };
10427             }
10428         }
10429         
10430         if (this.before || this.after) {
10431             
10432             inputblock = {
10433                 cls : 'input-group',
10434                 cn :  [] 
10435             };
10436             if (this.before) {
10437                 inputblock.cn.push({
10438                     tag :'span',
10439                     cls : 'input-group-addon input-group-prepend input-group-text',
10440                     html : this.before
10441                 });
10442             }
10443             
10444             inputblock.cn.push(input);
10445             
10446             if(this.hasFeedback && !this.allowBlank){
10447                 inputblock.cls += ' has-feedback';
10448                 inputblock.cn.push(feedback);
10449             }
10450             
10451             if (this.after) {
10452                 inputblock.cn.push({
10453                     tag :'span',
10454                     cls : 'input-group-addon input-group-append input-group-text',
10455                     html : this.after
10456                 });
10457             }
10458             
10459         };
10460         
10461       
10462         
10463         var ibwrap = inputblock;
10464         
10465         if(this.multiple){
10466             ibwrap = {
10467                 tag: 'ul',
10468                 cls: 'roo-select2-choices',
10469                 cn:[
10470                     {
10471                         tag: 'li',
10472                         cls: 'roo-select2-search-field',
10473                         cn: [
10474
10475                             inputblock
10476                         ]
10477                     }
10478                 ]
10479             };
10480                 
10481         }
10482         
10483         var combobox = {
10484             cls: 'roo-select2-container input-group',
10485             cn: [
10486                  {
10487                     tag: 'input',
10488                     type : 'hidden',
10489                     cls: 'form-hidden-field'
10490                 },
10491                 ibwrap
10492             ]
10493         };
10494         
10495         if(!this.multiple && this.showToggleBtn){
10496             
10497             var caret = {
10498                         tag: 'span',
10499                         cls: 'caret'
10500              };
10501             if (this.caret != false) {
10502                 caret = {
10503                      tag: 'i',
10504                      cls: 'fa fa-' + this.caret
10505                 };
10506                 
10507             }
10508             
10509             combobox.cn.push({
10510                 tag :'span',
10511                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10512                 cn : [
10513                     caret,
10514                     {
10515                         tag: 'span',
10516                         cls: 'combobox-clear',
10517                         cn  : [
10518                             {
10519                                 tag : 'i',
10520                                 cls: 'icon-remove'
10521                             }
10522                         ]
10523                     }
10524                 ]
10525
10526             })
10527         }
10528         
10529         if(this.multiple){
10530             combobox.cls += ' roo-select2-container-multi';
10531         }
10532          var indicator = {
10533             tag : 'i',
10534             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10535             tooltip : 'This field is required'
10536         };
10537         if (Roo.bootstrap.version == 4) {
10538             indicator = {
10539                 tag : 'i',
10540                 style : 'display:none'
10541             };
10542         }
10543         
10544         
10545         if (align ==='left' && this.fieldLabel.length) {
10546             
10547             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10548
10549             cfg.cn = [
10550                 indicator,
10551                 {
10552                     tag: 'label',
10553                     'for' :  id,
10554                     cls : 'control-label',
10555                     html : this.fieldLabel
10556
10557                 },
10558                 {
10559                     cls : "", 
10560                     cn: [
10561                         combobox
10562                     ]
10563                 }
10564
10565             ];
10566             
10567             var labelCfg = cfg.cn[1];
10568             var contentCfg = cfg.cn[2];
10569             
10570             if(this.indicatorpos == 'right'){
10571                 cfg.cn = [
10572                     {
10573                         tag: 'label',
10574                         'for' :  id,
10575                         cls : 'control-label',
10576                         cn : [
10577                             {
10578                                 tag : 'span',
10579                                 html : this.fieldLabel
10580                             },
10581                             indicator
10582                         ]
10583                     },
10584                     {
10585                         cls : "", 
10586                         cn: [
10587                             combobox
10588                         ]
10589                     }
10590
10591                 ];
10592                 
10593                 labelCfg = cfg.cn[0];
10594                 contentCfg = cfg.cn[1];
10595             }
10596             
10597             if(this.labelWidth > 12){
10598                 labelCfg.style = "width: " + this.labelWidth + 'px';
10599             }
10600             
10601             if(this.labelWidth < 13 && this.labelmd == 0){
10602                 this.labelmd = this.labelWidth;
10603             }
10604             
10605             if(this.labellg > 0){
10606                 labelCfg.cls += ' col-lg-' + this.labellg;
10607                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10608             }
10609             
10610             if(this.labelmd > 0){
10611                 labelCfg.cls += ' col-md-' + this.labelmd;
10612                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10613             }
10614             
10615             if(this.labelsm > 0){
10616                 labelCfg.cls += ' col-sm-' + this.labelsm;
10617                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10618             }
10619             
10620             if(this.labelxs > 0){
10621                 labelCfg.cls += ' col-xs-' + this.labelxs;
10622                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10623             }
10624             
10625         } else if ( this.fieldLabel.length) {
10626 //                Roo.log(" label");
10627             cfg.cn = [
10628                 indicator,
10629                {
10630                    tag: 'label',
10631                    //cls : 'input-group-addon',
10632                    html : this.fieldLabel
10633
10634                },
10635
10636                combobox
10637
10638             ];
10639             
10640             if(this.indicatorpos == 'right'){
10641                 
10642                 cfg.cn = [
10643                     {
10644                        tag: 'label',
10645                        cn : [
10646                            {
10647                                tag : 'span',
10648                                html : this.fieldLabel
10649                            },
10650                            indicator
10651                        ]
10652
10653                     },
10654                     combobox
10655
10656                 ];
10657
10658             }
10659
10660         } else {
10661             
10662 //                Roo.log(" no label && no align");
10663                 cfg = combobox
10664                      
10665                 
10666         }
10667         
10668         var settings=this;
10669         ['xs','sm','md','lg'].map(function(size){
10670             if (settings[size]) {
10671                 cfg.cls += ' col-' + size + '-' + settings[size];
10672             }
10673         });
10674         
10675         return cfg;
10676         
10677     },
10678     
10679     
10680     
10681     // private
10682     onResize : function(w, h){
10683 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10684 //        if(typeof w == 'number'){
10685 //            var x = w - this.trigger.getWidth();
10686 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10687 //            this.trigger.setStyle('left', x+'px');
10688 //        }
10689     },
10690
10691     // private
10692     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10693
10694     // private
10695     getResizeEl : function(){
10696         return this.inputEl();
10697     },
10698
10699     // private
10700     getPositionEl : function(){
10701         return this.inputEl();
10702     },
10703
10704     // private
10705     alignErrorIcon : function(){
10706         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10707     },
10708
10709     // private
10710     initEvents : function(){
10711         
10712         this.createList();
10713         
10714         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10715         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10716         if(!this.multiple && this.showToggleBtn){
10717             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10718             if(this.hideTrigger){
10719                 this.trigger.setDisplayed(false);
10720             }
10721             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10722         }
10723         
10724         if(this.multiple){
10725             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10726         }
10727         
10728         if(this.removable && !this.editable && !this.tickable){
10729             var close = this.closeTriggerEl();
10730             
10731             if(close){
10732                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10733                 close.on('click', this.removeBtnClick, this, close);
10734             }
10735         }
10736         
10737         //this.trigger.addClassOnOver('x-form-trigger-over');
10738         //this.trigger.addClassOnClick('x-form-trigger-click');
10739         
10740         //if(!this.width){
10741         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10742         //}
10743     },
10744     
10745     closeTriggerEl : function()
10746     {
10747         var close = this.el.select('.roo-combo-removable-btn', true).first();
10748         return close ? close : false;
10749     },
10750     
10751     removeBtnClick : function(e, h, el)
10752     {
10753         e.preventDefault();
10754         
10755         if(this.fireEvent("remove", this) !== false){
10756             this.reset();
10757             this.fireEvent("afterremove", this)
10758         }
10759     },
10760     
10761     createList : function()
10762     {
10763         this.list = Roo.get(document.body).createChild({
10764             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10765             cls: 'typeahead typeahead-long dropdown-menu',
10766             style: 'display:none'
10767         });
10768         
10769         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10770         
10771     },
10772
10773     // private
10774     initTrigger : function(){
10775        
10776     },
10777
10778     // private
10779     onDestroy : function(){
10780         if(this.trigger){
10781             this.trigger.removeAllListeners();
10782           //  this.trigger.remove();
10783         }
10784         //if(this.wrap){
10785         //    this.wrap.remove();
10786         //}
10787         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10788     },
10789
10790     // private
10791     onFocus : function(){
10792         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10793         /*
10794         if(!this.mimicing){
10795             this.wrap.addClass('x-trigger-wrap-focus');
10796             this.mimicing = true;
10797             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10798             if(this.monitorTab){
10799                 this.el.on("keydown", this.checkTab, this);
10800             }
10801         }
10802         */
10803     },
10804
10805     // private
10806     checkTab : function(e){
10807         if(e.getKey() == e.TAB){
10808             this.triggerBlur();
10809         }
10810     },
10811
10812     // private
10813     onBlur : function(){
10814         // do nothing
10815     },
10816
10817     // private
10818     mimicBlur : function(e, t){
10819         /*
10820         if(!this.wrap.contains(t) && this.validateBlur()){
10821             this.triggerBlur();
10822         }
10823         */
10824     },
10825
10826     // private
10827     triggerBlur : function(){
10828         this.mimicing = false;
10829         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10830         if(this.monitorTab){
10831             this.el.un("keydown", this.checkTab, this);
10832         }
10833         //this.wrap.removeClass('x-trigger-wrap-focus');
10834         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10835     },
10836
10837     // private
10838     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10839     validateBlur : function(e, t){
10840         return true;
10841     },
10842
10843     // private
10844     onDisable : function(){
10845         this.inputEl().dom.disabled = true;
10846         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10847         //if(this.wrap){
10848         //    this.wrap.addClass('x-item-disabled');
10849         //}
10850     },
10851
10852     // private
10853     onEnable : function(){
10854         this.inputEl().dom.disabled = false;
10855         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10856         //if(this.wrap){
10857         //    this.el.removeClass('x-item-disabled');
10858         //}
10859     },
10860
10861     // private
10862     onShow : function(){
10863         var ae = this.getActionEl();
10864         
10865         if(ae){
10866             ae.dom.style.display = '';
10867             ae.dom.style.visibility = 'visible';
10868         }
10869     },
10870
10871     // private
10872     
10873     onHide : function(){
10874         var ae = this.getActionEl();
10875         ae.dom.style.display = 'none';
10876     },
10877
10878     /**
10879      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10880      * by an implementing function.
10881      * @method
10882      * @param {EventObject} e
10883      */
10884     onTriggerClick : Roo.emptyFn
10885 });
10886  /*
10887  * Based on:
10888  * Ext JS Library 1.1.1
10889  * Copyright(c) 2006-2007, Ext JS, LLC.
10890  *
10891  * Originally Released Under LGPL - original licence link has changed is not relivant.
10892  *
10893  * Fork - LGPL
10894  * <script type="text/javascript">
10895  */
10896
10897
10898 /**
10899  * @class Roo.data.SortTypes
10900  * @singleton
10901  * Defines the default sorting (casting?) comparison functions used when sorting data.
10902  */
10903 Roo.data.SortTypes = {
10904     /**
10905      * Default sort that does nothing
10906      * @param {Mixed} s The value being converted
10907      * @return {Mixed} The comparison value
10908      */
10909     none : function(s){
10910         return s;
10911     },
10912     
10913     /**
10914      * The regular expression used to strip tags
10915      * @type {RegExp}
10916      * @property
10917      */
10918     stripTagsRE : /<\/?[^>]+>/gi,
10919     
10920     /**
10921      * Strips all HTML tags to sort on text only
10922      * @param {Mixed} s The value being converted
10923      * @return {String} The comparison value
10924      */
10925     asText : function(s){
10926         return String(s).replace(this.stripTagsRE, "");
10927     },
10928     
10929     /**
10930      * Strips all HTML tags to sort on text only - Case insensitive
10931      * @param {Mixed} s The value being converted
10932      * @return {String} The comparison value
10933      */
10934     asUCText : function(s){
10935         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10936     },
10937     
10938     /**
10939      * Case insensitive string
10940      * @param {Mixed} s The value being converted
10941      * @return {String} The comparison value
10942      */
10943     asUCString : function(s) {
10944         return String(s).toUpperCase();
10945     },
10946     
10947     /**
10948      * Date sorting
10949      * @param {Mixed} s The value being converted
10950      * @return {Number} The comparison value
10951      */
10952     asDate : function(s) {
10953         if(!s){
10954             return 0;
10955         }
10956         if(s instanceof Date){
10957             return s.getTime();
10958         }
10959         return Date.parse(String(s));
10960     },
10961     
10962     /**
10963      * Float sorting
10964      * @param {Mixed} s The value being converted
10965      * @return {Float} The comparison value
10966      */
10967     asFloat : function(s) {
10968         var val = parseFloat(String(s).replace(/,/g, ""));
10969         if(isNaN(val)) {
10970             val = 0;
10971         }
10972         return val;
10973     },
10974     
10975     /**
10976      * Integer sorting
10977      * @param {Mixed} s The value being converted
10978      * @return {Number} The comparison value
10979      */
10980     asInt : function(s) {
10981         var val = parseInt(String(s).replace(/,/g, ""));
10982         if(isNaN(val)) {
10983             val = 0;
10984         }
10985         return val;
10986     }
10987 };/*
10988  * Based on:
10989  * Ext JS Library 1.1.1
10990  * Copyright(c) 2006-2007, Ext JS, LLC.
10991  *
10992  * Originally Released Under LGPL - original licence link has changed is not relivant.
10993  *
10994  * Fork - LGPL
10995  * <script type="text/javascript">
10996  */
10997
10998 /**
10999 * @class Roo.data.Record
11000  * Instances of this class encapsulate both record <em>definition</em> information, and record
11001  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11002  * to access Records cached in an {@link Roo.data.Store} object.<br>
11003  * <p>
11004  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11005  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11006  * objects.<br>
11007  * <p>
11008  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11009  * @constructor
11010  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11011  * {@link #create}. The parameters are the same.
11012  * @param {Array} data An associative Array of data values keyed by the field name.
11013  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11014  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11015  * not specified an integer id is generated.
11016  */
11017 Roo.data.Record = function(data, id){
11018     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11019     this.data = data;
11020 };
11021
11022 /**
11023  * Generate a constructor for a specific record layout.
11024  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11025  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11026  * Each field definition object may contain the following properties: <ul>
11027  * <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,
11028  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11029  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11030  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11031  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11032  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11033  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11034  * this may be omitted.</p></li>
11035  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11036  * <ul><li>auto (Default, implies no conversion)</li>
11037  * <li>string</li>
11038  * <li>int</li>
11039  * <li>float</li>
11040  * <li>boolean</li>
11041  * <li>date</li></ul></p></li>
11042  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11043  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11044  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11045  * by the Reader into an object that will be stored in the Record. It is passed the
11046  * following parameters:<ul>
11047  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11048  * </ul></p></li>
11049  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11050  * </ul>
11051  * <br>usage:<br><pre><code>
11052 var TopicRecord = Roo.data.Record.create(
11053     {name: 'title', mapping: 'topic_title'},
11054     {name: 'author', mapping: 'username'},
11055     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11056     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11057     {name: 'lastPoster', mapping: 'user2'},
11058     {name: 'excerpt', mapping: 'post_text'}
11059 );
11060
11061 var myNewRecord = new TopicRecord({
11062     title: 'Do my job please',
11063     author: 'noobie',
11064     totalPosts: 1,
11065     lastPost: new Date(),
11066     lastPoster: 'Animal',
11067     excerpt: 'No way dude!'
11068 });
11069 myStore.add(myNewRecord);
11070 </code></pre>
11071  * @method create
11072  * @static
11073  */
11074 Roo.data.Record.create = function(o){
11075     var f = function(){
11076         f.superclass.constructor.apply(this, arguments);
11077     };
11078     Roo.extend(f, Roo.data.Record);
11079     var p = f.prototype;
11080     p.fields = new Roo.util.MixedCollection(false, function(field){
11081         return field.name;
11082     });
11083     for(var i = 0, len = o.length; i < len; i++){
11084         p.fields.add(new Roo.data.Field(o[i]));
11085     }
11086     f.getField = function(name){
11087         return p.fields.get(name);  
11088     };
11089     return f;
11090 };
11091
11092 Roo.data.Record.AUTO_ID = 1000;
11093 Roo.data.Record.EDIT = 'edit';
11094 Roo.data.Record.REJECT = 'reject';
11095 Roo.data.Record.COMMIT = 'commit';
11096
11097 Roo.data.Record.prototype = {
11098     /**
11099      * Readonly flag - true if this record has been modified.
11100      * @type Boolean
11101      */
11102     dirty : false,
11103     editing : false,
11104     error: null,
11105     modified: null,
11106
11107     // private
11108     join : function(store){
11109         this.store = store;
11110     },
11111
11112     /**
11113      * Set the named field to the specified value.
11114      * @param {String} name The name of the field to set.
11115      * @param {Object} value The value to set the field to.
11116      */
11117     set : function(name, value){
11118         if(this.data[name] == value){
11119             return;
11120         }
11121         this.dirty = true;
11122         if(!this.modified){
11123             this.modified = {};
11124         }
11125         if(typeof this.modified[name] == 'undefined'){
11126             this.modified[name] = this.data[name];
11127         }
11128         this.data[name] = value;
11129         if(!this.editing && this.store){
11130             this.store.afterEdit(this);
11131         }       
11132     },
11133
11134     /**
11135      * Get the value of the named field.
11136      * @param {String} name The name of the field to get the value of.
11137      * @return {Object} The value of the field.
11138      */
11139     get : function(name){
11140         return this.data[name]; 
11141     },
11142
11143     // private
11144     beginEdit : function(){
11145         this.editing = true;
11146         this.modified = {}; 
11147     },
11148
11149     // private
11150     cancelEdit : function(){
11151         this.editing = false;
11152         delete this.modified;
11153     },
11154
11155     // private
11156     endEdit : function(){
11157         this.editing = false;
11158         if(this.dirty && this.store){
11159             this.store.afterEdit(this);
11160         }
11161     },
11162
11163     /**
11164      * Usually called by the {@link Roo.data.Store} which owns the Record.
11165      * Rejects all changes made to the Record since either creation, or the last commit operation.
11166      * Modified fields are reverted to their original values.
11167      * <p>
11168      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11169      * of reject operations.
11170      */
11171     reject : function(){
11172         var m = this.modified;
11173         for(var n in m){
11174             if(typeof m[n] != "function"){
11175                 this.data[n] = m[n];
11176             }
11177         }
11178         this.dirty = false;
11179         delete this.modified;
11180         this.editing = false;
11181         if(this.store){
11182             this.store.afterReject(this);
11183         }
11184     },
11185
11186     /**
11187      * Usually called by the {@link Roo.data.Store} which owns the Record.
11188      * Commits all changes made to the Record since either creation, or the last commit operation.
11189      * <p>
11190      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11191      * of commit operations.
11192      */
11193     commit : function(){
11194         this.dirty = false;
11195         delete this.modified;
11196         this.editing = false;
11197         if(this.store){
11198             this.store.afterCommit(this);
11199         }
11200     },
11201
11202     // private
11203     hasError : function(){
11204         return this.error != null;
11205     },
11206
11207     // private
11208     clearError : function(){
11209         this.error = null;
11210     },
11211
11212     /**
11213      * Creates a copy of this record.
11214      * @param {String} id (optional) A new record id if you don't want to use this record's id
11215      * @return {Record}
11216      */
11217     copy : function(newId) {
11218         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11219     }
11220 };/*
11221  * Based on:
11222  * Ext JS Library 1.1.1
11223  * Copyright(c) 2006-2007, Ext JS, LLC.
11224  *
11225  * Originally Released Under LGPL - original licence link has changed is not relivant.
11226  *
11227  * Fork - LGPL
11228  * <script type="text/javascript">
11229  */
11230
11231
11232
11233 /**
11234  * @class Roo.data.Store
11235  * @extends Roo.util.Observable
11236  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11237  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11238  * <p>
11239  * 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
11240  * has no knowledge of the format of the data returned by the Proxy.<br>
11241  * <p>
11242  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11243  * instances from the data object. These records are cached and made available through accessor functions.
11244  * @constructor
11245  * Creates a new Store.
11246  * @param {Object} config A config object containing the objects needed for the Store to access data,
11247  * and read the data into Records.
11248  */
11249 Roo.data.Store = function(config){
11250     this.data = new Roo.util.MixedCollection(false);
11251     this.data.getKey = function(o){
11252         return o.id;
11253     };
11254     this.baseParams = {};
11255     // private
11256     this.paramNames = {
11257         "start" : "start",
11258         "limit" : "limit",
11259         "sort" : "sort",
11260         "dir" : "dir",
11261         "multisort" : "_multisort"
11262     };
11263
11264     if(config && config.data){
11265         this.inlineData = config.data;
11266         delete config.data;
11267     }
11268
11269     Roo.apply(this, config);
11270     
11271     if(this.reader){ // reader passed
11272         this.reader = Roo.factory(this.reader, Roo.data);
11273         this.reader.xmodule = this.xmodule || false;
11274         if(!this.recordType){
11275             this.recordType = this.reader.recordType;
11276         }
11277         if(this.reader.onMetaChange){
11278             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11279         }
11280     }
11281
11282     if(this.recordType){
11283         this.fields = this.recordType.prototype.fields;
11284     }
11285     this.modified = [];
11286
11287     this.addEvents({
11288         /**
11289          * @event datachanged
11290          * Fires when the data cache has changed, and a widget which is using this Store
11291          * as a Record cache should refresh its view.
11292          * @param {Store} this
11293          */
11294         datachanged : true,
11295         /**
11296          * @event metachange
11297          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11298          * @param {Store} this
11299          * @param {Object} meta The JSON metadata
11300          */
11301         metachange : true,
11302         /**
11303          * @event add
11304          * Fires when Records have been added to the Store
11305          * @param {Store} this
11306          * @param {Roo.data.Record[]} records The array of Records added
11307          * @param {Number} index The index at which the record(s) were added
11308          */
11309         add : true,
11310         /**
11311          * @event remove
11312          * Fires when a Record has been removed from the Store
11313          * @param {Store} this
11314          * @param {Roo.data.Record} record The Record that was removed
11315          * @param {Number} index The index at which the record was removed
11316          */
11317         remove : true,
11318         /**
11319          * @event update
11320          * Fires when a Record has been updated
11321          * @param {Store} this
11322          * @param {Roo.data.Record} record The Record that was updated
11323          * @param {String} operation The update operation being performed.  Value may be one of:
11324          * <pre><code>
11325  Roo.data.Record.EDIT
11326  Roo.data.Record.REJECT
11327  Roo.data.Record.COMMIT
11328          * </code></pre>
11329          */
11330         update : true,
11331         /**
11332          * @event clear
11333          * Fires when the data cache has been cleared.
11334          * @param {Store} this
11335          */
11336         clear : true,
11337         /**
11338          * @event beforeload
11339          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11340          * the load action will be canceled.
11341          * @param {Store} this
11342          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11343          */
11344         beforeload : true,
11345         /**
11346          * @event beforeloadadd
11347          * Fires after a new set of Records has been loaded.
11348          * @param {Store} this
11349          * @param {Roo.data.Record[]} records The Records that were loaded
11350          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11351          */
11352         beforeloadadd : true,
11353         /**
11354          * @event load
11355          * Fires after a new set of Records has been loaded, before they are added to the store.
11356          * @param {Store} this
11357          * @param {Roo.data.Record[]} records The Records that were loaded
11358          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11359          * @params {Object} return from reader
11360          */
11361         load : true,
11362         /**
11363          * @event loadexception
11364          * Fires if an exception occurs in the Proxy during loading.
11365          * Called with the signature of the Proxy's "loadexception" event.
11366          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11367          * 
11368          * @param {Proxy} 
11369          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11370          * @param {Object} load options 
11371          * @param {Object} jsonData from your request (normally this contains the Exception)
11372          */
11373         loadexception : true
11374     });
11375     
11376     if(this.proxy){
11377         this.proxy = Roo.factory(this.proxy, Roo.data);
11378         this.proxy.xmodule = this.xmodule || false;
11379         this.relayEvents(this.proxy,  ["loadexception"]);
11380     }
11381     this.sortToggle = {};
11382     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11383
11384     Roo.data.Store.superclass.constructor.call(this);
11385
11386     if(this.inlineData){
11387         this.loadData(this.inlineData);
11388         delete this.inlineData;
11389     }
11390 };
11391
11392 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11393      /**
11394     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11395     * without a remote query - used by combo/forms at present.
11396     */
11397     
11398     /**
11399     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11400     */
11401     /**
11402     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11403     */
11404     /**
11405     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11406     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11407     */
11408     /**
11409     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11410     * on any HTTP request
11411     */
11412     /**
11413     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11414     */
11415     /**
11416     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11417     */
11418     multiSort: false,
11419     /**
11420     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11421     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11422     */
11423     remoteSort : false,
11424
11425     /**
11426     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11427      * loaded or when a record is removed. (defaults to false).
11428     */
11429     pruneModifiedRecords : false,
11430
11431     // private
11432     lastOptions : null,
11433
11434     /**
11435      * Add Records to the Store and fires the add event.
11436      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11437      */
11438     add : function(records){
11439         records = [].concat(records);
11440         for(var i = 0, len = records.length; i < len; i++){
11441             records[i].join(this);
11442         }
11443         var index = this.data.length;
11444         this.data.addAll(records);
11445         this.fireEvent("add", this, records, index);
11446     },
11447
11448     /**
11449      * Remove a Record from the Store and fires the remove event.
11450      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11451      */
11452     remove : function(record){
11453         var index = this.data.indexOf(record);
11454         this.data.removeAt(index);
11455  
11456         if(this.pruneModifiedRecords){
11457             this.modified.remove(record);
11458         }
11459         this.fireEvent("remove", this, record, index);
11460     },
11461
11462     /**
11463      * Remove all Records from the Store and fires the clear event.
11464      */
11465     removeAll : function(){
11466         this.data.clear();
11467         if(this.pruneModifiedRecords){
11468             this.modified = [];
11469         }
11470         this.fireEvent("clear", this);
11471     },
11472
11473     /**
11474      * Inserts Records to the Store at the given index and fires the add event.
11475      * @param {Number} index The start index at which to insert the passed Records.
11476      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11477      */
11478     insert : function(index, records){
11479         records = [].concat(records);
11480         for(var i = 0, len = records.length; i < len; i++){
11481             this.data.insert(index, records[i]);
11482             records[i].join(this);
11483         }
11484         this.fireEvent("add", this, records, index);
11485     },
11486
11487     /**
11488      * Get the index within the cache of the passed Record.
11489      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11490      * @return {Number} The index of the passed Record. Returns -1 if not found.
11491      */
11492     indexOf : function(record){
11493         return this.data.indexOf(record);
11494     },
11495
11496     /**
11497      * Get the index within the cache of the Record with the passed id.
11498      * @param {String} id The id of the Record to find.
11499      * @return {Number} The index of the Record. Returns -1 if not found.
11500      */
11501     indexOfId : function(id){
11502         return this.data.indexOfKey(id);
11503     },
11504
11505     /**
11506      * Get the Record with the specified id.
11507      * @param {String} id The id of the Record to find.
11508      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11509      */
11510     getById : function(id){
11511         return this.data.key(id);
11512     },
11513
11514     /**
11515      * Get the Record at the specified index.
11516      * @param {Number} index The index of the Record to find.
11517      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11518      */
11519     getAt : function(index){
11520         return this.data.itemAt(index);
11521     },
11522
11523     /**
11524      * Returns a range of Records between specified indices.
11525      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11526      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11527      * @return {Roo.data.Record[]} An array of Records
11528      */
11529     getRange : function(start, end){
11530         return this.data.getRange(start, end);
11531     },
11532
11533     // private
11534     storeOptions : function(o){
11535         o = Roo.apply({}, o);
11536         delete o.callback;
11537         delete o.scope;
11538         this.lastOptions = o;
11539     },
11540
11541     /**
11542      * Loads the Record cache from the configured Proxy using the configured Reader.
11543      * <p>
11544      * If using remote paging, then the first load call must specify the <em>start</em>
11545      * and <em>limit</em> properties in the options.params property to establish the initial
11546      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11547      * <p>
11548      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11549      * and this call will return before the new data has been loaded. Perform any post-processing
11550      * in a callback function, or in a "load" event handler.</strong>
11551      * <p>
11552      * @param {Object} options An object containing properties which control loading options:<ul>
11553      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11554      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11555      * passed the following arguments:<ul>
11556      * <li>r : Roo.data.Record[]</li>
11557      * <li>options: Options object from the load call</li>
11558      * <li>success: Boolean success indicator</li></ul></li>
11559      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11560      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11561      * </ul>
11562      */
11563     load : function(options){
11564         options = options || {};
11565         if(this.fireEvent("beforeload", this, options) !== false){
11566             this.storeOptions(options);
11567             var p = Roo.apply(options.params || {}, this.baseParams);
11568             // if meta was not loaded from remote source.. try requesting it.
11569             if (!this.reader.metaFromRemote) {
11570                 p._requestMeta = 1;
11571             }
11572             if(this.sortInfo && this.remoteSort){
11573                 var pn = this.paramNames;
11574                 p[pn["sort"]] = this.sortInfo.field;
11575                 p[pn["dir"]] = this.sortInfo.direction;
11576             }
11577             if (this.multiSort) {
11578                 var pn = this.paramNames;
11579                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11580             }
11581             
11582             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11583         }
11584     },
11585
11586     /**
11587      * Reloads the Record cache from the configured Proxy using the configured Reader and
11588      * the options from the last load operation performed.
11589      * @param {Object} options (optional) An object containing properties which may override the options
11590      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11591      * the most recently used options are reused).
11592      */
11593     reload : function(options){
11594         this.load(Roo.applyIf(options||{}, this.lastOptions));
11595     },
11596
11597     // private
11598     // Called as a callback by the Reader during a load operation.
11599     loadRecords : function(o, options, success){
11600         if(!o || success === false){
11601             if(success !== false){
11602                 this.fireEvent("load", this, [], options, o);
11603             }
11604             if(options.callback){
11605                 options.callback.call(options.scope || this, [], options, false);
11606             }
11607             return;
11608         }
11609         // if data returned failure - throw an exception.
11610         if (o.success === false) {
11611             // show a message if no listener is registered.
11612             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11613                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11614             }
11615             // loadmask wil be hooked into this..
11616             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11617             return;
11618         }
11619         var r = o.records, t = o.totalRecords || r.length;
11620         
11621         this.fireEvent("beforeloadadd", this, r, options, o);
11622         
11623         if(!options || options.add !== true){
11624             if(this.pruneModifiedRecords){
11625                 this.modified = [];
11626             }
11627             for(var i = 0, len = r.length; i < len; i++){
11628                 r[i].join(this);
11629             }
11630             if(this.snapshot){
11631                 this.data = this.snapshot;
11632                 delete this.snapshot;
11633             }
11634             this.data.clear();
11635             this.data.addAll(r);
11636             this.totalLength = t;
11637             this.applySort();
11638             this.fireEvent("datachanged", this);
11639         }else{
11640             this.totalLength = Math.max(t, this.data.length+r.length);
11641             this.add(r);
11642         }
11643         
11644         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11645                 
11646             var e = new Roo.data.Record({});
11647
11648             e.set(this.parent.displayField, this.parent.emptyTitle);
11649             e.set(this.parent.valueField, '');
11650
11651             this.insert(0, e);
11652         }
11653             
11654         this.fireEvent("load", this, r, options, o);
11655         if(options.callback){
11656             options.callback.call(options.scope || this, r, options, true);
11657         }
11658     },
11659
11660
11661     /**
11662      * Loads data from a passed data block. A Reader which understands the format of the data
11663      * must have been configured in the constructor.
11664      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11665      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11666      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11667      */
11668     loadData : function(o, append){
11669         var r = this.reader.readRecords(o);
11670         this.loadRecords(r, {add: append}, true);
11671     },
11672
11673     /**
11674      * Gets the number of cached records.
11675      * <p>
11676      * <em>If using paging, this may not be the total size of the dataset. If the data object
11677      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11678      * the data set size</em>
11679      */
11680     getCount : function(){
11681         return this.data.length || 0;
11682     },
11683
11684     /**
11685      * Gets the total number of records in the dataset as returned by the server.
11686      * <p>
11687      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11688      * the dataset size</em>
11689      */
11690     getTotalCount : function(){
11691         return this.totalLength || 0;
11692     },
11693
11694     /**
11695      * Returns the sort state of the Store as an object with two properties:
11696      * <pre><code>
11697  field {String} The name of the field by which the Records are sorted
11698  direction {String} The sort order, "ASC" or "DESC"
11699      * </code></pre>
11700      */
11701     getSortState : function(){
11702         return this.sortInfo;
11703     },
11704
11705     // private
11706     applySort : function(){
11707         if(this.sortInfo && !this.remoteSort){
11708             var s = this.sortInfo, f = s.field;
11709             var st = this.fields.get(f).sortType;
11710             var fn = function(r1, r2){
11711                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11712                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11713             };
11714             this.data.sort(s.direction, fn);
11715             if(this.snapshot && this.snapshot != this.data){
11716                 this.snapshot.sort(s.direction, fn);
11717             }
11718         }
11719     },
11720
11721     /**
11722      * Sets the default sort column and order to be used by the next load operation.
11723      * @param {String} fieldName The name of the field to sort by.
11724      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11725      */
11726     setDefaultSort : function(field, dir){
11727         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11728     },
11729
11730     /**
11731      * Sort the Records.
11732      * If remote sorting is used, the sort is performed on the server, and the cache is
11733      * reloaded. If local sorting is used, the cache is sorted internally.
11734      * @param {String} fieldName The name of the field to sort by.
11735      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11736      */
11737     sort : function(fieldName, dir){
11738         var f = this.fields.get(fieldName);
11739         if(!dir){
11740             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11741             
11742             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11743                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11744             }else{
11745                 dir = f.sortDir;
11746             }
11747         }
11748         this.sortToggle[f.name] = dir;
11749         this.sortInfo = {field: f.name, direction: dir};
11750         if(!this.remoteSort){
11751             this.applySort();
11752             this.fireEvent("datachanged", this);
11753         }else{
11754             this.load(this.lastOptions);
11755         }
11756     },
11757
11758     /**
11759      * Calls the specified function for each of the Records in the cache.
11760      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11761      * Returning <em>false</em> aborts and exits the iteration.
11762      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11763      */
11764     each : function(fn, scope){
11765         this.data.each(fn, scope);
11766     },
11767
11768     /**
11769      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11770      * (e.g., during paging).
11771      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11772      */
11773     getModifiedRecords : function(){
11774         return this.modified;
11775     },
11776
11777     // private
11778     createFilterFn : function(property, value, anyMatch){
11779         if(!value.exec){ // not a regex
11780             value = String(value);
11781             if(value.length == 0){
11782                 return false;
11783             }
11784             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11785         }
11786         return function(r){
11787             return value.test(r.data[property]);
11788         };
11789     },
11790
11791     /**
11792      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11793      * @param {String} property A field on your records
11794      * @param {Number} start The record index to start at (defaults to 0)
11795      * @param {Number} end The last record index to include (defaults to length - 1)
11796      * @return {Number} The sum
11797      */
11798     sum : function(property, start, end){
11799         var rs = this.data.items, v = 0;
11800         start = start || 0;
11801         end = (end || end === 0) ? end : rs.length-1;
11802
11803         for(var i = start; i <= end; i++){
11804             v += (rs[i].data[property] || 0);
11805         }
11806         return v;
11807     },
11808
11809     /**
11810      * Filter the records by a specified property.
11811      * @param {String} field A field on your records
11812      * @param {String/RegExp} value Either a string that the field
11813      * should start with or a RegExp to test against the field
11814      * @param {Boolean} anyMatch True to match any part not just the beginning
11815      */
11816     filter : function(property, value, anyMatch){
11817         var fn = this.createFilterFn(property, value, anyMatch);
11818         return fn ? this.filterBy(fn) : this.clearFilter();
11819     },
11820
11821     /**
11822      * Filter by a function. The specified function will be called with each
11823      * record in this data source. If the function returns true the record is included,
11824      * otherwise it is filtered.
11825      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11826      * @param {Object} scope (optional) The scope of the function (defaults to this)
11827      */
11828     filterBy : function(fn, scope){
11829         this.snapshot = this.snapshot || this.data;
11830         this.data = this.queryBy(fn, scope||this);
11831         this.fireEvent("datachanged", this);
11832     },
11833
11834     /**
11835      * Query the records by a specified property.
11836      * @param {String} field A field on your records
11837      * @param {String/RegExp} value Either a string that the field
11838      * should start with or a RegExp to test against the field
11839      * @param {Boolean} anyMatch True to match any part not just the beginning
11840      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11841      */
11842     query : function(property, value, anyMatch){
11843         var fn = this.createFilterFn(property, value, anyMatch);
11844         return fn ? this.queryBy(fn) : this.data.clone();
11845     },
11846
11847     /**
11848      * Query by a function. The specified function will be called with each
11849      * record in this data source. If the function returns true the record is included
11850      * in the results.
11851      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11852      * @param {Object} scope (optional) The scope of the function (defaults to this)
11853       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11854      **/
11855     queryBy : function(fn, scope){
11856         var data = this.snapshot || this.data;
11857         return data.filterBy(fn, scope||this);
11858     },
11859
11860     /**
11861      * Collects unique values for a particular dataIndex from this store.
11862      * @param {String} dataIndex The property to collect
11863      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11864      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11865      * @return {Array} An array of the unique values
11866      **/
11867     collect : function(dataIndex, allowNull, bypassFilter){
11868         var d = (bypassFilter === true && this.snapshot) ?
11869                 this.snapshot.items : this.data.items;
11870         var v, sv, r = [], l = {};
11871         for(var i = 0, len = d.length; i < len; i++){
11872             v = d[i].data[dataIndex];
11873             sv = String(v);
11874             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11875                 l[sv] = true;
11876                 r[r.length] = v;
11877             }
11878         }
11879         return r;
11880     },
11881
11882     /**
11883      * Revert to a view of the Record cache with no filtering applied.
11884      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11885      */
11886     clearFilter : function(suppressEvent){
11887         if(this.snapshot && this.snapshot != this.data){
11888             this.data = this.snapshot;
11889             delete this.snapshot;
11890             if(suppressEvent !== true){
11891                 this.fireEvent("datachanged", this);
11892             }
11893         }
11894     },
11895
11896     // private
11897     afterEdit : function(record){
11898         if(this.modified.indexOf(record) == -1){
11899             this.modified.push(record);
11900         }
11901         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11902     },
11903     
11904     // private
11905     afterReject : function(record){
11906         this.modified.remove(record);
11907         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11908     },
11909
11910     // private
11911     afterCommit : function(record){
11912         this.modified.remove(record);
11913         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11914     },
11915
11916     /**
11917      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11918      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11919      */
11920     commitChanges : function(){
11921         var m = this.modified.slice(0);
11922         this.modified = [];
11923         for(var i = 0, len = m.length; i < len; i++){
11924             m[i].commit();
11925         }
11926     },
11927
11928     /**
11929      * Cancel outstanding changes on all changed records.
11930      */
11931     rejectChanges : function(){
11932         var m = this.modified.slice(0);
11933         this.modified = [];
11934         for(var i = 0, len = m.length; i < len; i++){
11935             m[i].reject();
11936         }
11937     },
11938
11939     onMetaChange : function(meta, rtype, o){
11940         this.recordType = rtype;
11941         this.fields = rtype.prototype.fields;
11942         delete this.snapshot;
11943         this.sortInfo = meta.sortInfo || this.sortInfo;
11944         this.modified = [];
11945         this.fireEvent('metachange', this, this.reader.meta);
11946     },
11947     
11948     moveIndex : function(data, type)
11949     {
11950         var index = this.indexOf(data);
11951         
11952         var newIndex = index + type;
11953         
11954         this.remove(data);
11955         
11956         this.insert(newIndex, data);
11957         
11958     }
11959 });/*
11960  * Based on:
11961  * Ext JS Library 1.1.1
11962  * Copyright(c) 2006-2007, Ext JS, LLC.
11963  *
11964  * Originally Released Under LGPL - original licence link has changed is not relivant.
11965  *
11966  * Fork - LGPL
11967  * <script type="text/javascript">
11968  */
11969
11970 /**
11971  * @class Roo.data.SimpleStore
11972  * @extends Roo.data.Store
11973  * Small helper class to make creating Stores from Array data easier.
11974  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11975  * @cfg {Array} fields An array of field definition objects, or field name strings.
11976  * @cfg {Array} data The multi-dimensional array of data
11977  * @constructor
11978  * @param {Object} config
11979  */
11980 Roo.data.SimpleStore = function(config){
11981     Roo.data.SimpleStore.superclass.constructor.call(this, {
11982         isLocal : true,
11983         reader: new Roo.data.ArrayReader({
11984                 id: config.id
11985             },
11986             Roo.data.Record.create(config.fields)
11987         ),
11988         proxy : new Roo.data.MemoryProxy(config.data)
11989     });
11990     this.load();
11991 };
11992 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11993  * Based on:
11994  * Ext JS Library 1.1.1
11995  * Copyright(c) 2006-2007, Ext JS, LLC.
11996  *
11997  * Originally Released Under LGPL - original licence link has changed is not relivant.
11998  *
11999  * Fork - LGPL
12000  * <script type="text/javascript">
12001  */
12002
12003 /**
12004 /**
12005  * @extends Roo.data.Store
12006  * @class Roo.data.JsonStore
12007  * Small helper class to make creating Stores for JSON data easier. <br/>
12008 <pre><code>
12009 var store = new Roo.data.JsonStore({
12010     url: 'get-images.php',
12011     root: 'images',
12012     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12013 });
12014 </code></pre>
12015  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12016  * JsonReader and HttpProxy (unless inline data is provided).</b>
12017  * @cfg {Array} fields An array of field definition objects, or field name strings.
12018  * @constructor
12019  * @param {Object} config
12020  */
12021 Roo.data.JsonStore = function(c){
12022     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12023         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12024         reader: new Roo.data.JsonReader(c, c.fields)
12025     }));
12026 };
12027 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12028  * Based on:
12029  * Ext JS Library 1.1.1
12030  * Copyright(c) 2006-2007, Ext JS, LLC.
12031  *
12032  * Originally Released Under LGPL - original licence link has changed is not relivant.
12033  *
12034  * Fork - LGPL
12035  * <script type="text/javascript">
12036  */
12037
12038  
12039 Roo.data.Field = function(config){
12040     if(typeof config == "string"){
12041         config = {name: config};
12042     }
12043     Roo.apply(this, config);
12044     
12045     if(!this.type){
12046         this.type = "auto";
12047     }
12048     
12049     var st = Roo.data.SortTypes;
12050     // named sortTypes are supported, here we look them up
12051     if(typeof this.sortType == "string"){
12052         this.sortType = st[this.sortType];
12053     }
12054     
12055     // set default sortType for strings and dates
12056     if(!this.sortType){
12057         switch(this.type){
12058             case "string":
12059                 this.sortType = st.asUCString;
12060                 break;
12061             case "date":
12062                 this.sortType = st.asDate;
12063                 break;
12064             default:
12065                 this.sortType = st.none;
12066         }
12067     }
12068
12069     // define once
12070     var stripRe = /[\$,%]/g;
12071
12072     // prebuilt conversion function for this field, instead of
12073     // switching every time we're reading a value
12074     if(!this.convert){
12075         var cv, dateFormat = this.dateFormat;
12076         switch(this.type){
12077             case "":
12078             case "auto":
12079             case undefined:
12080                 cv = function(v){ return v; };
12081                 break;
12082             case "string":
12083                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12084                 break;
12085             case "int":
12086                 cv = function(v){
12087                     return v !== undefined && v !== null && v !== '' ?
12088                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12089                     };
12090                 break;
12091             case "float":
12092                 cv = function(v){
12093                     return v !== undefined && v !== null && v !== '' ?
12094                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12095                     };
12096                 break;
12097             case "bool":
12098             case "boolean":
12099                 cv = function(v){ return v === true || v === "true" || v == 1; };
12100                 break;
12101             case "date":
12102                 cv = function(v){
12103                     if(!v){
12104                         return '';
12105                     }
12106                     if(v instanceof Date){
12107                         return v;
12108                     }
12109                     if(dateFormat){
12110                         if(dateFormat == "timestamp"){
12111                             return new Date(v*1000);
12112                         }
12113                         return Date.parseDate(v, dateFormat);
12114                     }
12115                     var parsed = Date.parse(v);
12116                     return parsed ? new Date(parsed) : null;
12117                 };
12118              break;
12119             
12120         }
12121         this.convert = cv;
12122     }
12123 };
12124
12125 Roo.data.Field.prototype = {
12126     dateFormat: null,
12127     defaultValue: "",
12128     mapping: null,
12129     sortType : null,
12130     sortDir : "ASC"
12131 };/*
12132  * Based on:
12133  * Ext JS Library 1.1.1
12134  * Copyright(c) 2006-2007, Ext JS, LLC.
12135  *
12136  * Originally Released Under LGPL - original licence link has changed is not relivant.
12137  *
12138  * Fork - LGPL
12139  * <script type="text/javascript">
12140  */
12141  
12142 // Base class for reading structured data from a data source.  This class is intended to be
12143 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12144
12145 /**
12146  * @class Roo.data.DataReader
12147  * Base class for reading structured data from a data source.  This class is intended to be
12148  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12149  */
12150
12151 Roo.data.DataReader = function(meta, recordType){
12152     
12153     this.meta = meta;
12154     
12155     this.recordType = recordType instanceof Array ? 
12156         Roo.data.Record.create(recordType) : recordType;
12157 };
12158
12159 Roo.data.DataReader.prototype = {
12160      /**
12161      * Create an empty record
12162      * @param {Object} data (optional) - overlay some values
12163      * @return {Roo.data.Record} record created.
12164      */
12165     newRow :  function(d) {
12166         var da =  {};
12167         this.recordType.prototype.fields.each(function(c) {
12168             switch( c.type) {
12169                 case 'int' : da[c.name] = 0; break;
12170                 case 'date' : da[c.name] = new Date(); break;
12171                 case 'float' : da[c.name] = 0.0; break;
12172                 case 'boolean' : da[c.name] = false; break;
12173                 default : da[c.name] = ""; break;
12174             }
12175             
12176         });
12177         return new this.recordType(Roo.apply(da, d));
12178     }
12179     
12180 };/*
12181  * Based on:
12182  * Ext JS Library 1.1.1
12183  * Copyright(c) 2006-2007, Ext JS, LLC.
12184  *
12185  * Originally Released Under LGPL - original licence link has changed is not relivant.
12186  *
12187  * Fork - LGPL
12188  * <script type="text/javascript">
12189  */
12190
12191 /**
12192  * @class Roo.data.DataProxy
12193  * @extends Roo.data.Observable
12194  * This class is an abstract base class for implementations which provide retrieval of
12195  * unformatted data objects.<br>
12196  * <p>
12197  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12198  * (of the appropriate type which knows how to parse the data object) to provide a block of
12199  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12200  * <p>
12201  * Custom implementations must implement the load method as described in
12202  * {@link Roo.data.HttpProxy#load}.
12203  */
12204 Roo.data.DataProxy = function(){
12205     this.addEvents({
12206         /**
12207          * @event beforeload
12208          * Fires before a network request is made to retrieve a data object.
12209          * @param {Object} This DataProxy object.
12210          * @param {Object} params The params parameter to the load function.
12211          */
12212         beforeload : true,
12213         /**
12214          * @event load
12215          * Fires before the load method's callback is called.
12216          * @param {Object} This DataProxy object.
12217          * @param {Object} o The data object.
12218          * @param {Object} arg The callback argument object passed to the load function.
12219          */
12220         load : true,
12221         /**
12222          * @event loadexception
12223          * Fires if an Exception occurs during data retrieval.
12224          * @param {Object} This DataProxy object.
12225          * @param {Object} o The data object.
12226          * @param {Object} arg The callback argument object passed to the load function.
12227          * @param {Object} e The Exception.
12228          */
12229         loadexception : true
12230     });
12231     Roo.data.DataProxy.superclass.constructor.call(this);
12232 };
12233
12234 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12235
12236     /**
12237      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12238      */
12239 /*
12240  * Based on:
12241  * Ext JS Library 1.1.1
12242  * Copyright(c) 2006-2007, Ext JS, LLC.
12243  *
12244  * Originally Released Under LGPL - original licence link has changed is not relivant.
12245  *
12246  * Fork - LGPL
12247  * <script type="text/javascript">
12248  */
12249 /**
12250  * @class Roo.data.MemoryProxy
12251  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12252  * to the Reader when its load method is called.
12253  * @constructor
12254  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12255  */
12256 Roo.data.MemoryProxy = function(data){
12257     if (data.data) {
12258         data = data.data;
12259     }
12260     Roo.data.MemoryProxy.superclass.constructor.call(this);
12261     this.data = data;
12262 };
12263
12264 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12265     
12266     /**
12267      * Load data from the requested source (in this case an in-memory
12268      * data object passed to the constructor), read the data object into
12269      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12270      * process that block using the passed callback.
12271      * @param {Object} params This parameter is not used by the MemoryProxy class.
12272      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12273      * object into a block of Roo.data.Records.
12274      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12275      * The function must be passed <ul>
12276      * <li>The Record block object</li>
12277      * <li>The "arg" argument from the load function</li>
12278      * <li>A boolean success indicator</li>
12279      * </ul>
12280      * @param {Object} scope The scope in which to call the callback
12281      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12282      */
12283     load : function(params, reader, callback, scope, arg){
12284         params = params || {};
12285         var result;
12286         try {
12287             result = reader.readRecords(this.data);
12288         }catch(e){
12289             this.fireEvent("loadexception", this, arg, null, e);
12290             callback.call(scope, null, arg, false);
12291             return;
12292         }
12293         callback.call(scope, result, arg, true);
12294     },
12295     
12296     // private
12297     update : function(params, records){
12298         
12299     }
12300 });/*
12301  * Based on:
12302  * Ext JS Library 1.1.1
12303  * Copyright(c) 2006-2007, Ext JS, LLC.
12304  *
12305  * Originally Released Under LGPL - original licence link has changed is not relivant.
12306  *
12307  * Fork - LGPL
12308  * <script type="text/javascript">
12309  */
12310 /**
12311  * @class Roo.data.HttpProxy
12312  * @extends Roo.data.DataProxy
12313  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12314  * configured to reference a certain URL.<br><br>
12315  * <p>
12316  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12317  * from which the running page was served.<br><br>
12318  * <p>
12319  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12320  * <p>
12321  * Be aware that to enable the browser to parse an XML document, the server must set
12322  * the Content-Type header in the HTTP response to "text/xml".
12323  * @constructor
12324  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12325  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12326  * will be used to make the request.
12327  */
12328 Roo.data.HttpProxy = function(conn){
12329     Roo.data.HttpProxy.superclass.constructor.call(this);
12330     // is conn a conn config or a real conn?
12331     this.conn = conn;
12332     this.useAjax = !conn || !conn.events;
12333   
12334 };
12335
12336 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12337     // thse are take from connection...
12338     
12339     /**
12340      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12341      */
12342     /**
12343      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12344      * extra parameters to each request made by this object. (defaults to undefined)
12345      */
12346     /**
12347      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12348      *  to each request made by this object. (defaults to undefined)
12349      */
12350     /**
12351      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12352      */
12353     /**
12354      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12355      */
12356      /**
12357      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12358      * @type Boolean
12359      */
12360   
12361
12362     /**
12363      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12364      * @type Boolean
12365      */
12366     /**
12367      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12368      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12369      * a finer-grained basis than the DataProxy events.
12370      */
12371     getConnection : function(){
12372         return this.useAjax ? Roo.Ajax : this.conn;
12373     },
12374
12375     /**
12376      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12377      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12378      * process that block using the passed callback.
12379      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12380      * for the request to the remote server.
12381      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12382      * object into a block of Roo.data.Records.
12383      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12384      * The function must be passed <ul>
12385      * <li>The Record block object</li>
12386      * <li>The "arg" argument from the load function</li>
12387      * <li>A boolean success indicator</li>
12388      * </ul>
12389      * @param {Object} scope The scope in which to call the callback
12390      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12391      */
12392     load : function(params, reader, callback, scope, arg){
12393         if(this.fireEvent("beforeload", this, params) !== false){
12394             var  o = {
12395                 params : params || {},
12396                 request: {
12397                     callback : callback,
12398                     scope : scope,
12399                     arg : arg
12400                 },
12401                 reader: reader,
12402                 callback : this.loadResponse,
12403                 scope: this
12404             };
12405             if(this.useAjax){
12406                 Roo.applyIf(o, this.conn);
12407                 if(this.activeRequest){
12408                     Roo.Ajax.abort(this.activeRequest);
12409                 }
12410                 this.activeRequest = Roo.Ajax.request(o);
12411             }else{
12412                 this.conn.request(o);
12413             }
12414         }else{
12415             callback.call(scope||this, null, arg, false);
12416         }
12417     },
12418
12419     // private
12420     loadResponse : function(o, success, response){
12421         delete this.activeRequest;
12422         if(!success){
12423             this.fireEvent("loadexception", this, o, response);
12424             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12425             return;
12426         }
12427         var result;
12428         try {
12429             result = o.reader.read(response);
12430         }catch(e){
12431             this.fireEvent("loadexception", this, o, response, e);
12432             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12433             return;
12434         }
12435         
12436         this.fireEvent("load", this, o, o.request.arg);
12437         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12438     },
12439
12440     // private
12441     update : function(dataSet){
12442
12443     },
12444
12445     // private
12446     updateResponse : function(dataSet){
12447
12448     }
12449 });/*
12450  * Based on:
12451  * Ext JS Library 1.1.1
12452  * Copyright(c) 2006-2007, Ext JS, LLC.
12453  *
12454  * Originally Released Under LGPL - original licence link has changed is not relivant.
12455  *
12456  * Fork - LGPL
12457  * <script type="text/javascript">
12458  */
12459
12460 /**
12461  * @class Roo.data.ScriptTagProxy
12462  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12463  * other than the originating domain of the running page.<br><br>
12464  * <p>
12465  * <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
12466  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12467  * <p>
12468  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12469  * source code that is used as the source inside a &lt;script> tag.<br><br>
12470  * <p>
12471  * In order for the browser to process the returned data, the server must wrap the data object
12472  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12473  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12474  * depending on whether the callback name was passed:
12475  * <p>
12476  * <pre><code>
12477 boolean scriptTag = false;
12478 String cb = request.getParameter("callback");
12479 if (cb != null) {
12480     scriptTag = true;
12481     response.setContentType("text/javascript");
12482 } else {
12483     response.setContentType("application/x-json");
12484 }
12485 Writer out = response.getWriter();
12486 if (scriptTag) {
12487     out.write(cb + "(");
12488 }
12489 out.print(dataBlock.toJsonString());
12490 if (scriptTag) {
12491     out.write(");");
12492 }
12493 </pre></code>
12494  *
12495  * @constructor
12496  * @param {Object} config A configuration object.
12497  */
12498 Roo.data.ScriptTagProxy = function(config){
12499     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12500     Roo.apply(this, config);
12501     this.head = document.getElementsByTagName("head")[0];
12502 };
12503
12504 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12505
12506 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12507     /**
12508      * @cfg {String} url The URL from which to request the data object.
12509      */
12510     /**
12511      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12512      */
12513     timeout : 30000,
12514     /**
12515      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12516      * the server the name of the callback function set up by the load call to process the returned data object.
12517      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12518      * javascript output which calls this named function passing the data object as its only parameter.
12519      */
12520     callbackParam : "callback",
12521     /**
12522      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12523      * name to the request.
12524      */
12525     nocache : true,
12526
12527     /**
12528      * Load data from the configured URL, read the data object into
12529      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12530      * process that block using the passed callback.
12531      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12532      * for the request to the remote server.
12533      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12534      * object into a block of Roo.data.Records.
12535      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12536      * The function must be passed <ul>
12537      * <li>The Record block object</li>
12538      * <li>The "arg" argument from the load function</li>
12539      * <li>A boolean success indicator</li>
12540      * </ul>
12541      * @param {Object} scope The scope in which to call the callback
12542      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12543      */
12544     load : function(params, reader, callback, scope, arg){
12545         if(this.fireEvent("beforeload", this, params) !== false){
12546
12547             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12548
12549             var url = this.url;
12550             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12551             if(this.nocache){
12552                 url += "&_dc=" + (new Date().getTime());
12553             }
12554             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12555             var trans = {
12556                 id : transId,
12557                 cb : "stcCallback"+transId,
12558                 scriptId : "stcScript"+transId,
12559                 params : params,
12560                 arg : arg,
12561                 url : url,
12562                 callback : callback,
12563                 scope : scope,
12564                 reader : reader
12565             };
12566             var conn = this;
12567
12568             window[trans.cb] = function(o){
12569                 conn.handleResponse(o, trans);
12570             };
12571
12572             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12573
12574             if(this.autoAbort !== false){
12575                 this.abort();
12576             }
12577
12578             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12579
12580             var script = document.createElement("script");
12581             script.setAttribute("src", url);
12582             script.setAttribute("type", "text/javascript");
12583             script.setAttribute("id", trans.scriptId);
12584             this.head.appendChild(script);
12585
12586             this.trans = trans;
12587         }else{
12588             callback.call(scope||this, null, arg, false);
12589         }
12590     },
12591
12592     // private
12593     isLoading : function(){
12594         return this.trans ? true : false;
12595     },
12596
12597     /**
12598      * Abort the current server request.
12599      */
12600     abort : function(){
12601         if(this.isLoading()){
12602             this.destroyTrans(this.trans);
12603         }
12604     },
12605
12606     // private
12607     destroyTrans : function(trans, isLoaded){
12608         this.head.removeChild(document.getElementById(trans.scriptId));
12609         clearTimeout(trans.timeoutId);
12610         if(isLoaded){
12611             window[trans.cb] = undefined;
12612             try{
12613                 delete window[trans.cb];
12614             }catch(e){}
12615         }else{
12616             // if hasn't been loaded, wait for load to remove it to prevent script error
12617             window[trans.cb] = function(){
12618                 window[trans.cb] = undefined;
12619                 try{
12620                     delete window[trans.cb];
12621                 }catch(e){}
12622             };
12623         }
12624     },
12625
12626     // private
12627     handleResponse : function(o, trans){
12628         this.trans = false;
12629         this.destroyTrans(trans, true);
12630         var result;
12631         try {
12632             result = trans.reader.readRecords(o);
12633         }catch(e){
12634             this.fireEvent("loadexception", this, o, trans.arg, e);
12635             trans.callback.call(trans.scope||window, null, trans.arg, false);
12636             return;
12637         }
12638         this.fireEvent("load", this, o, trans.arg);
12639         trans.callback.call(trans.scope||window, result, trans.arg, true);
12640     },
12641
12642     // private
12643     handleFailure : function(trans){
12644         this.trans = false;
12645         this.destroyTrans(trans, false);
12646         this.fireEvent("loadexception", this, null, trans.arg);
12647         trans.callback.call(trans.scope||window, null, trans.arg, false);
12648     }
12649 });/*
12650  * Based on:
12651  * Ext JS Library 1.1.1
12652  * Copyright(c) 2006-2007, Ext JS, LLC.
12653  *
12654  * Originally Released Under LGPL - original licence link has changed is not relivant.
12655  *
12656  * Fork - LGPL
12657  * <script type="text/javascript">
12658  */
12659
12660 /**
12661  * @class Roo.data.JsonReader
12662  * @extends Roo.data.DataReader
12663  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12664  * based on mappings in a provided Roo.data.Record constructor.
12665  * 
12666  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12667  * in the reply previously. 
12668  * 
12669  * <p>
12670  * Example code:
12671  * <pre><code>
12672 var RecordDef = Roo.data.Record.create([
12673     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12674     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12675 ]);
12676 var myReader = new Roo.data.JsonReader({
12677     totalProperty: "results",    // The property which contains the total dataset size (optional)
12678     root: "rows",                // The property which contains an Array of row objects
12679     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12680 }, RecordDef);
12681 </code></pre>
12682  * <p>
12683  * This would consume a JSON file like this:
12684  * <pre><code>
12685 { 'results': 2, 'rows': [
12686     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12687     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12688 }
12689 </code></pre>
12690  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12691  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12692  * paged from the remote server.
12693  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12694  * @cfg {String} root name of the property which contains the Array of row objects.
12695  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12696  * @cfg {Array} fields Array of field definition objects
12697  * @constructor
12698  * Create a new JsonReader
12699  * @param {Object} meta Metadata configuration options
12700  * @param {Object} recordType Either an Array of field definition objects,
12701  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12702  */
12703 Roo.data.JsonReader = function(meta, recordType){
12704     
12705     meta = meta || {};
12706     // set some defaults:
12707     Roo.applyIf(meta, {
12708         totalProperty: 'total',
12709         successProperty : 'success',
12710         root : 'data',
12711         id : 'id'
12712     });
12713     
12714     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12715 };
12716 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12717     
12718     /**
12719      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12720      * Used by Store query builder to append _requestMeta to params.
12721      * 
12722      */
12723     metaFromRemote : false,
12724     /**
12725      * This method is only used by a DataProxy which has retrieved data from a remote server.
12726      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12727      * @return {Object} data A data block which is used by an Roo.data.Store object as
12728      * a cache of Roo.data.Records.
12729      */
12730     read : function(response){
12731         var json = response.responseText;
12732        
12733         var o = /* eval:var:o */ eval("("+json+")");
12734         if(!o) {
12735             throw {message: "JsonReader.read: Json object not found"};
12736         }
12737         
12738         if(o.metaData){
12739             
12740             delete this.ef;
12741             this.metaFromRemote = true;
12742             this.meta = o.metaData;
12743             this.recordType = Roo.data.Record.create(o.metaData.fields);
12744             this.onMetaChange(this.meta, this.recordType, o);
12745         }
12746         return this.readRecords(o);
12747     },
12748
12749     // private function a store will implement
12750     onMetaChange : function(meta, recordType, o){
12751
12752     },
12753
12754     /**
12755          * @ignore
12756          */
12757     simpleAccess: function(obj, subsc) {
12758         return obj[subsc];
12759     },
12760
12761         /**
12762          * @ignore
12763          */
12764     getJsonAccessor: function(){
12765         var re = /[\[\.]/;
12766         return function(expr) {
12767             try {
12768                 return(re.test(expr))
12769                     ? new Function("obj", "return obj." + expr)
12770                     : function(obj){
12771                         return obj[expr];
12772                     };
12773             } catch(e){}
12774             return Roo.emptyFn;
12775         };
12776     }(),
12777
12778     /**
12779      * Create a data block containing Roo.data.Records from an XML document.
12780      * @param {Object} o An object which contains an Array of row objects in the property specified
12781      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12782      * which contains the total size of the dataset.
12783      * @return {Object} data A data block which is used by an Roo.data.Store object as
12784      * a cache of Roo.data.Records.
12785      */
12786     readRecords : function(o){
12787         /**
12788          * After any data loads, the raw JSON data is available for further custom processing.
12789          * @type Object
12790          */
12791         this.o = o;
12792         var s = this.meta, Record = this.recordType,
12793             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12794
12795 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12796         if (!this.ef) {
12797             if(s.totalProperty) {
12798                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12799                 }
12800                 if(s.successProperty) {
12801                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12802                 }
12803                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12804                 if (s.id) {
12805                         var g = this.getJsonAccessor(s.id);
12806                         this.getId = function(rec) {
12807                                 var r = g(rec);  
12808                                 return (r === undefined || r === "") ? null : r;
12809                         };
12810                 } else {
12811                         this.getId = function(){return null;};
12812                 }
12813             this.ef = [];
12814             for(var jj = 0; jj < fl; jj++){
12815                 f = fi[jj];
12816                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12817                 this.ef[jj] = this.getJsonAccessor(map);
12818             }
12819         }
12820
12821         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12822         if(s.totalProperty){
12823             var vt = parseInt(this.getTotal(o), 10);
12824             if(!isNaN(vt)){
12825                 totalRecords = vt;
12826             }
12827         }
12828         if(s.successProperty){
12829             var vs = this.getSuccess(o);
12830             if(vs === false || vs === 'false'){
12831                 success = false;
12832             }
12833         }
12834         var records = [];
12835         for(var i = 0; i < c; i++){
12836                 var n = root[i];
12837             var values = {};
12838             var id = this.getId(n);
12839             for(var j = 0; j < fl; j++){
12840                 f = fi[j];
12841             var v = this.ef[j](n);
12842             if (!f.convert) {
12843                 Roo.log('missing convert for ' + f.name);
12844                 Roo.log(f);
12845                 continue;
12846             }
12847             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12848             }
12849             var record = new Record(values, id);
12850             record.json = n;
12851             records[i] = record;
12852         }
12853         return {
12854             raw : o,
12855             success : success,
12856             records : records,
12857             totalRecords : totalRecords
12858         };
12859     }
12860 });/*
12861  * Based on:
12862  * Ext JS Library 1.1.1
12863  * Copyright(c) 2006-2007, Ext JS, LLC.
12864  *
12865  * Originally Released Under LGPL - original licence link has changed is not relivant.
12866  *
12867  * Fork - LGPL
12868  * <script type="text/javascript">
12869  */
12870
12871 /**
12872  * @class Roo.data.ArrayReader
12873  * @extends Roo.data.DataReader
12874  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12875  * Each element of that Array represents a row of data fields. The
12876  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12877  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12878  * <p>
12879  * Example code:.
12880  * <pre><code>
12881 var RecordDef = Roo.data.Record.create([
12882     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12883     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12884 ]);
12885 var myReader = new Roo.data.ArrayReader({
12886     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12887 }, RecordDef);
12888 </code></pre>
12889  * <p>
12890  * This would consume an Array like this:
12891  * <pre><code>
12892 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12893   </code></pre>
12894  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12895  * @constructor
12896  * Create a new JsonReader
12897  * @param {Object} meta Metadata configuration options.
12898  * @param {Object} recordType Either an Array of field definition objects
12899  * as specified to {@link Roo.data.Record#create},
12900  * or an {@link Roo.data.Record} object
12901  * created using {@link Roo.data.Record#create}.
12902  */
12903 Roo.data.ArrayReader = function(meta, recordType){
12904     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12905 };
12906
12907 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12908     /**
12909      * Create a data block containing Roo.data.Records from an XML document.
12910      * @param {Object} o An Array of row objects which represents the dataset.
12911      * @return {Object} data A data block which is used by an Roo.data.Store object as
12912      * a cache of Roo.data.Records.
12913      */
12914     readRecords : function(o){
12915         var sid = this.meta ? this.meta.id : null;
12916         var recordType = this.recordType, fields = recordType.prototype.fields;
12917         var records = [];
12918         var root = o;
12919             for(var i = 0; i < root.length; i++){
12920                     var n = root[i];
12921                 var values = {};
12922                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12923                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12924                 var f = fields.items[j];
12925                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12926                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12927                 v = f.convert(v);
12928                 values[f.name] = v;
12929             }
12930                 var record = new recordType(values, id);
12931                 record.json = n;
12932                 records[records.length] = record;
12933             }
12934             return {
12935                 records : records,
12936                 totalRecords : records.length
12937             };
12938     }
12939 });/*
12940  * - LGPL
12941  * * 
12942  */
12943
12944 /**
12945  * @class Roo.bootstrap.ComboBox
12946  * @extends Roo.bootstrap.TriggerField
12947  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12948  * @cfg {Boolean} append (true|false) default false
12949  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12950  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12951  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12952  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12953  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12954  * @cfg {Boolean} animate default true
12955  * @cfg {Boolean} emptyResultText only for touch device
12956  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12957  * @cfg {String} emptyTitle default ''
12958  * @constructor
12959  * Create a new ComboBox.
12960  * @param {Object} config Configuration options
12961  */
12962 Roo.bootstrap.ComboBox = function(config){
12963     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12964     this.addEvents({
12965         /**
12966          * @event expand
12967          * Fires when the dropdown list is expanded
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         */
12970         'expand' : true,
12971         /**
12972          * @event collapse
12973          * Fires when the dropdown list is collapsed
12974         * @param {Roo.bootstrap.ComboBox} combo This combo box
12975         */
12976         'collapse' : true,
12977         /**
12978          * @event beforeselect
12979          * Fires before a list item is selected. Return false to cancel the selection.
12980         * @param {Roo.bootstrap.ComboBox} combo This combo box
12981         * @param {Roo.data.Record} record The data record returned from the underlying store
12982         * @param {Number} index The index of the selected item in the dropdown list
12983         */
12984         'beforeselect' : true,
12985         /**
12986          * @event select
12987          * Fires when a list item is selected
12988         * @param {Roo.bootstrap.ComboBox} combo This combo box
12989         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12990         * @param {Number} index The index of the selected item in the dropdown list
12991         */
12992         'select' : true,
12993         /**
12994          * @event beforequery
12995          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12996          * The event object passed has these properties:
12997         * @param {Roo.bootstrap.ComboBox} combo This combo box
12998         * @param {String} query The query
12999         * @param {Boolean} forceAll true to force "all" query
13000         * @param {Boolean} cancel true to cancel the query
13001         * @param {Object} e The query event object
13002         */
13003         'beforequery': true,
13004          /**
13005          * @event add
13006          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13007         * @param {Roo.bootstrap.ComboBox} combo This combo box
13008         */
13009         'add' : true,
13010         /**
13011          * @event edit
13012          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13013         * @param {Roo.bootstrap.ComboBox} combo This combo box
13014         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13015         */
13016         'edit' : true,
13017         /**
13018          * @event remove
13019          * Fires when the remove value from the combobox array
13020         * @param {Roo.bootstrap.ComboBox} combo This combo box
13021         */
13022         'remove' : true,
13023         /**
13024          * @event afterremove
13025          * Fires when the remove value from the combobox array
13026         * @param {Roo.bootstrap.ComboBox} combo This combo box
13027         */
13028         'afterremove' : true,
13029         /**
13030          * @event specialfilter
13031          * Fires when specialfilter
13032             * @param {Roo.bootstrap.ComboBox} combo This combo box
13033             */
13034         'specialfilter' : true,
13035         /**
13036          * @event tick
13037          * Fires when tick the element
13038             * @param {Roo.bootstrap.ComboBox} combo This combo box
13039             */
13040         'tick' : true,
13041         /**
13042          * @event touchviewdisplay
13043          * Fires when touch view require special display (default is using displayField)
13044             * @param {Roo.bootstrap.ComboBox} combo This combo box
13045             * @param {Object} cfg set html .
13046             */
13047         'touchviewdisplay' : true
13048         
13049     });
13050     
13051     this.item = [];
13052     this.tickItems = [];
13053     
13054     this.selectedIndex = -1;
13055     if(this.mode == 'local'){
13056         if(config.queryDelay === undefined){
13057             this.queryDelay = 10;
13058         }
13059         if(config.minChars === undefined){
13060             this.minChars = 0;
13061         }
13062     }
13063 };
13064
13065 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13066      
13067     /**
13068      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13069      * rendering into an Roo.Editor, defaults to false)
13070      */
13071     /**
13072      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13073      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13074      */
13075     /**
13076      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13077      */
13078     /**
13079      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13080      * the dropdown list (defaults to undefined, with no header element)
13081      */
13082
13083      /**
13084      * @cfg {String/Roo.Template} tpl The template to use to render the output
13085      */
13086      
13087      /**
13088      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13089      */
13090     listWidth: undefined,
13091     /**
13092      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13093      * mode = 'remote' or 'text' if mode = 'local')
13094      */
13095     displayField: undefined,
13096     
13097     /**
13098      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13099      * mode = 'remote' or 'value' if mode = 'local'). 
13100      * Note: use of a valueField requires the user make a selection
13101      * in order for a value to be mapped.
13102      */
13103     valueField: undefined,
13104     /**
13105      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13106      */
13107     modalTitle : '',
13108     
13109     /**
13110      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13111      * field's data value (defaults to the underlying DOM element's name)
13112      */
13113     hiddenName: undefined,
13114     /**
13115      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13116      */
13117     listClass: '',
13118     /**
13119      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13120      */
13121     selectedClass: 'active',
13122     
13123     /**
13124      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13125      */
13126     shadow:'sides',
13127     /**
13128      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13129      * anchor positions (defaults to 'tl-bl')
13130      */
13131     listAlign: 'tl-bl?',
13132     /**
13133      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13134      */
13135     maxHeight: 300,
13136     /**
13137      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13138      * query specified by the allQuery config option (defaults to 'query')
13139      */
13140     triggerAction: 'query',
13141     /**
13142      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13143      * (defaults to 4, does not apply if editable = false)
13144      */
13145     minChars : 4,
13146     /**
13147      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13148      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13149      */
13150     typeAhead: false,
13151     /**
13152      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13153      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13154      */
13155     queryDelay: 500,
13156     /**
13157      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13158      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13159      */
13160     pageSize: 0,
13161     /**
13162      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13163      * when editable = true (defaults to false)
13164      */
13165     selectOnFocus:false,
13166     /**
13167      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13168      */
13169     queryParam: 'query',
13170     /**
13171      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13172      * when mode = 'remote' (defaults to 'Loading...')
13173      */
13174     loadingText: 'Loading...',
13175     /**
13176      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13177      */
13178     resizable: false,
13179     /**
13180      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13181      */
13182     handleHeight : 8,
13183     /**
13184      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13185      * traditional select (defaults to true)
13186      */
13187     editable: true,
13188     /**
13189      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13190      */
13191     allQuery: '',
13192     /**
13193      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13194      */
13195     mode: 'remote',
13196     /**
13197      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13198      * listWidth has a higher value)
13199      */
13200     minListWidth : 70,
13201     /**
13202      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13203      * allow the user to set arbitrary text into the field (defaults to false)
13204      */
13205     forceSelection:false,
13206     /**
13207      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13208      * if typeAhead = true (defaults to 250)
13209      */
13210     typeAheadDelay : 250,
13211     /**
13212      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13213      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13214      */
13215     valueNotFoundText : undefined,
13216     /**
13217      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13218      */
13219     blockFocus : false,
13220     
13221     /**
13222      * @cfg {Boolean} disableClear Disable showing of clear button.
13223      */
13224     disableClear : false,
13225     /**
13226      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13227      */
13228     alwaysQuery : false,
13229     
13230     /**
13231      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13232      */
13233     multiple : false,
13234     
13235     /**
13236      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13237      */
13238     invalidClass : "has-warning",
13239     
13240     /**
13241      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13242      */
13243     validClass : "has-success",
13244     
13245     /**
13246      * @cfg {Boolean} specialFilter (true|false) special filter default false
13247      */
13248     specialFilter : false,
13249     
13250     /**
13251      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13252      */
13253     mobileTouchView : true,
13254     
13255     /**
13256      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13257      */
13258     useNativeIOS : false,
13259     
13260     /**
13261      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13262      */
13263     mobile_restrict_height : false,
13264     
13265     ios_options : false,
13266     
13267     //private
13268     addicon : false,
13269     editicon: false,
13270     
13271     page: 0,
13272     hasQuery: false,
13273     append: false,
13274     loadNext: false,
13275     autoFocus : true,
13276     tickable : false,
13277     btnPosition : 'right',
13278     triggerList : true,
13279     showToggleBtn : true,
13280     animate : true,
13281     emptyResultText: 'Empty',
13282     triggerText : 'Select',
13283     emptyTitle : '',
13284     
13285     // element that contains real text value.. (when hidden is used..)
13286     
13287     getAutoCreate : function()
13288     {   
13289         var cfg = false;
13290         //render
13291         /*
13292          * Render classic select for iso
13293          */
13294         
13295         if(Roo.isIOS && this.useNativeIOS){
13296             cfg = this.getAutoCreateNativeIOS();
13297             return cfg;
13298         }
13299         
13300         /*
13301          * Touch Devices
13302          */
13303         
13304         if(Roo.isTouch && this.mobileTouchView){
13305             cfg = this.getAutoCreateTouchView();
13306             return cfg;;
13307         }
13308         
13309         /*
13310          *  Normal ComboBox
13311          */
13312         if(!this.tickable){
13313             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13314             return cfg;
13315         }
13316         
13317         /*
13318          *  ComboBox with tickable selections
13319          */
13320              
13321         var align = this.labelAlign || this.parentLabelAlign();
13322         
13323         cfg = {
13324             cls : 'form-group roo-combobox-tickable' //input-group
13325         };
13326         
13327         var btn_text_select = '';
13328         var btn_text_done = '';
13329         var btn_text_cancel = '';
13330         
13331         if (this.btn_text_show) {
13332             btn_text_select = 'Select';
13333             btn_text_done = 'Done';
13334             btn_text_cancel = 'Cancel'; 
13335         }
13336         
13337         var buttons = {
13338             tag : 'div',
13339             cls : 'tickable-buttons',
13340             cn : [
13341                 {
13342                     tag : 'button',
13343                     type : 'button',
13344                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13345                     //html : this.triggerText
13346                     html: btn_text_select
13347                 },
13348                 {
13349                     tag : 'button',
13350                     type : 'button',
13351                     name : 'ok',
13352                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13353                     //html : 'Done'
13354                     html: btn_text_done
13355                 },
13356                 {
13357                     tag : 'button',
13358                     type : 'button',
13359                     name : 'cancel',
13360                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13361                     //html : 'Cancel'
13362                     html: btn_text_cancel
13363                 }
13364             ]
13365         };
13366         
13367         if(this.editable){
13368             buttons.cn.unshift({
13369                 tag: 'input',
13370                 cls: 'roo-select2-search-field-input'
13371             });
13372         }
13373         
13374         var _this = this;
13375         
13376         Roo.each(buttons.cn, function(c){
13377             if (_this.size) {
13378                 c.cls += ' btn-' + _this.size;
13379             }
13380
13381             if (_this.disabled) {
13382                 c.disabled = true;
13383             }
13384         });
13385         
13386         var box = {
13387             tag: 'div',
13388             style : 'display: contents',
13389             cn: [
13390                 {
13391                     tag: 'input',
13392                     type : 'hidden',
13393                     cls: 'form-hidden-field'
13394                 },
13395                 {
13396                     tag: 'ul',
13397                     cls: 'roo-select2-choices',
13398                     cn:[
13399                         {
13400                             tag: 'li',
13401                             cls: 'roo-select2-search-field',
13402                             cn: [
13403                                 buttons
13404                             ]
13405                         }
13406                     ]
13407                 }
13408             ]
13409         };
13410         
13411         var combobox = {
13412             cls: 'roo-select2-container input-group roo-select2-container-multi',
13413             cn: [
13414                 
13415                 box
13416 //                {
13417 //                    tag: 'ul',
13418 //                    cls: 'typeahead typeahead-long dropdown-menu',
13419 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13420 //                }
13421             ]
13422         };
13423         
13424         if(this.hasFeedback && !this.allowBlank){
13425             
13426             var feedback = {
13427                 tag: 'span',
13428                 cls: 'glyphicon form-control-feedback'
13429             };
13430
13431             combobox.cn.push(feedback);
13432         }
13433         
13434         var indicator = {
13435             tag : 'i',
13436             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13437             tooltip : 'This field is required'
13438         };
13439         if (Roo.bootstrap.version == 4) {
13440             indicator = {
13441                 tag : 'i',
13442                 style : 'display:none'
13443             };
13444         }
13445         if (align ==='left' && this.fieldLabel.length) {
13446             
13447             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13448             
13449             cfg.cn = [
13450                 indicator,
13451                 {
13452                     tag: 'label',
13453                     'for' :  id,
13454                     cls : 'control-label col-form-label',
13455                     html : this.fieldLabel
13456
13457                 },
13458                 {
13459                     cls : "", 
13460                     cn: [
13461                         combobox
13462                     ]
13463                 }
13464
13465             ];
13466             
13467             var labelCfg = cfg.cn[1];
13468             var contentCfg = cfg.cn[2];
13469             
13470
13471             if(this.indicatorpos == 'right'){
13472                 
13473                 cfg.cn = [
13474                     {
13475                         tag: 'label',
13476                         'for' :  id,
13477                         cls : 'control-label col-form-label',
13478                         cn : [
13479                             {
13480                                 tag : 'span',
13481                                 html : this.fieldLabel
13482                             },
13483                             indicator
13484                         ]
13485                     },
13486                     {
13487                         cls : "",
13488                         cn: [
13489                             combobox
13490                         ]
13491                     }
13492
13493                 ];
13494                 
13495                 
13496                 
13497                 labelCfg = cfg.cn[0];
13498                 contentCfg = cfg.cn[1];
13499             
13500             }
13501             
13502             if(this.labelWidth > 12){
13503                 labelCfg.style = "width: " + this.labelWidth + 'px';
13504             }
13505             
13506             if(this.labelWidth < 13 && this.labelmd == 0){
13507                 this.labelmd = this.labelWidth;
13508             }
13509             
13510             if(this.labellg > 0){
13511                 labelCfg.cls += ' col-lg-' + this.labellg;
13512                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13513             }
13514             
13515             if(this.labelmd > 0){
13516                 labelCfg.cls += ' col-md-' + this.labelmd;
13517                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13518             }
13519             
13520             if(this.labelsm > 0){
13521                 labelCfg.cls += ' col-sm-' + this.labelsm;
13522                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13523             }
13524             
13525             if(this.labelxs > 0){
13526                 labelCfg.cls += ' col-xs-' + this.labelxs;
13527                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13528             }
13529                 
13530                 
13531         } else if ( this.fieldLabel.length) {
13532 //                Roo.log(" label");
13533                  cfg.cn = [
13534                    indicator,
13535                     {
13536                         tag: 'label',
13537                         //cls : 'input-group-addon',
13538                         html : this.fieldLabel
13539                     },
13540                     combobox
13541                 ];
13542                 
13543                 if(this.indicatorpos == 'right'){
13544                     cfg.cn = [
13545                         {
13546                             tag: 'label',
13547                             //cls : 'input-group-addon',
13548                             html : this.fieldLabel
13549                         },
13550                         indicator,
13551                         combobox
13552                     ];
13553                     
13554                 }
13555
13556         } else {
13557             
13558 //                Roo.log(" no label && no align");
13559                 cfg = combobox
13560                      
13561                 
13562         }
13563          
13564         var settings=this;
13565         ['xs','sm','md','lg'].map(function(size){
13566             if (settings[size]) {
13567                 cfg.cls += ' col-' + size + '-' + settings[size];
13568             }
13569         });
13570         
13571         return cfg;
13572         
13573     },
13574     
13575     _initEventsCalled : false,
13576     
13577     // private
13578     initEvents: function()
13579     {   
13580         if (this._initEventsCalled) { // as we call render... prevent looping...
13581             return;
13582         }
13583         this._initEventsCalled = true;
13584         
13585         if (!this.store) {
13586             throw "can not find store for combo";
13587         }
13588         
13589         this.indicator = this.indicatorEl();
13590         
13591         this.store = Roo.factory(this.store, Roo.data);
13592         this.store.parent = this;
13593         
13594         // if we are building from html. then this element is so complex, that we can not really
13595         // use the rendered HTML.
13596         // so we have to trash and replace the previous code.
13597         if (Roo.XComponent.build_from_html) {
13598             // remove this element....
13599             var e = this.el.dom, k=0;
13600             while (e ) { e = e.previousSibling;  ++k;}
13601
13602             this.el.remove();
13603             
13604             this.el=false;
13605             this.rendered = false;
13606             
13607             this.render(this.parent().getChildContainer(true), k);
13608         }
13609         
13610         if(Roo.isIOS && this.useNativeIOS){
13611             this.initIOSView();
13612             return;
13613         }
13614         
13615         /*
13616          * Touch Devices
13617          */
13618         
13619         if(Roo.isTouch && this.mobileTouchView){
13620             this.initTouchView();
13621             return;
13622         }
13623         
13624         if(this.tickable){
13625             this.initTickableEvents();
13626             return;
13627         }
13628         
13629         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13630         
13631         if(this.hiddenName){
13632             
13633             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13634             
13635             this.hiddenField.dom.value =
13636                 this.hiddenValue !== undefined ? this.hiddenValue :
13637                 this.value !== undefined ? this.value : '';
13638
13639             // prevent input submission
13640             this.el.dom.removeAttribute('name');
13641             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13642              
13643              
13644         }
13645         //if(Roo.isGecko){
13646         //    this.el.dom.setAttribute('autocomplete', 'off');
13647         //}
13648         
13649         var cls = 'x-combo-list';
13650         
13651         //this.list = new Roo.Layer({
13652         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13653         //});
13654         
13655         var _this = this;
13656         
13657         (function(){
13658             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13659             _this.list.setWidth(lw);
13660         }).defer(100);
13661         
13662         this.list.on('mouseover', this.onViewOver, this);
13663         this.list.on('mousemove', this.onViewMove, this);
13664         this.list.on('scroll', this.onViewScroll, this);
13665         
13666         /*
13667         this.list.swallowEvent('mousewheel');
13668         this.assetHeight = 0;
13669
13670         if(this.title){
13671             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13672             this.assetHeight += this.header.getHeight();
13673         }
13674
13675         this.innerList = this.list.createChild({cls:cls+'-inner'});
13676         this.innerList.on('mouseover', this.onViewOver, this);
13677         this.innerList.on('mousemove', this.onViewMove, this);
13678         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13679         
13680         if(this.allowBlank && !this.pageSize && !this.disableClear){
13681             this.footer = this.list.createChild({cls:cls+'-ft'});
13682             this.pageTb = new Roo.Toolbar(this.footer);
13683            
13684         }
13685         if(this.pageSize){
13686             this.footer = this.list.createChild({cls:cls+'-ft'});
13687             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13688                     {pageSize: this.pageSize});
13689             
13690         }
13691         
13692         if (this.pageTb && this.allowBlank && !this.disableClear) {
13693             var _this = this;
13694             this.pageTb.add(new Roo.Toolbar.Fill(), {
13695                 cls: 'x-btn-icon x-btn-clear',
13696                 text: '&#160;',
13697                 handler: function()
13698                 {
13699                     _this.collapse();
13700                     _this.clearValue();
13701                     _this.onSelect(false, -1);
13702                 }
13703             });
13704         }
13705         if (this.footer) {
13706             this.assetHeight += this.footer.getHeight();
13707         }
13708         */
13709             
13710         if(!this.tpl){
13711             this.tpl = Roo.bootstrap.version == 4 ?
13712                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13713                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13714         }
13715
13716         this.view = new Roo.View(this.list, this.tpl, {
13717             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13718         });
13719         //this.view.wrapEl.setDisplayed(false);
13720         this.view.on('click', this.onViewClick, this);
13721         
13722         
13723         this.store.on('beforeload', this.onBeforeLoad, this);
13724         this.store.on('load', this.onLoad, this);
13725         this.store.on('loadexception', this.onLoadException, this);
13726         /*
13727         if(this.resizable){
13728             this.resizer = new Roo.Resizable(this.list,  {
13729                pinned:true, handles:'se'
13730             });
13731             this.resizer.on('resize', function(r, w, h){
13732                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13733                 this.listWidth = w;
13734                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13735                 this.restrictHeight();
13736             }, this);
13737             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13738         }
13739         */
13740         if(!this.editable){
13741             this.editable = true;
13742             this.setEditable(false);
13743         }
13744         
13745         /*
13746         
13747         if (typeof(this.events.add.listeners) != 'undefined') {
13748             
13749             this.addicon = this.wrap.createChild(
13750                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13751        
13752             this.addicon.on('click', function(e) {
13753                 this.fireEvent('add', this);
13754             }, this);
13755         }
13756         if (typeof(this.events.edit.listeners) != 'undefined') {
13757             
13758             this.editicon = this.wrap.createChild(
13759                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13760             if (this.addicon) {
13761                 this.editicon.setStyle('margin-left', '40px');
13762             }
13763             this.editicon.on('click', function(e) {
13764                 
13765                 // we fire even  if inothing is selected..
13766                 this.fireEvent('edit', this, this.lastData );
13767                 
13768             }, this);
13769         }
13770         */
13771         
13772         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13773             "up" : function(e){
13774                 this.inKeyMode = true;
13775                 this.selectPrev();
13776             },
13777
13778             "down" : function(e){
13779                 if(!this.isExpanded()){
13780                     this.onTriggerClick();
13781                 }else{
13782                     this.inKeyMode = true;
13783                     this.selectNext();
13784                 }
13785             },
13786
13787             "enter" : function(e){
13788 //                this.onViewClick();
13789                 //return true;
13790                 this.collapse();
13791                 
13792                 if(this.fireEvent("specialkey", this, e)){
13793                     this.onViewClick(false);
13794                 }
13795                 
13796                 return true;
13797             },
13798
13799             "esc" : function(e){
13800                 this.collapse();
13801             },
13802
13803             "tab" : function(e){
13804                 this.collapse();
13805                 
13806                 if(this.fireEvent("specialkey", this, e)){
13807                     this.onViewClick(false);
13808                 }
13809                 
13810                 return true;
13811             },
13812
13813             scope : this,
13814
13815             doRelay : function(foo, bar, hname){
13816                 if(hname == 'down' || this.scope.isExpanded()){
13817                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13818                 }
13819                 return true;
13820             },
13821
13822             forceKeyDown: true
13823         });
13824         
13825         
13826         this.queryDelay = Math.max(this.queryDelay || 10,
13827                 this.mode == 'local' ? 10 : 250);
13828         
13829         
13830         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13831         
13832         if(this.typeAhead){
13833             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13834         }
13835         if(this.editable !== false){
13836             this.inputEl().on("keyup", this.onKeyUp, this);
13837         }
13838         if(this.forceSelection){
13839             this.inputEl().on('blur', this.doForce, this);
13840         }
13841         
13842         if(this.multiple){
13843             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13844             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13845         }
13846     },
13847     
13848     initTickableEvents: function()
13849     {   
13850         this.createList();
13851         
13852         if(this.hiddenName){
13853             
13854             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13855             
13856             this.hiddenField.dom.value =
13857                 this.hiddenValue !== undefined ? this.hiddenValue :
13858                 this.value !== undefined ? this.value : '';
13859
13860             // prevent input submission
13861             this.el.dom.removeAttribute('name');
13862             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13863              
13864              
13865         }
13866         
13867 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13868         
13869         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13870         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13871         if(this.triggerList){
13872             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13873         }
13874          
13875         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13876         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13877         
13878         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13879         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13880         
13881         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13882         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13883         
13884         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13885         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13886         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13887         
13888         this.okBtn.hide();
13889         this.cancelBtn.hide();
13890         
13891         var _this = this;
13892         
13893         (function(){
13894             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13895             _this.list.setWidth(lw);
13896         }).defer(100);
13897         
13898         this.list.on('mouseover', this.onViewOver, this);
13899         this.list.on('mousemove', this.onViewMove, this);
13900         
13901         this.list.on('scroll', this.onViewScroll, this);
13902         
13903         if(!this.tpl){
13904             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13905                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13906         }
13907
13908         this.view = new Roo.View(this.list, this.tpl, {
13909             singleSelect:true,
13910             tickable:true,
13911             parent:this,
13912             store: this.store,
13913             selectedClass: this.selectedClass
13914         });
13915         
13916         //this.view.wrapEl.setDisplayed(false);
13917         this.view.on('click', this.onViewClick, this);
13918         
13919         
13920         
13921         this.store.on('beforeload', this.onBeforeLoad, this);
13922         this.store.on('load', this.onLoad, this);
13923         this.store.on('loadexception', this.onLoadException, this);
13924         
13925         if(this.editable){
13926             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13927                 "up" : function(e){
13928                     this.inKeyMode = true;
13929                     this.selectPrev();
13930                 },
13931
13932                 "down" : function(e){
13933                     this.inKeyMode = true;
13934                     this.selectNext();
13935                 },
13936
13937                 "enter" : function(e){
13938                     if(this.fireEvent("specialkey", this, e)){
13939                         this.onViewClick(false);
13940                     }
13941                     
13942                     return true;
13943                 },
13944
13945                 "esc" : function(e){
13946                     this.onTickableFooterButtonClick(e, false, false);
13947                 },
13948
13949                 "tab" : function(e){
13950                     this.fireEvent("specialkey", this, e);
13951                     
13952                     this.onTickableFooterButtonClick(e, false, false);
13953                     
13954                     return true;
13955                 },
13956
13957                 scope : this,
13958
13959                 doRelay : function(e, fn, key){
13960                     if(this.scope.isExpanded()){
13961                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13962                     }
13963                     return true;
13964                 },
13965
13966                 forceKeyDown: true
13967             });
13968         }
13969         
13970         this.queryDelay = Math.max(this.queryDelay || 10,
13971                 this.mode == 'local' ? 10 : 250);
13972         
13973         
13974         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13975         
13976         if(this.typeAhead){
13977             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13978         }
13979         
13980         if(this.editable !== false){
13981             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13982         }
13983         
13984         this.indicator = this.indicatorEl();
13985         
13986         if(this.indicator){
13987             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13988             this.indicator.hide();
13989         }
13990         
13991     },
13992
13993     onDestroy : function(){
13994         if(this.view){
13995             this.view.setStore(null);
13996             this.view.el.removeAllListeners();
13997             this.view.el.remove();
13998             this.view.purgeListeners();
13999         }
14000         if(this.list){
14001             this.list.dom.innerHTML  = '';
14002         }
14003         
14004         if(this.store){
14005             this.store.un('beforeload', this.onBeforeLoad, this);
14006             this.store.un('load', this.onLoad, this);
14007             this.store.un('loadexception', this.onLoadException, this);
14008         }
14009         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14010     },
14011
14012     // private
14013     fireKey : function(e){
14014         if(e.isNavKeyPress() && !this.list.isVisible()){
14015             this.fireEvent("specialkey", this, e);
14016         }
14017     },
14018
14019     // private
14020     onResize: function(w, h){
14021 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14022 //        
14023 //        if(typeof w != 'number'){
14024 //            // we do not handle it!?!?
14025 //            return;
14026 //        }
14027 //        var tw = this.trigger.getWidth();
14028 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14029 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14030 //        var x = w - tw;
14031 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14032 //            
14033 //        //this.trigger.setStyle('left', x+'px');
14034 //        
14035 //        if(this.list && this.listWidth === undefined){
14036 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14037 //            this.list.setWidth(lw);
14038 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14039 //        }
14040         
14041     
14042         
14043     },
14044
14045     /**
14046      * Allow or prevent the user from directly editing the field text.  If false is passed,
14047      * the user will only be able to select from the items defined in the dropdown list.  This method
14048      * is the runtime equivalent of setting the 'editable' config option at config time.
14049      * @param {Boolean} value True to allow the user to directly edit the field text
14050      */
14051     setEditable : function(value){
14052         if(value == this.editable){
14053             return;
14054         }
14055         this.editable = value;
14056         if(!value){
14057             this.inputEl().dom.setAttribute('readOnly', true);
14058             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14059             this.inputEl().addClass('x-combo-noedit');
14060         }else{
14061             this.inputEl().dom.setAttribute('readOnly', false);
14062             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14063             this.inputEl().removeClass('x-combo-noedit');
14064         }
14065     },
14066
14067     // private
14068     
14069     onBeforeLoad : function(combo,opts){
14070         if(!this.hasFocus){
14071             return;
14072         }
14073          if (!opts.add) {
14074             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14075          }
14076         this.restrictHeight();
14077         this.selectedIndex = -1;
14078     },
14079
14080     // private
14081     onLoad : function(){
14082         
14083         this.hasQuery = false;
14084         
14085         if(!this.hasFocus){
14086             return;
14087         }
14088         
14089         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14090             this.loading.hide();
14091         }
14092         
14093         if(this.store.getCount() > 0){
14094             
14095             this.expand();
14096             this.restrictHeight();
14097             if(this.lastQuery == this.allQuery){
14098                 if(this.editable && !this.tickable){
14099                     this.inputEl().dom.select();
14100                 }
14101                 
14102                 if(
14103                     !this.selectByValue(this.value, true) &&
14104                     this.autoFocus && 
14105                     (
14106                         !this.store.lastOptions ||
14107                         typeof(this.store.lastOptions.add) == 'undefined' || 
14108                         this.store.lastOptions.add != true
14109                     )
14110                 ){
14111                     this.select(0, true);
14112                 }
14113             }else{
14114                 if(this.autoFocus){
14115                     this.selectNext();
14116                 }
14117                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14118                     this.taTask.delay(this.typeAheadDelay);
14119                 }
14120             }
14121         }else{
14122             this.onEmptyResults();
14123         }
14124         
14125         //this.el.focus();
14126     },
14127     // private
14128     onLoadException : function()
14129     {
14130         this.hasQuery = false;
14131         
14132         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14133             this.loading.hide();
14134         }
14135         
14136         if(this.tickable && this.editable){
14137             return;
14138         }
14139         
14140         this.collapse();
14141         // only causes errors at present
14142         //Roo.log(this.store.reader.jsonData);
14143         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14144             // fixme
14145             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14146         //}
14147         
14148         
14149     },
14150     // private
14151     onTypeAhead : function(){
14152         if(this.store.getCount() > 0){
14153             var r = this.store.getAt(0);
14154             var newValue = r.data[this.displayField];
14155             var len = newValue.length;
14156             var selStart = this.getRawValue().length;
14157             
14158             if(selStart != len){
14159                 this.setRawValue(newValue);
14160                 this.selectText(selStart, newValue.length);
14161             }
14162         }
14163     },
14164
14165     // private
14166     onSelect : function(record, index){
14167         
14168         if(this.fireEvent('beforeselect', this, record, index) !== false){
14169         
14170             this.setFromData(index > -1 ? record.data : false);
14171             
14172             this.collapse();
14173             this.fireEvent('select', this, record, index);
14174         }
14175     },
14176
14177     /**
14178      * Returns the currently selected field value or empty string if no value is set.
14179      * @return {String} value The selected value
14180      */
14181     getValue : function()
14182     {
14183         if(Roo.isIOS && this.useNativeIOS){
14184             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14185         }
14186         
14187         if(this.multiple){
14188             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14189         }
14190         
14191         if(this.valueField){
14192             return typeof this.value != 'undefined' ? this.value : '';
14193         }else{
14194             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14195         }
14196     },
14197     
14198     getRawValue : function()
14199     {
14200         if(Roo.isIOS && this.useNativeIOS){
14201             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14202         }
14203         
14204         var v = this.inputEl().getValue();
14205         
14206         return v;
14207     },
14208
14209     /**
14210      * Clears any text/value currently set in the field
14211      */
14212     clearValue : function(){
14213         
14214         if(this.hiddenField){
14215             this.hiddenField.dom.value = '';
14216         }
14217         this.value = '';
14218         this.setRawValue('');
14219         this.lastSelectionText = '';
14220         this.lastData = false;
14221         
14222         var close = this.closeTriggerEl();
14223         
14224         if(close){
14225             close.hide();
14226         }
14227         
14228         this.validate();
14229         
14230     },
14231
14232     /**
14233      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14234      * will be displayed in the field.  If the value does not match the data value of an existing item,
14235      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14236      * Otherwise the field will be blank (although the value will still be set).
14237      * @param {String} value The value to match
14238      */
14239     setValue : function(v)
14240     {
14241         if(Roo.isIOS && this.useNativeIOS){
14242             this.setIOSValue(v);
14243             return;
14244         }
14245         
14246         if(this.multiple){
14247             this.syncValue();
14248             return;
14249         }
14250         
14251         var text = v;
14252         if(this.valueField){
14253             var r = this.findRecord(this.valueField, v);
14254             if(r){
14255                 text = r.data[this.displayField];
14256             }else if(this.valueNotFoundText !== undefined){
14257                 text = this.valueNotFoundText;
14258             }
14259         }
14260         this.lastSelectionText = text;
14261         if(this.hiddenField){
14262             this.hiddenField.dom.value = v;
14263         }
14264         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14265         this.value = v;
14266         
14267         var close = this.closeTriggerEl();
14268         
14269         if(close){
14270             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14271         }
14272         
14273         this.validate();
14274     },
14275     /**
14276      * @property {Object} the last set data for the element
14277      */
14278     
14279     lastData : false,
14280     /**
14281      * Sets the value of the field based on a object which is related to the record format for the store.
14282      * @param {Object} value the value to set as. or false on reset?
14283      */
14284     setFromData : function(o){
14285         
14286         if(this.multiple){
14287             this.addItem(o);
14288             return;
14289         }
14290             
14291         var dv = ''; // display value
14292         var vv = ''; // value value..
14293         this.lastData = o;
14294         if (this.displayField) {
14295             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14296         } else {
14297             // this is an error condition!!!
14298             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14299         }
14300         
14301         if(this.valueField){
14302             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14303         }
14304         
14305         var close = this.closeTriggerEl();
14306         
14307         if(close){
14308             if(dv.length || vv * 1 > 0){
14309                 close.show() ;
14310                 this.blockFocus=true;
14311             } else {
14312                 close.hide();
14313             }             
14314         }
14315         
14316         if(this.hiddenField){
14317             this.hiddenField.dom.value = vv;
14318             
14319             this.lastSelectionText = dv;
14320             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14321             this.value = vv;
14322             return;
14323         }
14324         // no hidden field.. - we store the value in 'value', but still display
14325         // display field!!!!
14326         this.lastSelectionText = dv;
14327         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14328         this.value = vv;
14329         
14330         
14331         
14332     },
14333     // private
14334     reset : function(){
14335         // overridden so that last data is reset..
14336         
14337         if(this.multiple){
14338             this.clearItem();
14339             return;
14340         }
14341         
14342         this.setValue(this.originalValue);
14343         //this.clearInvalid();
14344         this.lastData = false;
14345         if (this.view) {
14346             this.view.clearSelections();
14347         }
14348         
14349         this.validate();
14350     },
14351     // private
14352     findRecord : function(prop, value){
14353         var record;
14354         if(this.store.getCount() > 0){
14355             this.store.each(function(r){
14356                 if(r.data[prop] == value){
14357                     record = r;
14358                     return false;
14359                 }
14360                 return true;
14361             });
14362         }
14363         return record;
14364     },
14365     
14366     getName: function()
14367     {
14368         // returns hidden if it's set..
14369         if (!this.rendered) {return ''};
14370         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14371         
14372     },
14373     // private
14374     onViewMove : function(e, t){
14375         this.inKeyMode = false;
14376     },
14377
14378     // private
14379     onViewOver : function(e, t){
14380         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14381             return;
14382         }
14383         var item = this.view.findItemFromChild(t);
14384         
14385         if(item){
14386             var index = this.view.indexOf(item);
14387             this.select(index, false);
14388         }
14389     },
14390
14391     // private
14392     onViewClick : function(view, doFocus, el, e)
14393     {
14394         var index = this.view.getSelectedIndexes()[0];
14395         
14396         var r = this.store.getAt(index);
14397         
14398         if(this.tickable){
14399             
14400             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14401                 return;
14402             }
14403             
14404             var rm = false;
14405             var _this = this;
14406             
14407             Roo.each(this.tickItems, function(v,k){
14408                 
14409                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14410                     Roo.log(v);
14411                     _this.tickItems.splice(k, 1);
14412                     
14413                     if(typeof(e) == 'undefined' && view == false){
14414                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14415                     }
14416                     
14417                     rm = true;
14418                     return;
14419                 }
14420             });
14421             
14422             if(rm){
14423                 return;
14424             }
14425             
14426             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14427                 this.tickItems.push(r.data);
14428             }
14429             
14430             if(typeof(e) == 'undefined' && view == false){
14431                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14432             }
14433                     
14434             return;
14435         }
14436         
14437         if(r){
14438             this.onSelect(r, index);
14439         }
14440         if(doFocus !== false && !this.blockFocus){
14441             this.inputEl().focus();
14442         }
14443     },
14444
14445     // private
14446     restrictHeight : function(){
14447         //this.innerList.dom.style.height = '';
14448         //var inner = this.innerList.dom;
14449         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14450         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14451         //this.list.beginUpdate();
14452         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14453         this.list.alignTo(this.inputEl(), this.listAlign);
14454         this.list.alignTo(this.inputEl(), this.listAlign);
14455         //this.list.endUpdate();
14456     },
14457
14458     // private
14459     onEmptyResults : function(){
14460         
14461         if(this.tickable && this.editable){
14462             this.hasFocus = false;
14463             this.restrictHeight();
14464             return;
14465         }
14466         
14467         this.collapse();
14468     },
14469
14470     /**
14471      * Returns true if the dropdown list is expanded, else false.
14472      */
14473     isExpanded : function(){
14474         return this.list.isVisible();
14475     },
14476
14477     /**
14478      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14479      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14480      * @param {String} value The data value of the item to select
14481      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14482      * selected item if it is not currently in view (defaults to true)
14483      * @return {Boolean} True if the value matched an item in the list, else false
14484      */
14485     selectByValue : function(v, scrollIntoView){
14486         if(v !== undefined && v !== null){
14487             var r = this.findRecord(this.valueField || this.displayField, v);
14488             if(r){
14489                 this.select(this.store.indexOf(r), scrollIntoView);
14490                 return true;
14491             }
14492         }
14493         return false;
14494     },
14495
14496     /**
14497      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14498      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14499      * @param {Number} index The zero-based index of the list item to select
14500      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14501      * selected item if it is not currently in view (defaults to true)
14502      */
14503     select : function(index, scrollIntoView){
14504         this.selectedIndex = index;
14505         this.view.select(index);
14506         if(scrollIntoView !== false){
14507             var el = this.view.getNode(index);
14508             /*
14509              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14510              */
14511             if(el){
14512                 this.list.scrollChildIntoView(el, false);
14513             }
14514         }
14515     },
14516
14517     // private
14518     selectNext : function(){
14519         var ct = this.store.getCount();
14520         if(ct > 0){
14521             if(this.selectedIndex == -1){
14522                 this.select(0);
14523             }else if(this.selectedIndex < ct-1){
14524                 this.select(this.selectedIndex+1);
14525             }
14526         }
14527     },
14528
14529     // private
14530     selectPrev : function(){
14531         var ct = this.store.getCount();
14532         if(ct > 0){
14533             if(this.selectedIndex == -1){
14534                 this.select(0);
14535             }else if(this.selectedIndex != 0){
14536                 this.select(this.selectedIndex-1);
14537             }
14538         }
14539     },
14540
14541     // private
14542     onKeyUp : function(e){
14543         if(this.editable !== false && !e.isSpecialKey()){
14544             this.lastKey = e.getKey();
14545             this.dqTask.delay(this.queryDelay);
14546         }
14547     },
14548
14549     // private
14550     validateBlur : function(){
14551         return !this.list || !this.list.isVisible();   
14552     },
14553
14554     // private
14555     initQuery : function(){
14556         
14557         var v = this.getRawValue();
14558         
14559         if(this.tickable && this.editable){
14560             v = this.tickableInputEl().getValue();
14561         }
14562         
14563         this.doQuery(v);
14564     },
14565
14566     // private
14567     doForce : function(){
14568         if(this.inputEl().dom.value.length > 0){
14569             this.inputEl().dom.value =
14570                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14571              
14572         }
14573     },
14574
14575     /**
14576      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14577      * query allowing the query action to be canceled if needed.
14578      * @param {String} query The SQL query to execute
14579      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14580      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14581      * saved in the current store (defaults to false)
14582      */
14583     doQuery : function(q, forceAll){
14584         
14585         if(q === undefined || q === null){
14586             q = '';
14587         }
14588         var qe = {
14589             query: q,
14590             forceAll: forceAll,
14591             combo: this,
14592             cancel:false
14593         };
14594         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14595             return false;
14596         }
14597         q = qe.query;
14598         
14599         forceAll = qe.forceAll;
14600         if(forceAll === true || (q.length >= this.minChars)){
14601             
14602             this.hasQuery = true;
14603             
14604             if(this.lastQuery != q || this.alwaysQuery){
14605                 this.lastQuery = q;
14606                 if(this.mode == 'local'){
14607                     this.selectedIndex = -1;
14608                     if(forceAll){
14609                         this.store.clearFilter();
14610                     }else{
14611                         
14612                         if(this.specialFilter){
14613                             this.fireEvent('specialfilter', this);
14614                             this.onLoad();
14615                             return;
14616                         }
14617                         
14618                         this.store.filter(this.displayField, q);
14619                     }
14620                     
14621                     this.store.fireEvent("datachanged", this.store);
14622                     
14623                     this.onLoad();
14624                     
14625                     
14626                 }else{
14627                     
14628                     this.store.baseParams[this.queryParam] = q;
14629                     
14630                     var options = {params : this.getParams(q)};
14631                     
14632                     if(this.loadNext){
14633                         options.add = true;
14634                         options.params.start = this.page * this.pageSize;
14635                     }
14636                     
14637                     this.store.load(options);
14638                     
14639                     /*
14640                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14641                      *  we should expand the list on onLoad
14642                      *  so command out it
14643                      */
14644 //                    this.expand();
14645                 }
14646             }else{
14647                 this.selectedIndex = -1;
14648                 this.onLoad();   
14649             }
14650         }
14651         
14652         this.loadNext = false;
14653     },
14654     
14655     // private
14656     getParams : function(q){
14657         var p = {};
14658         //p[this.queryParam] = q;
14659         
14660         if(this.pageSize){
14661             p.start = 0;
14662             p.limit = this.pageSize;
14663         }
14664         return p;
14665     },
14666
14667     /**
14668      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14669      */
14670     collapse : function(){
14671         if(!this.isExpanded()){
14672             return;
14673         }
14674         
14675         this.list.hide();
14676         
14677         this.hasFocus = false;
14678         
14679         if(this.tickable){
14680             this.okBtn.hide();
14681             this.cancelBtn.hide();
14682             this.trigger.show();
14683             
14684             if(this.editable){
14685                 this.tickableInputEl().dom.value = '';
14686                 this.tickableInputEl().blur();
14687             }
14688             
14689         }
14690         
14691         Roo.get(document).un('mousedown', this.collapseIf, this);
14692         Roo.get(document).un('mousewheel', this.collapseIf, this);
14693         if (!this.editable) {
14694             Roo.get(document).un('keydown', this.listKeyPress, this);
14695         }
14696         this.fireEvent('collapse', this);
14697         
14698         this.validate();
14699     },
14700
14701     // private
14702     collapseIf : function(e){
14703         var in_combo  = e.within(this.el);
14704         var in_list =  e.within(this.list);
14705         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14706         
14707         if (in_combo || in_list || is_list) {
14708             //e.stopPropagation();
14709             return;
14710         }
14711         
14712         if(this.tickable){
14713             this.onTickableFooterButtonClick(e, false, false);
14714         }
14715
14716         this.collapse();
14717         
14718     },
14719
14720     /**
14721      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14722      */
14723     expand : function(){
14724        
14725         if(this.isExpanded() || !this.hasFocus){
14726             return;
14727         }
14728         
14729         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14730         this.list.setWidth(lw);
14731         
14732         Roo.log('expand');
14733         
14734         this.list.show();
14735         
14736         this.restrictHeight();
14737         
14738         if(this.tickable){
14739             
14740             this.tickItems = Roo.apply([], this.item);
14741             
14742             this.okBtn.show();
14743             this.cancelBtn.show();
14744             this.trigger.hide();
14745             
14746             if(this.editable){
14747                 this.tickableInputEl().focus();
14748             }
14749             
14750         }
14751         
14752         Roo.get(document).on('mousedown', this.collapseIf, this);
14753         Roo.get(document).on('mousewheel', this.collapseIf, this);
14754         if (!this.editable) {
14755             Roo.get(document).on('keydown', this.listKeyPress, this);
14756         }
14757         
14758         this.fireEvent('expand', this);
14759     },
14760
14761     // private
14762     // Implements the default empty TriggerField.onTriggerClick function
14763     onTriggerClick : function(e)
14764     {
14765         Roo.log('trigger click');
14766         
14767         if(this.disabled || !this.triggerList){
14768             return;
14769         }
14770         
14771         this.page = 0;
14772         this.loadNext = false;
14773         
14774         if(this.isExpanded()){
14775             this.collapse();
14776             if (!this.blockFocus) {
14777                 this.inputEl().focus();
14778             }
14779             
14780         }else {
14781             this.hasFocus = true;
14782             if(this.triggerAction == 'all') {
14783                 this.doQuery(this.allQuery, true);
14784             } else {
14785                 this.doQuery(this.getRawValue());
14786             }
14787             if (!this.blockFocus) {
14788                 this.inputEl().focus();
14789             }
14790         }
14791     },
14792     
14793     onTickableTriggerClick : function(e)
14794     {
14795         if(this.disabled){
14796             return;
14797         }
14798         
14799         this.page = 0;
14800         this.loadNext = false;
14801         this.hasFocus = true;
14802         
14803         if(this.triggerAction == 'all') {
14804             this.doQuery(this.allQuery, true);
14805         } else {
14806             this.doQuery(this.getRawValue());
14807         }
14808     },
14809     
14810     onSearchFieldClick : function(e)
14811     {
14812         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14813             this.onTickableFooterButtonClick(e, false, false);
14814             return;
14815         }
14816         
14817         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14818             return;
14819         }
14820         
14821         this.page = 0;
14822         this.loadNext = false;
14823         this.hasFocus = true;
14824         
14825         if(this.triggerAction == 'all') {
14826             this.doQuery(this.allQuery, true);
14827         } else {
14828             this.doQuery(this.getRawValue());
14829         }
14830     },
14831     
14832     listKeyPress : function(e)
14833     {
14834         //Roo.log('listkeypress');
14835         // scroll to first matching element based on key pres..
14836         if (e.isSpecialKey()) {
14837             return false;
14838         }
14839         var k = String.fromCharCode(e.getKey()).toUpperCase();
14840         //Roo.log(k);
14841         var match  = false;
14842         var csel = this.view.getSelectedNodes();
14843         var cselitem = false;
14844         if (csel.length) {
14845             var ix = this.view.indexOf(csel[0]);
14846             cselitem  = this.store.getAt(ix);
14847             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14848                 cselitem = false;
14849             }
14850             
14851         }
14852         
14853         this.store.each(function(v) { 
14854             if (cselitem) {
14855                 // start at existing selection.
14856                 if (cselitem.id == v.id) {
14857                     cselitem = false;
14858                 }
14859                 return true;
14860             }
14861                 
14862             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14863                 match = this.store.indexOf(v);
14864                 return false;
14865             }
14866             return true;
14867         }, this);
14868         
14869         if (match === false) {
14870             return true; // no more action?
14871         }
14872         // scroll to?
14873         this.view.select(match);
14874         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14875         sn.scrollIntoView(sn.dom.parentNode, false);
14876     },
14877     
14878     onViewScroll : function(e, t){
14879         
14880         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){
14881             return;
14882         }
14883         
14884         this.hasQuery = true;
14885         
14886         this.loading = this.list.select('.loading', true).first();
14887         
14888         if(this.loading === null){
14889             this.list.createChild({
14890                 tag: 'div',
14891                 cls: 'loading roo-select2-more-results roo-select2-active',
14892                 html: 'Loading more results...'
14893             });
14894             
14895             this.loading = this.list.select('.loading', true).first();
14896             
14897             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14898             
14899             this.loading.hide();
14900         }
14901         
14902         this.loading.show();
14903         
14904         var _combo = this;
14905         
14906         this.page++;
14907         this.loadNext = true;
14908         
14909         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14910         
14911         return;
14912     },
14913     
14914     addItem : function(o)
14915     {   
14916         var dv = ''; // display value
14917         
14918         if (this.displayField) {
14919             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14920         } else {
14921             // this is an error condition!!!
14922             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14923         }
14924         
14925         if(!dv.length){
14926             return;
14927         }
14928         
14929         var choice = this.choices.createChild({
14930             tag: 'li',
14931             cls: 'roo-select2-search-choice',
14932             cn: [
14933                 {
14934                     tag: 'div',
14935                     html: dv
14936                 },
14937                 {
14938                     tag: 'a',
14939                     href: '#',
14940                     cls: 'roo-select2-search-choice-close fa fa-times',
14941                     tabindex: '-1'
14942                 }
14943             ]
14944             
14945         }, this.searchField);
14946         
14947         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14948         
14949         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14950         
14951         this.item.push(o);
14952         
14953         this.lastData = o;
14954         
14955         this.syncValue();
14956         
14957         this.inputEl().dom.value = '';
14958         
14959         this.validate();
14960     },
14961     
14962     onRemoveItem : function(e, _self, o)
14963     {
14964         e.preventDefault();
14965         
14966         this.lastItem = Roo.apply([], this.item);
14967         
14968         var index = this.item.indexOf(o.data) * 1;
14969         
14970         if( index < 0){
14971             Roo.log('not this item?!');
14972             return;
14973         }
14974         
14975         this.item.splice(index, 1);
14976         o.item.remove();
14977         
14978         this.syncValue();
14979         
14980         this.fireEvent('remove', this, e);
14981         
14982         this.validate();
14983         
14984     },
14985     
14986     syncValue : function()
14987     {
14988         if(!this.item.length){
14989             this.clearValue();
14990             return;
14991         }
14992             
14993         var value = [];
14994         var _this = this;
14995         Roo.each(this.item, function(i){
14996             if(_this.valueField){
14997                 value.push(i[_this.valueField]);
14998                 return;
14999             }
15000
15001             value.push(i);
15002         });
15003
15004         this.value = value.join(',');
15005
15006         if(this.hiddenField){
15007             this.hiddenField.dom.value = this.value;
15008         }
15009         
15010         this.store.fireEvent("datachanged", this.store);
15011         
15012         this.validate();
15013     },
15014     
15015     clearItem : function()
15016     {
15017         if(!this.multiple){
15018             return;
15019         }
15020         
15021         this.item = [];
15022         
15023         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15024            c.remove();
15025         });
15026         
15027         this.syncValue();
15028         
15029         this.validate();
15030         
15031         if(this.tickable && !Roo.isTouch){
15032             this.view.refresh();
15033         }
15034     },
15035     
15036     inputEl: function ()
15037     {
15038         if(Roo.isIOS && this.useNativeIOS){
15039             return this.el.select('select.roo-ios-select', true).first();
15040         }
15041         
15042         if(Roo.isTouch && this.mobileTouchView){
15043             return this.el.select('input.form-control',true).first();
15044         }
15045         
15046         if(this.tickable){
15047             return this.searchField;
15048         }
15049         
15050         return this.el.select('input.form-control',true).first();
15051     },
15052     
15053     onTickableFooterButtonClick : function(e, btn, el)
15054     {
15055         e.preventDefault();
15056         
15057         this.lastItem = Roo.apply([], this.item);
15058         
15059         if(btn && btn.name == 'cancel'){
15060             this.tickItems = Roo.apply([], this.item);
15061             this.collapse();
15062             return;
15063         }
15064         
15065         this.clearItem();
15066         
15067         var _this = this;
15068         
15069         Roo.each(this.tickItems, function(o){
15070             _this.addItem(o);
15071         });
15072         
15073         this.collapse();
15074         
15075     },
15076     
15077     validate : function()
15078     {
15079         if(this.getVisibilityEl().hasClass('hidden')){
15080             return true;
15081         }
15082         
15083         var v = this.getRawValue();
15084         
15085         if(this.multiple){
15086             v = this.getValue();
15087         }
15088         
15089         if(this.disabled || this.allowBlank || v.length){
15090             this.markValid();
15091             return true;
15092         }
15093         
15094         this.markInvalid();
15095         return false;
15096     },
15097     
15098     tickableInputEl : function()
15099     {
15100         if(!this.tickable || !this.editable){
15101             return this.inputEl();
15102         }
15103         
15104         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15105     },
15106     
15107     
15108     getAutoCreateTouchView : function()
15109     {
15110         var id = Roo.id();
15111         
15112         var cfg = {
15113             cls: 'form-group' //input-group
15114         };
15115         
15116         var input =  {
15117             tag: 'input',
15118             id : id,
15119             type : this.inputType,
15120             cls : 'form-control x-combo-noedit',
15121             autocomplete: 'new-password',
15122             placeholder : this.placeholder || '',
15123             readonly : true
15124         };
15125         
15126         if (this.name) {
15127             input.name = this.name;
15128         }
15129         
15130         if (this.size) {
15131             input.cls += ' input-' + this.size;
15132         }
15133         
15134         if (this.disabled) {
15135             input.disabled = true;
15136         }
15137         
15138         var inputblock = {
15139             cls : '',
15140             cn : [
15141                 input
15142             ]
15143         };
15144         
15145         if(this.before){
15146             inputblock.cls += ' input-group';
15147             
15148             inputblock.cn.unshift({
15149                 tag :'span',
15150                 cls : 'input-group-addon input-group-prepend input-group-text',
15151                 html : this.before
15152             });
15153         }
15154         
15155         if(this.removable && !this.multiple){
15156             inputblock.cls += ' roo-removable';
15157             
15158             inputblock.cn.push({
15159                 tag: 'button',
15160                 html : 'x',
15161                 cls : 'roo-combo-removable-btn close'
15162             });
15163         }
15164
15165         if(this.hasFeedback && !this.allowBlank){
15166             
15167             inputblock.cls += ' has-feedback';
15168             
15169             inputblock.cn.push({
15170                 tag: 'span',
15171                 cls: 'glyphicon form-control-feedback'
15172             });
15173             
15174         }
15175         
15176         if (this.after) {
15177             
15178             inputblock.cls += (this.before) ? '' : ' input-group';
15179             
15180             inputblock.cn.push({
15181                 tag :'span',
15182                 cls : 'input-group-addon input-group-append input-group-text',
15183                 html : this.after
15184             });
15185         }
15186
15187         
15188         var ibwrap = inputblock;
15189         
15190         if(this.multiple){
15191             ibwrap = {
15192                 tag: 'ul',
15193                 cls: 'roo-select2-choices',
15194                 cn:[
15195                     {
15196                         tag: 'li',
15197                         cls: 'roo-select2-search-field',
15198                         cn: [
15199
15200                             inputblock
15201                         ]
15202                     }
15203                 ]
15204             };
15205         
15206             
15207         }
15208         
15209         var combobox = {
15210             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15211             cn: [
15212                 {
15213                     tag: 'input',
15214                     type : 'hidden',
15215                     cls: 'form-hidden-field'
15216                 },
15217                 ibwrap
15218             ]
15219         };
15220         
15221         if(!this.multiple && this.showToggleBtn){
15222             
15223             var caret = {
15224                         tag: 'span',
15225                         cls: 'caret'
15226             };
15227             
15228             if (this.caret != false) {
15229                 caret = {
15230                      tag: 'i',
15231                      cls: 'fa fa-' + this.caret
15232                 };
15233                 
15234             }
15235             
15236             combobox.cn.push({
15237                 tag :'span',
15238                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15239                 cn : [
15240                     caret,
15241                     {
15242                         tag: 'span',
15243                         cls: 'combobox-clear',
15244                         cn  : [
15245                             {
15246                                 tag : 'i',
15247                                 cls: 'icon-remove'
15248                             }
15249                         ]
15250                     }
15251                 ]
15252
15253             })
15254         }
15255         
15256         if(this.multiple){
15257             combobox.cls += ' roo-select2-container-multi';
15258         }
15259         
15260         var align = this.labelAlign || this.parentLabelAlign();
15261         
15262         if (align ==='left' && this.fieldLabel.length) {
15263
15264             cfg.cn = [
15265                 {
15266                    tag : 'i',
15267                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15268                    tooltip : 'This field is required'
15269                 },
15270                 {
15271                     tag: 'label',
15272                     cls : 'control-label col-form-label',
15273                     html : this.fieldLabel
15274
15275                 },
15276                 {
15277                     cls : '', 
15278                     cn: [
15279                         combobox
15280                     ]
15281                 }
15282             ];
15283             
15284             var labelCfg = cfg.cn[1];
15285             var contentCfg = cfg.cn[2];
15286             
15287
15288             if(this.indicatorpos == 'right'){
15289                 cfg.cn = [
15290                     {
15291                         tag: 'label',
15292                         'for' :  id,
15293                         cls : 'control-label col-form-label',
15294                         cn : [
15295                             {
15296                                 tag : 'span',
15297                                 html : this.fieldLabel
15298                             },
15299                             {
15300                                 tag : 'i',
15301                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15302                                 tooltip : 'This field is required'
15303                             }
15304                         ]
15305                     },
15306                     {
15307                         cls : "",
15308                         cn: [
15309                             combobox
15310                         ]
15311                     }
15312
15313                 ];
15314                 
15315                 labelCfg = cfg.cn[0];
15316                 contentCfg = cfg.cn[1];
15317             }
15318             
15319            
15320             
15321             if(this.labelWidth > 12){
15322                 labelCfg.style = "width: " + this.labelWidth + 'px';
15323             }
15324             
15325             if(this.labelWidth < 13 && this.labelmd == 0){
15326                 this.labelmd = this.labelWidth;
15327             }
15328             
15329             if(this.labellg > 0){
15330                 labelCfg.cls += ' col-lg-' + this.labellg;
15331                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15332             }
15333             
15334             if(this.labelmd > 0){
15335                 labelCfg.cls += ' col-md-' + this.labelmd;
15336                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15337             }
15338             
15339             if(this.labelsm > 0){
15340                 labelCfg.cls += ' col-sm-' + this.labelsm;
15341                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15342             }
15343             
15344             if(this.labelxs > 0){
15345                 labelCfg.cls += ' col-xs-' + this.labelxs;
15346                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15347             }
15348                 
15349                 
15350         } else if ( this.fieldLabel.length) {
15351             cfg.cn = [
15352                 {
15353                    tag : 'i',
15354                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15355                    tooltip : 'This field is required'
15356                 },
15357                 {
15358                     tag: 'label',
15359                     cls : 'control-label',
15360                     html : this.fieldLabel
15361
15362                 },
15363                 {
15364                     cls : '', 
15365                     cn: [
15366                         combobox
15367                     ]
15368                 }
15369             ];
15370             
15371             if(this.indicatorpos == 'right'){
15372                 cfg.cn = [
15373                     {
15374                         tag: 'label',
15375                         cls : 'control-label',
15376                         html : this.fieldLabel,
15377                         cn : [
15378                             {
15379                                tag : 'i',
15380                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15381                                tooltip : 'This field is required'
15382                             }
15383                         ]
15384                     },
15385                     {
15386                         cls : '', 
15387                         cn: [
15388                             combobox
15389                         ]
15390                     }
15391                 ];
15392             }
15393         } else {
15394             cfg.cn = combobox;    
15395         }
15396         
15397         
15398         var settings = this;
15399         
15400         ['xs','sm','md','lg'].map(function(size){
15401             if (settings[size]) {
15402                 cfg.cls += ' col-' + size + '-' + settings[size];
15403             }
15404         });
15405         
15406         return cfg;
15407     },
15408     
15409     initTouchView : function()
15410     {
15411         this.renderTouchView();
15412         
15413         this.touchViewEl.on('scroll', function(){
15414             this.el.dom.scrollTop = 0;
15415         }, this);
15416         
15417         this.originalValue = this.getValue();
15418         
15419         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15420         
15421         this.inputEl().on("click", this.showTouchView, this);
15422         if (this.triggerEl) {
15423             this.triggerEl.on("click", this.showTouchView, this);
15424         }
15425         
15426         
15427         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15428         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15429         
15430         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15431         
15432         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15433         this.store.on('load', this.onTouchViewLoad, this);
15434         this.store.on('loadexception', this.onTouchViewLoadException, this);
15435         
15436         if(this.hiddenName){
15437             
15438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15439             
15440             this.hiddenField.dom.value =
15441                 this.hiddenValue !== undefined ? this.hiddenValue :
15442                 this.value !== undefined ? this.value : '';
15443         
15444             this.el.dom.removeAttribute('name');
15445             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15446         }
15447         
15448         if(this.multiple){
15449             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15450             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15451         }
15452         
15453         if(this.removable && !this.multiple){
15454             var close = this.closeTriggerEl();
15455             if(close){
15456                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15457                 close.on('click', this.removeBtnClick, this, close);
15458             }
15459         }
15460         /*
15461          * fix the bug in Safari iOS8
15462          */
15463         this.inputEl().on("focus", function(e){
15464             document.activeElement.blur();
15465         }, this);
15466         
15467         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15468         
15469         return;
15470         
15471         
15472     },
15473     
15474     renderTouchView : function()
15475     {
15476         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15477         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15478         
15479         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15480         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15481         
15482         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15483         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15484         this.touchViewBodyEl.setStyle('overflow', 'auto');
15485         
15486         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15487         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15488         
15489         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15490         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15491         
15492     },
15493     
15494     showTouchView : function()
15495     {
15496         if(this.disabled){
15497             return;
15498         }
15499         
15500         this.touchViewHeaderEl.hide();
15501
15502         if(this.modalTitle.length){
15503             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15504             this.touchViewHeaderEl.show();
15505         }
15506
15507         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15508         this.touchViewEl.show();
15509
15510         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15511         
15512         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15513         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15514
15515         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15516
15517         if(this.modalTitle.length){
15518             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15519         }
15520         
15521         this.touchViewBodyEl.setHeight(bodyHeight);
15522
15523         if(this.animate){
15524             var _this = this;
15525             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15526         }else{
15527             this.touchViewEl.addClass('in');
15528         }
15529         
15530         if(this._touchViewMask){
15531             Roo.get(document.body).addClass("x-body-masked");
15532             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15533             this._touchViewMask.setStyle('z-index', 10000);
15534             this._touchViewMask.addClass('show');
15535         }
15536         
15537         this.doTouchViewQuery();
15538         
15539     },
15540     
15541     hideTouchView : function()
15542     {
15543         this.touchViewEl.removeClass('in');
15544
15545         if(this.animate){
15546             var _this = this;
15547             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15548         }else{
15549             this.touchViewEl.setStyle('display', 'none');
15550         }
15551         
15552         if(this._touchViewMask){
15553             this._touchViewMask.removeClass('show');
15554             Roo.get(document.body).removeClass("x-body-masked");
15555         }
15556     },
15557     
15558     setTouchViewValue : function()
15559     {
15560         if(this.multiple){
15561             this.clearItem();
15562         
15563             var _this = this;
15564
15565             Roo.each(this.tickItems, function(o){
15566                 this.addItem(o);
15567             }, this);
15568         }
15569         
15570         this.hideTouchView();
15571     },
15572     
15573     doTouchViewQuery : function()
15574     {
15575         var qe = {
15576             query: '',
15577             forceAll: true,
15578             combo: this,
15579             cancel:false
15580         };
15581         
15582         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15583             return false;
15584         }
15585         
15586         if(!this.alwaysQuery || this.mode == 'local'){
15587             this.onTouchViewLoad();
15588             return;
15589         }
15590         
15591         this.store.load();
15592     },
15593     
15594     onTouchViewBeforeLoad : function(combo,opts)
15595     {
15596         return;
15597     },
15598
15599     // private
15600     onTouchViewLoad : function()
15601     {
15602         if(this.store.getCount() < 1){
15603             this.onTouchViewEmptyResults();
15604             return;
15605         }
15606         
15607         this.clearTouchView();
15608         
15609         var rawValue = this.getRawValue();
15610         
15611         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15612         
15613         this.tickItems = [];
15614         
15615         this.store.data.each(function(d, rowIndex){
15616             var row = this.touchViewListGroup.createChild(template);
15617             
15618             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15619                 row.addClass(d.data.cls);
15620             }
15621             
15622             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15623                 var cfg = {
15624                     data : d.data,
15625                     html : d.data[this.displayField]
15626                 };
15627                 
15628                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15629                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15630                 }
15631             }
15632             row.removeClass('selected');
15633             if(!this.multiple && this.valueField &&
15634                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15635             {
15636                 // radio buttons..
15637                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15638                 row.addClass('selected');
15639             }
15640             
15641             if(this.multiple && this.valueField &&
15642                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15643             {
15644                 
15645                 // checkboxes...
15646                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15647                 this.tickItems.push(d.data);
15648             }
15649             
15650             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15651             
15652         }, this);
15653         
15654         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15655         
15656         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15657
15658         if(this.modalTitle.length){
15659             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15660         }
15661
15662         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15663         
15664         if(this.mobile_restrict_height && listHeight < bodyHeight){
15665             this.touchViewBodyEl.setHeight(listHeight);
15666         }
15667         
15668         var _this = this;
15669         
15670         if(firstChecked && listHeight > bodyHeight){
15671             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15672         }
15673         
15674     },
15675     
15676     onTouchViewLoadException : function()
15677     {
15678         this.hideTouchView();
15679     },
15680     
15681     onTouchViewEmptyResults : function()
15682     {
15683         this.clearTouchView();
15684         
15685         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15686         
15687         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15688         
15689     },
15690     
15691     clearTouchView : function()
15692     {
15693         this.touchViewListGroup.dom.innerHTML = '';
15694     },
15695     
15696     onTouchViewClick : function(e, el, o)
15697     {
15698         e.preventDefault();
15699         
15700         var row = o.row;
15701         var rowIndex = o.rowIndex;
15702         
15703         var r = this.store.getAt(rowIndex);
15704         
15705         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15706             
15707             if(!this.multiple){
15708                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15709                     c.dom.removeAttribute('checked');
15710                 }, this);
15711
15712                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15713
15714                 this.setFromData(r.data);
15715
15716                 var close = this.closeTriggerEl();
15717
15718                 if(close){
15719                     close.show();
15720                 }
15721
15722                 this.hideTouchView();
15723
15724                 this.fireEvent('select', this, r, rowIndex);
15725
15726                 return;
15727             }
15728
15729             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15730                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15731                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15732                 return;
15733             }
15734
15735             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15736             this.addItem(r.data);
15737             this.tickItems.push(r.data);
15738         }
15739     },
15740     
15741     getAutoCreateNativeIOS : function()
15742     {
15743         var cfg = {
15744             cls: 'form-group' //input-group,
15745         };
15746         
15747         var combobox =  {
15748             tag: 'select',
15749             cls : 'roo-ios-select'
15750         };
15751         
15752         if (this.name) {
15753             combobox.name = this.name;
15754         }
15755         
15756         if (this.disabled) {
15757             combobox.disabled = true;
15758         }
15759         
15760         var settings = this;
15761         
15762         ['xs','sm','md','lg'].map(function(size){
15763             if (settings[size]) {
15764                 cfg.cls += ' col-' + size + '-' + settings[size];
15765             }
15766         });
15767         
15768         cfg.cn = combobox;
15769         
15770         return cfg;
15771         
15772     },
15773     
15774     initIOSView : function()
15775     {
15776         this.store.on('load', this.onIOSViewLoad, this);
15777         
15778         return;
15779     },
15780     
15781     onIOSViewLoad : function()
15782     {
15783         if(this.store.getCount() < 1){
15784             return;
15785         }
15786         
15787         this.clearIOSView();
15788         
15789         if(this.allowBlank) {
15790             
15791             var default_text = '-- SELECT --';
15792             
15793             if(this.placeholder.length){
15794                 default_text = this.placeholder;
15795             }
15796             
15797             if(this.emptyTitle.length){
15798                 default_text += ' - ' + this.emptyTitle + ' -';
15799             }
15800             
15801             var opt = this.inputEl().createChild({
15802                 tag: 'option',
15803                 value : 0,
15804                 html : default_text
15805             });
15806             
15807             var o = {};
15808             o[this.valueField] = 0;
15809             o[this.displayField] = default_text;
15810             
15811             this.ios_options.push({
15812                 data : o,
15813                 el : opt
15814             });
15815             
15816         }
15817         
15818         this.store.data.each(function(d, rowIndex){
15819             
15820             var html = '';
15821             
15822             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15823                 html = d.data[this.displayField];
15824             }
15825             
15826             var value = '';
15827             
15828             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15829                 value = d.data[this.valueField];
15830             }
15831             
15832             var option = {
15833                 tag: 'option',
15834                 value : value,
15835                 html : html
15836             };
15837             
15838             if(this.value == d.data[this.valueField]){
15839                 option['selected'] = true;
15840             }
15841             
15842             var opt = this.inputEl().createChild(option);
15843             
15844             this.ios_options.push({
15845                 data : d.data,
15846                 el : opt
15847             });
15848             
15849         }, this);
15850         
15851         this.inputEl().on('change', function(){
15852            this.fireEvent('select', this);
15853         }, this);
15854         
15855     },
15856     
15857     clearIOSView: function()
15858     {
15859         this.inputEl().dom.innerHTML = '';
15860         
15861         this.ios_options = [];
15862     },
15863     
15864     setIOSValue: function(v)
15865     {
15866         this.value = v;
15867         
15868         if(!this.ios_options){
15869             return;
15870         }
15871         
15872         Roo.each(this.ios_options, function(opts){
15873            
15874            opts.el.dom.removeAttribute('selected');
15875            
15876            if(opts.data[this.valueField] != v){
15877                return;
15878            }
15879            
15880            opts.el.dom.setAttribute('selected', true);
15881            
15882         }, this);
15883     }
15884
15885     /** 
15886     * @cfg {Boolean} grow 
15887     * @hide 
15888     */
15889     /** 
15890     * @cfg {Number} growMin 
15891     * @hide 
15892     */
15893     /** 
15894     * @cfg {Number} growMax 
15895     * @hide 
15896     */
15897     /**
15898      * @hide
15899      * @method autoSize
15900      */
15901 });
15902
15903 Roo.apply(Roo.bootstrap.ComboBox,  {
15904     
15905     header : {
15906         tag: 'div',
15907         cls: 'modal-header',
15908         cn: [
15909             {
15910                 tag: 'h4',
15911                 cls: 'modal-title'
15912             }
15913         ]
15914     },
15915     
15916     body : {
15917         tag: 'div',
15918         cls: 'modal-body',
15919         cn: [
15920             {
15921                 tag: 'ul',
15922                 cls: 'list-group'
15923             }
15924         ]
15925     },
15926     
15927     listItemRadio : {
15928         tag: 'li',
15929         cls: 'list-group-item',
15930         cn: [
15931             {
15932                 tag: 'span',
15933                 cls: 'roo-combobox-list-group-item-value'
15934             },
15935             {
15936                 tag: 'div',
15937                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15938                 cn: [
15939                     {
15940                         tag: 'input',
15941                         type: 'radio'
15942                     },
15943                     {
15944                         tag: 'label'
15945                     }
15946                 ]
15947             }
15948         ]
15949     },
15950     
15951     listItemCheckbox : {
15952         tag: 'li',
15953         cls: 'list-group-item',
15954         cn: [
15955             {
15956                 tag: 'span',
15957                 cls: 'roo-combobox-list-group-item-value'
15958             },
15959             {
15960                 tag: 'div',
15961                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15962                 cn: [
15963                     {
15964                         tag: 'input',
15965                         type: 'checkbox'
15966                     },
15967                     {
15968                         tag: 'label'
15969                     }
15970                 ]
15971             }
15972         ]
15973     },
15974     
15975     emptyResult : {
15976         tag: 'div',
15977         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15978     },
15979     
15980     footer : {
15981         tag: 'div',
15982         cls: 'modal-footer',
15983         cn: [
15984             {
15985                 tag: 'div',
15986                 cls: 'row',
15987                 cn: [
15988                     {
15989                         tag: 'div',
15990                         cls: 'col-xs-6 text-left',
15991                         cn: {
15992                             tag: 'button',
15993                             cls: 'btn btn-danger roo-touch-view-cancel',
15994                             html: 'Cancel'
15995                         }
15996                     },
15997                     {
15998                         tag: 'div',
15999                         cls: 'col-xs-6 text-right',
16000                         cn: {
16001                             tag: 'button',
16002                             cls: 'btn btn-success roo-touch-view-ok',
16003                             html: 'OK'
16004                         }
16005                     }
16006                 ]
16007             }
16008         ]
16009         
16010     }
16011 });
16012
16013 Roo.apply(Roo.bootstrap.ComboBox,  {
16014     
16015     touchViewTemplate : {
16016         tag: 'div',
16017         cls: 'modal fade roo-combobox-touch-view',
16018         cn: [
16019             {
16020                 tag: 'div',
16021                 cls: 'modal-dialog',
16022                 style : 'position:fixed', // we have to fix position....
16023                 cn: [
16024                     {
16025                         tag: 'div',
16026                         cls: 'modal-content',
16027                         cn: [
16028                             Roo.bootstrap.ComboBox.header,
16029                             Roo.bootstrap.ComboBox.body,
16030                             Roo.bootstrap.ComboBox.footer
16031                         ]
16032                     }
16033                 ]
16034             }
16035         ]
16036     }
16037 });/*
16038  * Based on:
16039  * Ext JS Library 1.1.1
16040  * Copyright(c) 2006-2007, Ext JS, LLC.
16041  *
16042  * Originally Released Under LGPL - original licence link has changed is not relivant.
16043  *
16044  * Fork - LGPL
16045  * <script type="text/javascript">
16046  */
16047
16048 /**
16049  * @class Roo.View
16050  * @extends Roo.util.Observable
16051  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16052  * This class also supports single and multi selection modes. <br>
16053  * Create a data model bound view:
16054  <pre><code>
16055  var store = new Roo.data.Store(...);
16056
16057  var view = new Roo.View({
16058     el : "my-element",
16059     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16060  
16061     singleSelect: true,
16062     selectedClass: "ydataview-selected",
16063     store: store
16064  });
16065
16066  // listen for node click?
16067  view.on("click", function(vw, index, node, e){
16068  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16069  });
16070
16071  // load XML data
16072  dataModel.load("foobar.xml");
16073  </code></pre>
16074  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16075  * <br><br>
16076  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16077  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16078  * 
16079  * Note: old style constructor is still suported (container, template, config)
16080  * 
16081  * @constructor
16082  * Create a new View
16083  * @param {Object} config The config object
16084  * 
16085  */
16086 Roo.View = function(config, depreciated_tpl, depreciated_config){
16087     
16088     this.parent = false;
16089     
16090     if (typeof(depreciated_tpl) == 'undefined') {
16091         // new way.. - universal constructor.
16092         Roo.apply(this, config);
16093         this.el  = Roo.get(this.el);
16094     } else {
16095         // old format..
16096         this.el  = Roo.get(config);
16097         this.tpl = depreciated_tpl;
16098         Roo.apply(this, depreciated_config);
16099     }
16100     this.wrapEl  = this.el.wrap().wrap();
16101     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16102     
16103     
16104     if(typeof(this.tpl) == "string"){
16105         this.tpl = new Roo.Template(this.tpl);
16106     } else {
16107         // support xtype ctors..
16108         this.tpl = new Roo.factory(this.tpl, Roo);
16109     }
16110     
16111     
16112     this.tpl.compile();
16113     
16114     /** @private */
16115     this.addEvents({
16116         /**
16117          * @event beforeclick
16118          * Fires before a click is processed. Returns false to cancel the default action.
16119          * @param {Roo.View} this
16120          * @param {Number} index The index of the target node
16121          * @param {HTMLElement} node The target node
16122          * @param {Roo.EventObject} e The raw event object
16123          */
16124             "beforeclick" : true,
16125         /**
16126          * @event click
16127          * Fires when a template node is clicked.
16128          * @param {Roo.View} this
16129          * @param {Number} index The index of the target node
16130          * @param {HTMLElement} node The target node
16131          * @param {Roo.EventObject} e The raw event object
16132          */
16133             "click" : true,
16134         /**
16135          * @event dblclick
16136          * Fires when a template node is double clicked.
16137          * @param {Roo.View} this
16138          * @param {Number} index The index of the target node
16139          * @param {HTMLElement} node The target node
16140          * @param {Roo.EventObject} e The raw event object
16141          */
16142             "dblclick" : true,
16143         /**
16144          * @event contextmenu
16145          * Fires when a template node is right clicked.
16146          * @param {Roo.View} this
16147          * @param {Number} index The index of the target node
16148          * @param {HTMLElement} node The target node
16149          * @param {Roo.EventObject} e The raw event object
16150          */
16151             "contextmenu" : true,
16152         /**
16153          * @event selectionchange
16154          * Fires when the selected nodes change.
16155          * @param {Roo.View} this
16156          * @param {Array} selections Array of the selected nodes
16157          */
16158             "selectionchange" : true,
16159     
16160         /**
16161          * @event beforeselect
16162          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16163          * @param {Roo.View} this
16164          * @param {HTMLElement} node The node to be selected
16165          * @param {Array} selections Array of currently selected nodes
16166          */
16167             "beforeselect" : true,
16168         /**
16169          * @event preparedata
16170          * Fires on every row to render, to allow you to change the data.
16171          * @param {Roo.View} this
16172          * @param {Object} data to be rendered (change this)
16173          */
16174           "preparedata" : true
16175           
16176           
16177         });
16178
16179
16180
16181     this.el.on({
16182         "click": this.onClick,
16183         "dblclick": this.onDblClick,
16184         "contextmenu": this.onContextMenu,
16185         scope:this
16186     });
16187
16188     this.selections = [];
16189     this.nodes = [];
16190     this.cmp = new Roo.CompositeElementLite([]);
16191     if(this.store){
16192         this.store = Roo.factory(this.store, Roo.data);
16193         this.setStore(this.store, true);
16194     }
16195     
16196     if ( this.footer && this.footer.xtype) {
16197            
16198          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16199         
16200         this.footer.dataSource = this.store;
16201         this.footer.container = fctr;
16202         this.footer = Roo.factory(this.footer, Roo);
16203         fctr.insertFirst(this.el);
16204         
16205         // this is a bit insane - as the paging toolbar seems to detach the el..
16206 //        dom.parentNode.parentNode.parentNode
16207          // they get detached?
16208     }
16209     
16210     
16211     Roo.View.superclass.constructor.call(this);
16212     
16213     
16214 };
16215
16216 Roo.extend(Roo.View, Roo.util.Observable, {
16217     
16218      /**
16219      * @cfg {Roo.data.Store} store Data store to load data from.
16220      */
16221     store : false,
16222     
16223     /**
16224      * @cfg {String|Roo.Element} el The container element.
16225      */
16226     el : '',
16227     
16228     /**
16229      * @cfg {String|Roo.Template} tpl The template used by this View 
16230      */
16231     tpl : false,
16232     /**
16233      * @cfg {String} dataName the named area of the template to use as the data area
16234      *                          Works with domtemplates roo-name="name"
16235      */
16236     dataName: false,
16237     /**
16238      * @cfg {String} selectedClass The css class to add to selected nodes
16239      */
16240     selectedClass : "x-view-selected",
16241      /**
16242      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16243      */
16244     emptyText : "",
16245     
16246     /**
16247      * @cfg {String} text to display on mask (default Loading)
16248      */
16249     mask : false,
16250     /**
16251      * @cfg {Boolean} multiSelect Allow multiple selection
16252      */
16253     multiSelect : false,
16254     /**
16255      * @cfg {Boolean} singleSelect Allow single selection
16256      */
16257     singleSelect:  false,
16258     
16259     /**
16260      * @cfg {Boolean} toggleSelect - selecting 
16261      */
16262     toggleSelect : false,
16263     
16264     /**
16265      * @cfg {Boolean} tickable - selecting 
16266      */
16267     tickable : false,
16268     
16269     /**
16270      * Returns the element this view is bound to.
16271      * @return {Roo.Element}
16272      */
16273     getEl : function(){
16274         return this.wrapEl;
16275     },
16276     
16277     
16278
16279     /**
16280      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16281      */
16282     refresh : function(){
16283         //Roo.log('refresh');
16284         var t = this.tpl;
16285         
16286         // if we are using something like 'domtemplate', then
16287         // the what gets used is:
16288         // t.applySubtemplate(NAME, data, wrapping data..)
16289         // the outer template then get' applied with
16290         //     the store 'extra data'
16291         // and the body get's added to the
16292         //      roo-name="data" node?
16293         //      <span class='roo-tpl-{name}'></span> ?????
16294         
16295         
16296         
16297         this.clearSelections();
16298         this.el.update("");
16299         var html = [];
16300         var records = this.store.getRange();
16301         if(records.length < 1) {
16302             
16303             // is this valid??  = should it render a template??
16304             
16305             this.el.update(this.emptyText);
16306             return;
16307         }
16308         var el = this.el;
16309         if (this.dataName) {
16310             this.el.update(t.apply(this.store.meta)); //????
16311             el = this.el.child('.roo-tpl-' + this.dataName);
16312         }
16313         
16314         for(var i = 0, len = records.length; i < len; i++){
16315             var data = this.prepareData(records[i].data, i, records[i]);
16316             this.fireEvent("preparedata", this, data, i, records[i]);
16317             
16318             var d = Roo.apply({}, data);
16319             
16320             if(this.tickable){
16321                 Roo.apply(d, {'roo-id' : Roo.id()});
16322                 
16323                 var _this = this;
16324             
16325                 Roo.each(this.parent.item, function(item){
16326                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16327                         return;
16328                     }
16329                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16330                 });
16331             }
16332             
16333             html[html.length] = Roo.util.Format.trim(
16334                 this.dataName ?
16335                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16336                     t.apply(d)
16337             );
16338         }
16339         
16340         
16341         
16342         el.update(html.join(""));
16343         this.nodes = el.dom.childNodes;
16344         this.updateIndexes(0);
16345     },
16346     
16347
16348     /**
16349      * Function to override to reformat the data that is sent to
16350      * the template for each node.
16351      * DEPRICATED - use the preparedata event handler.
16352      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16353      * a JSON object for an UpdateManager bound view).
16354      */
16355     prepareData : function(data, index, record)
16356     {
16357         this.fireEvent("preparedata", this, data, index, record);
16358         return data;
16359     },
16360
16361     onUpdate : function(ds, record){
16362         // Roo.log('on update');   
16363         this.clearSelections();
16364         var index = this.store.indexOf(record);
16365         var n = this.nodes[index];
16366         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16367         n.parentNode.removeChild(n);
16368         this.updateIndexes(index, index);
16369     },
16370
16371     
16372     
16373 // --------- FIXME     
16374     onAdd : function(ds, records, index)
16375     {
16376         //Roo.log(['on Add', ds, records, index] );        
16377         this.clearSelections();
16378         if(this.nodes.length == 0){
16379             this.refresh();
16380             return;
16381         }
16382         var n = this.nodes[index];
16383         for(var i = 0, len = records.length; i < len; i++){
16384             var d = this.prepareData(records[i].data, i, records[i]);
16385             if(n){
16386                 this.tpl.insertBefore(n, d);
16387             }else{
16388                 
16389                 this.tpl.append(this.el, d);
16390             }
16391         }
16392         this.updateIndexes(index);
16393     },
16394
16395     onRemove : function(ds, record, index){
16396        // Roo.log('onRemove');
16397         this.clearSelections();
16398         var el = this.dataName  ?
16399             this.el.child('.roo-tpl-' + this.dataName) :
16400             this.el; 
16401         
16402         el.dom.removeChild(this.nodes[index]);
16403         this.updateIndexes(index);
16404     },
16405
16406     /**
16407      * Refresh an individual node.
16408      * @param {Number} index
16409      */
16410     refreshNode : function(index){
16411         this.onUpdate(this.store, this.store.getAt(index));
16412     },
16413
16414     updateIndexes : function(startIndex, endIndex){
16415         var ns = this.nodes;
16416         startIndex = startIndex || 0;
16417         endIndex = endIndex || ns.length - 1;
16418         for(var i = startIndex; i <= endIndex; i++){
16419             ns[i].nodeIndex = i;
16420         }
16421     },
16422
16423     /**
16424      * Changes the data store this view uses and refresh the view.
16425      * @param {Store} store
16426      */
16427     setStore : function(store, initial){
16428         if(!initial && this.store){
16429             this.store.un("datachanged", this.refresh);
16430             this.store.un("add", this.onAdd);
16431             this.store.un("remove", this.onRemove);
16432             this.store.un("update", this.onUpdate);
16433             this.store.un("clear", this.refresh);
16434             this.store.un("beforeload", this.onBeforeLoad);
16435             this.store.un("load", this.onLoad);
16436             this.store.un("loadexception", this.onLoad);
16437         }
16438         if(store){
16439           
16440             store.on("datachanged", this.refresh, this);
16441             store.on("add", this.onAdd, this);
16442             store.on("remove", this.onRemove, this);
16443             store.on("update", this.onUpdate, this);
16444             store.on("clear", this.refresh, this);
16445             store.on("beforeload", this.onBeforeLoad, this);
16446             store.on("load", this.onLoad, this);
16447             store.on("loadexception", this.onLoad, this);
16448         }
16449         
16450         if(store){
16451             this.refresh();
16452         }
16453     },
16454     /**
16455      * onbeforeLoad - masks the loading area.
16456      *
16457      */
16458     onBeforeLoad : function(store,opts)
16459     {
16460          //Roo.log('onBeforeLoad');   
16461         if (!opts.add) {
16462             this.el.update("");
16463         }
16464         this.el.mask(this.mask ? this.mask : "Loading" ); 
16465     },
16466     onLoad : function ()
16467     {
16468         this.el.unmask();
16469     },
16470     
16471
16472     /**
16473      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16474      * @param {HTMLElement} node
16475      * @return {HTMLElement} The template node
16476      */
16477     findItemFromChild : function(node){
16478         var el = this.dataName  ?
16479             this.el.child('.roo-tpl-' + this.dataName,true) :
16480             this.el.dom; 
16481         
16482         if(!node || node.parentNode == el){
16483                     return node;
16484             }
16485             var p = node.parentNode;
16486             while(p && p != el){
16487             if(p.parentNode == el){
16488                 return p;
16489             }
16490             p = p.parentNode;
16491         }
16492             return null;
16493     },
16494
16495     /** @ignore */
16496     onClick : function(e){
16497         var item = this.findItemFromChild(e.getTarget());
16498         if(item){
16499             var index = this.indexOf(item);
16500             if(this.onItemClick(item, index, e) !== false){
16501                 this.fireEvent("click", this, index, item, e);
16502             }
16503         }else{
16504             this.clearSelections();
16505         }
16506     },
16507
16508     /** @ignore */
16509     onContextMenu : function(e){
16510         var item = this.findItemFromChild(e.getTarget());
16511         if(item){
16512             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16513         }
16514     },
16515
16516     /** @ignore */
16517     onDblClick : function(e){
16518         var item = this.findItemFromChild(e.getTarget());
16519         if(item){
16520             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16521         }
16522     },
16523
16524     onItemClick : function(item, index, e)
16525     {
16526         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16527             return false;
16528         }
16529         if (this.toggleSelect) {
16530             var m = this.isSelected(item) ? 'unselect' : 'select';
16531             //Roo.log(m);
16532             var _t = this;
16533             _t[m](item, true, false);
16534             return true;
16535         }
16536         if(this.multiSelect || this.singleSelect){
16537             if(this.multiSelect && e.shiftKey && this.lastSelection){
16538                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16539             }else{
16540                 this.select(item, this.multiSelect && e.ctrlKey);
16541                 this.lastSelection = item;
16542             }
16543             
16544             if(!this.tickable){
16545                 e.preventDefault();
16546             }
16547             
16548         }
16549         return true;
16550     },
16551
16552     /**
16553      * Get the number of selected nodes.
16554      * @return {Number}
16555      */
16556     getSelectionCount : function(){
16557         return this.selections.length;
16558     },
16559
16560     /**
16561      * Get the currently selected nodes.
16562      * @return {Array} An array of HTMLElements
16563      */
16564     getSelectedNodes : function(){
16565         return this.selections;
16566     },
16567
16568     /**
16569      * Get the indexes of the selected nodes.
16570      * @return {Array}
16571      */
16572     getSelectedIndexes : function(){
16573         var indexes = [], s = this.selections;
16574         for(var i = 0, len = s.length; i < len; i++){
16575             indexes.push(s[i].nodeIndex);
16576         }
16577         return indexes;
16578     },
16579
16580     /**
16581      * Clear all selections
16582      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16583      */
16584     clearSelections : function(suppressEvent){
16585         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16586             this.cmp.elements = this.selections;
16587             this.cmp.removeClass(this.selectedClass);
16588             this.selections = [];
16589             if(!suppressEvent){
16590                 this.fireEvent("selectionchange", this, this.selections);
16591             }
16592         }
16593     },
16594
16595     /**
16596      * Returns true if the passed node is selected
16597      * @param {HTMLElement/Number} node The node or node index
16598      * @return {Boolean}
16599      */
16600     isSelected : function(node){
16601         var s = this.selections;
16602         if(s.length < 1){
16603             return false;
16604         }
16605         node = this.getNode(node);
16606         return s.indexOf(node) !== -1;
16607     },
16608
16609     /**
16610      * Selects nodes.
16611      * @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
16612      * @param {Boolean} keepExisting (optional) true to keep existing selections
16613      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16614      */
16615     select : function(nodeInfo, keepExisting, suppressEvent){
16616         if(nodeInfo instanceof Array){
16617             if(!keepExisting){
16618                 this.clearSelections(true);
16619             }
16620             for(var i = 0, len = nodeInfo.length; i < len; i++){
16621                 this.select(nodeInfo[i], true, true);
16622             }
16623             return;
16624         } 
16625         var node = this.getNode(nodeInfo);
16626         if(!node || this.isSelected(node)){
16627             return; // already selected.
16628         }
16629         if(!keepExisting){
16630             this.clearSelections(true);
16631         }
16632         
16633         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16634             Roo.fly(node).addClass(this.selectedClass);
16635             this.selections.push(node);
16636             if(!suppressEvent){
16637                 this.fireEvent("selectionchange", this, this.selections);
16638             }
16639         }
16640         
16641         
16642     },
16643       /**
16644      * Unselects nodes.
16645      * @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
16646      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16647      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16648      */
16649     unselect : function(nodeInfo, keepExisting, suppressEvent)
16650     {
16651         if(nodeInfo instanceof Array){
16652             Roo.each(this.selections, function(s) {
16653                 this.unselect(s, nodeInfo);
16654             }, this);
16655             return;
16656         }
16657         var node = this.getNode(nodeInfo);
16658         if(!node || !this.isSelected(node)){
16659             //Roo.log("not selected");
16660             return; // not selected.
16661         }
16662         // fireevent???
16663         var ns = [];
16664         Roo.each(this.selections, function(s) {
16665             if (s == node ) {
16666                 Roo.fly(node).removeClass(this.selectedClass);
16667
16668                 return;
16669             }
16670             ns.push(s);
16671         },this);
16672         
16673         this.selections= ns;
16674         this.fireEvent("selectionchange", this, this.selections);
16675     },
16676
16677     /**
16678      * Gets a template node.
16679      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16680      * @return {HTMLElement} The node or null if it wasn't found
16681      */
16682     getNode : function(nodeInfo){
16683         if(typeof nodeInfo == "string"){
16684             return document.getElementById(nodeInfo);
16685         }else if(typeof nodeInfo == "number"){
16686             return this.nodes[nodeInfo];
16687         }
16688         return nodeInfo;
16689     },
16690
16691     /**
16692      * Gets a range template nodes.
16693      * @param {Number} startIndex
16694      * @param {Number} endIndex
16695      * @return {Array} An array of nodes
16696      */
16697     getNodes : function(start, end){
16698         var ns = this.nodes;
16699         start = start || 0;
16700         end = typeof end == "undefined" ? ns.length - 1 : end;
16701         var nodes = [];
16702         if(start <= end){
16703             for(var i = start; i <= end; i++){
16704                 nodes.push(ns[i]);
16705             }
16706         } else{
16707             for(var i = start; i >= end; i--){
16708                 nodes.push(ns[i]);
16709             }
16710         }
16711         return nodes;
16712     },
16713
16714     /**
16715      * Finds the index of the passed node
16716      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16717      * @return {Number} The index of the node or -1
16718      */
16719     indexOf : function(node){
16720         node = this.getNode(node);
16721         if(typeof node.nodeIndex == "number"){
16722             return node.nodeIndex;
16723         }
16724         var ns = this.nodes;
16725         for(var i = 0, len = ns.length; i < len; i++){
16726             if(ns[i] == node){
16727                 return i;
16728             }
16729         }
16730         return -1;
16731     }
16732 });
16733 /*
16734  * - LGPL
16735  *
16736  * based on jquery fullcalendar
16737  * 
16738  */
16739
16740 Roo.bootstrap = Roo.bootstrap || {};
16741 /**
16742  * @class Roo.bootstrap.Calendar
16743  * @extends Roo.bootstrap.Component
16744  * Bootstrap Calendar class
16745  * @cfg {Boolean} loadMask (true|false) default false
16746  * @cfg {Object} header generate the user specific header of the calendar, default false
16747
16748  * @constructor
16749  * Create a new Container
16750  * @param {Object} config The config object
16751  */
16752
16753
16754
16755 Roo.bootstrap.Calendar = function(config){
16756     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16757      this.addEvents({
16758         /**
16759              * @event select
16760              * Fires when a date is selected
16761              * @param {DatePicker} this
16762              * @param {Date} date The selected date
16763              */
16764         'select': true,
16765         /**
16766              * @event monthchange
16767              * Fires when the displayed month changes 
16768              * @param {DatePicker} this
16769              * @param {Date} date The selected month
16770              */
16771         'monthchange': true,
16772         /**
16773              * @event evententer
16774              * Fires when mouse over an event
16775              * @param {Calendar} this
16776              * @param {event} Event
16777              */
16778         'evententer': true,
16779         /**
16780              * @event eventleave
16781              * Fires when the mouse leaves an
16782              * @param {Calendar} this
16783              * @param {event}
16784              */
16785         'eventleave': true,
16786         /**
16787              * @event eventclick
16788              * Fires when the mouse click an
16789              * @param {Calendar} this
16790              * @param {event}
16791              */
16792         'eventclick': true
16793         
16794     });
16795
16796 };
16797
16798 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16799     
16800      /**
16801      * @cfg {Number} startDay
16802      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16803      */
16804     startDay : 0,
16805     
16806     loadMask : false,
16807     
16808     header : false,
16809       
16810     getAutoCreate : function(){
16811         
16812         
16813         var fc_button = function(name, corner, style, content ) {
16814             return Roo.apply({},{
16815                 tag : 'span',
16816                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16817                          (corner.length ?
16818                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16819                             ''
16820                         ),
16821                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16822                 unselectable: 'on'
16823             });
16824         };
16825         
16826         var header = {};
16827         
16828         if(!this.header){
16829             header = {
16830                 tag : 'table',
16831                 cls : 'fc-header',
16832                 style : 'width:100%',
16833                 cn : [
16834                     {
16835                         tag: 'tr',
16836                         cn : [
16837                             {
16838                                 tag : 'td',
16839                                 cls : 'fc-header-left',
16840                                 cn : [
16841                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16842                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16843                                     { tag: 'span', cls: 'fc-header-space' },
16844                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16845
16846
16847                                 ]
16848                             },
16849
16850                             {
16851                                 tag : 'td',
16852                                 cls : 'fc-header-center',
16853                                 cn : [
16854                                     {
16855                                         tag: 'span',
16856                                         cls: 'fc-header-title',
16857                                         cn : {
16858                                             tag: 'H2',
16859                                             html : 'month / year'
16860                                         }
16861                                     }
16862
16863                                 ]
16864                             },
16865                             {
16866                                 tag : 'td',
16867                                 cls : 'fc-header-right',
16868                                 cn : [
16869                               /*      fc_button('month', 'left', '', 'month' ),
16870                                     fc_button('week', '', '', 'week' ),
16871                                     fc_button('day', 'right', '', 'day' )
16872                                 */    
16873
16874                                 ]
16875                             }
16876
16877                         ]
16878                     }
16879                 ]
16880             };
16881         }
16882         
16883         header = this.header;
16884         
16885        
16886         var cal_heads = function() {
16887             var ret = [];
16888             // fixme - handle this.
16889             
16890             for (var i =0; i < Date.dayNames.length; i++) {
16891                 var d = Date.dayNames[i];
16892                 ret.push({
16893                     tag: 'th',
16894                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16895                     html : d.substring(0,3)
16896                 });
16897                 
16898             }
16899             ret[0].cls += ' fc-first';
16900             ret[6].cls += ' fc-last';
16901             return ret;
16902         };
16903         var cal_cell = function(n) {
16904             return  {
16905                 tag: 'td',
16906                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16907                 cn : [
16908                     {
16909                         cn : [
16910                             {
16911                                 cls: 'fc-day-number',
16912                                 html: 'D'
16913                             },
16914                             {
16915                                 cls: 'fc-day-content',
16916                              
16917                                 cn : [
16918                                      {
16919                                         style: 'position: relative;' // height: 17px;
16920                                     }
16921                                 ]
16922                             }
16923                             
16924                             
16925                         ]
16926                     }
16927                 ]
16928                 
16929             }
16930         };
16931         var cal_rows = function() {
16932             
16933             var ret = [];
16934             for (var r = 0; r < 6; r++) {
16935                 var row= {
16936                     tag : 'tr',
16937                     cls : 'fc-week',
16938                     cn : []
16939                 };
16940                 
16941                 for (var i =0; i < Date.dayNames.length; i++) {
16942                     var d = Date.dayNames[i];
16943                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16944
16945                 }
16946                 row.cn[0].cls+=' fc-first';
16947                 row.cn[0].cn[0].style = 'min-height:90px';
16948                 row.cn[6].cls+=' fc-last';
16949                 ret.push(row);
16950                 
16951             }
16952             ret[0].cls += ' fc-first';
16953             ret[4].cls += ' fc-prev-last';
16954             ret[5].cls += ' fc-last';
16955             return ret;
16956             
16957         };
16958         
16959         var cal_table = {
16960             tag: 'table',
16961             cls: 'fc-border-separate',
16962             style : 'width:100%',
16963             cellspacing  : 0,
16964             cn : [
16965                 { 
16966                     tag: 'thead',
16967                     cn : [
16968                         { 
16969                             tag: 'tr',
16970                             cls : 'fc-first fc-last',
16971                             cn : cal_heads()
16972                         }
16973                     ]
16974                 },
16975                 { 
16976                     tag: 'tbody',
16977                     cn : cal_rows()
16978                 }
16979                   
16980             ]
16981         };
16982          
16983          var cfg = {
16984             cls : 'fc fc-ltr',
16985             cn : [
16986                 header,
16987                 {
16988                     cls : 'fc-content',
16989                     style : "position: relative;",
16990                     cn : [
16991                         {
16992                             cls : 'fc-view fc-view-month fc-grid',
16993                             style : 'position: relative',
16994                             unselectable : 'on',
16995                             cn : [
16996                                 {
16997                                     cls : 'fc-event-container',
16998                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16999                                 },
17000                                 cal_table
17001                             ]
17002                         }
17003                     ]
17004     
17005                 }
17006            ] 
17007             
17008         };
17009         
17010          
17011         
17012         return cfg;
17013     },
17014     
17015     
17016     initEvents : function()
17017     {
17018         if(!this.store){
17019             throw "can not find store for calendar";
17020         }
17021         
17022         var mark = {
17023             tag: "div",
17024             cls:"x-dlg-mask",
17025             style: "text-align:center",
17026             cn: [
17027                 {
17028                     tag: "div",
17029                     style: "background-color:white;width:50%;margin:250 auto",
17030                     cn: [
17031                         {
17032                             tag: "img",
17033                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17034                         },
17035                         {
17036                             tag: "span",
17037                             html: "Loading"
17038                         }
17039                         
17040                     ]
17041                 }
17042             ]
17043         };
17044         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17045         
17046         var size = this.el.select('.fc-content', true).first().getSize();
17047         this.maskEl.setSize(size.width, size.height);
17048         this.maskEl.enableDisplayMode("block");
17049         if(!this.loadMask){
17050             this.maskEl.hide();
17051         }
17052         
17053         this.store = Roo.factory(this.store, Roo.data);
17054         this.store.on('load', this.onLoad, this);
17055         this.store.on('beforeload', this.onBeforeLoad, this);
17056         
17057         this.resize();
17058         
17059         this.cells = this.el.select('.fc-day',true);
17060         //Roo.log(this.cells);
17061         this.textNodes = this.el.query('.fc-day-number');
17062         this.cells.addClassOnOver('fc-state-hover');
17063         
17064         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17065         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17066         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17067         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17068         
17069         this.on('monthchange', this.onMonthChange, this);
17070         
17071         this.update(new Date().clearTime());
17072     },
17073     
17074     resize : function() {
17075         var sz  = this.el.getSize();
17076         
17077         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17078         this.el.select('.fc-day-content div',true).setHeight(34);
17079     },
17080     
17081     
17082     // private
17083     showPrevMonth : function(e){
17084         this.update(this.activeDate.add("mo", -1));
17085     },
17086     showToday : function(e){
17087         this.update(new Date().clearTime());
17088     },
17089     // private
17090     showNextMonth : function(e){
17091         this.update(this.activeDate.add("mo", 1));
17092     },
17093
17094     // private
17095     showPrevYear : function(){
17096         this.update(this.activeDate.add("y", -1));
17097     },
17098
17099     // private
17100     showNextYear : function(){
17101         this.update(this.activeDate.add("y", 1));
17102     },
17103
17104     
17105    // private
17106     update : function(date)
17107     {
17108         var vd = this.activeDate;
17109         this.activeDate = date;
17110 //        if(vd && this.el){
17111 //            var t = date.getTime();
17112 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17113 //                Roo.log('using add remove');
17114 //                
17115 //                this.fireEvent('monthchange', this, date);
17116 //                
17117 //                this.cells.removeClass("fc-state-highlight");
17118 //                this.cells.each(function(c){
17119 //                   if(c.dateValue == t){
17120 //                       c.addClass("fc-state-highlight");
17121 //                       setTimeout(function(){
17122 //                            try{c.dom.firstChild.focus();}catch(e){}
17123 //                       }, 50);
17124 //                       return false;
17125 //                   }
17126 //                   return true;
17127 //                });
17128 //                return;
17129 //            }
17130 //        }
17131         
17132         var days = date.getDaysInMonth();
17133         
17134         var firstOfMonth = date.getFirstDateOfMonth();
17135         var startingPos = firstOfMonth.getDay()-this.startDay;
17136         
17137         if(startingPos < this.startDay){
17138             startingPos += 7;
17139         }
17140         
17141         var pm = date.add(Date.MONTH, -1);
17142         var prevStart = pm.getDaysInMonth()-startingPos;
17143 //        
17144         this.cells = this.el.select('.fc-day',true);
17145         this.textNodes = this.el.query('.fc-day-number');
17146         this.cells.addClassOnOver('fc-state-hover');
17147         
17148         var cells = this.cells.elements;
17149         var textEls = this.textNodes;
17150         
17151         Roo.each(cells, function(cell){
17152             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17153         });
17154         
17155         days += startingPos;
17156
17157         // convert everything to numbers so it's fast
17158         var day = 86400000;
17159         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17160         //Roo.log(d);
17161         //Roo.log(pm);
17162         //Roo.log(prevStart);
17163         
17164         var today = new Date().clearTime().getTime();
17165         var sel = date.clearTime().getTime();
17166         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17167         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17168         var ddMatch = this.disabledDatesRE;
17169         var ddText = this.disabledDatesText;
17170         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17171         var ddaysText = this.disabledDaysText;
17172         var format = this.format;
17173         
17174         var setCellClass = function(cal, cell){
17175             cell.row = 0;
17176             cell.events = [];
17177             cell.more = [];
17178             //Roo.log('set Cell Class');
17179             cell.title = "";
17180             var t = d.getTime();
17181             
17182             //Roo.log(d);
17183             
17184             cell.dateValue = t;
17185             if(t == today){
17186                 cell.className += " fc-today";
17187                 cell.className += " fc-state-highlight";
17188                 cell.title = cal.todayText;
17189             }
17190             if(t == sel){
17191                 // disable highlight in other month..
17192                 //cell.className += " fc-state-highlight";
17193                 
17194             }
17195             // disabling
17196             if(t < min) {
17197                 cell.className = " fc-state-disabled";
17198                 cell.title = cal.minText;
17199                 return;
17200             }
17201             if(t > max) {
17202                 cell.className = " fc-state-disabled";
17203                 cell.title = cal.maxText;
17204                 return;
17205             }
17206             if(ddays){
17207                 if(ddays.indexOf(d.getDay()) != -1){
17208                     cell.title = ddaysText;
17209                     cell.className = " fc-state-disabled";
17210                 }
17211             }
17212             if(ddMatch && format){
17213                 var fvalue = d.dateFormat(format);
17214                 if(ddMatch.test(fvalue)){
17215                     cell.title = ddText.replace("%0", fvalue);
17216                     cell.className = " fc-state-disabled";
17217                 }
17218             }
17219             
17220             if (!cell.initialClassName) {
17221                 cell.initialClassName = cell.dom.className;
17222             }
17223             
17224             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17225         };
17226
17227         var i = 0;
17228         
17229         for(; i < startingPos; i++) {
17230             textEls[i].innerHTML = (++prevStart);
17231             d.setDate(d.getDate()+1);
17232             
17233             cells[i].className = "fc-past fc-other-month";
17234             setCellClass(this, cells[i]);
17235         }
17236         
17237         var intDay = 0;
17238         
17239         for(; i < days; i++){
17240             intDay = i - startingPos + 1;
17241             textEls[i].innerHTML = (intDay);
17242             d.setDate(d.getDate()+1);
17243             
17244             cells[i].className = ''; // "x-date-active";
17245             setCellClass(this, cells[i]);
17246         }
17247         var extraDays = 0;
17248         
17249         for(; i < 42; i++) {
17250             textEls[i].innerHTML = (++extraDays);
17251             d.setDate(d.getDate()+1);
17252             
17253             cells[i].className = "fc-future fc-other-month";
17254             setCellClass(this, cells[i]);
17255         }
17256         
17257         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17258         
17259         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17260         
17261         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17262         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17263         
17264         if(totalRows != 6){
17265             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17266             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17267         }
17268         
17269         this.fireEvent('monthchange', this, date);
17270         
17271         
17272         /*
17273         if(!this.internalRender){
17274             var main = this.el.dom.firstChild;
17275             var w = main.offsetWidth;
17276             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17277             Roo.fly(main).setWidth(w);
17278             this.internalRender = true;
17279             // opera does not respect the auto grow header center column
17280             // then, after it gets a width opera refuses to recalculate
17281             // without a second pass
17282             if(Roo.isOpera && !this.secondPass){
17283                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17284                 this.secondPass = true;
17285                 this.update.defer(10, this, [date]);
17286             }
17287         }
17288         */
17289         
17290     },
17291     
17292     findCell : function(dt) {
17293         dt = dt.clearTime().getTime();
17294         var ret = false;
17295         this.cells.each(function(c){
17296             //Roo.log("check " +c.dateValue + '?=' + dt);
17297             if(c.dateValue == dt){
17298                 ret = c;
17299                 return false;
17300             }
17301             return true;
17302         });
17303         
17304         return ret;
17305     },
17306     
17307     findCells : function(ev) {
17308         var s = ev.start.clone().clearTime().getTime();
17309        // Roo.log(s);
17310         var e= ev.end.clone().clearTime().getTime();
17311        // Roo.log(e);
17312         var ret = [];
17313         this.cells.each(function(c){
17314              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17315             
17316             if(c.dateValue > e){
17317                 return ;
17318             }
17319             if(c.dateValue < s){
17320                 return ;
17321             }
17322             ret.push(c);
17323         });
17324         
17325         return ret;    
17326     },
17327     
17328 //    findBestRow: function(cells)
17329 //    {
17330 //        var ret = 0;
17331 //        
17332 //        for (var i =0 ; i < cells.length;i++) {
17333 //            ret  = Math.max(cells[i].rows || 0,ret);
17334 //        }
17335 //        return ret;
17336 //        
17337 //    },
17338     
17339     
17340     addItem : function(ev)
17341     {
17342         // look for vertical location slot in
17343         var cells = this.findCells(ev);
17344         
17345 //        ev.row = this.findBestRow(cells);
17346         
17347         // work out the location.
17348         
17349         var crow = false;
17350         var rows = [];
17351         for(var i =0; i < cells.length; i++) {
17352             
17353             cells[i].row = cells[0].row;
17354             
17355             if(i == 0){
17356                 cells[i].row = cells[i].row + 1;
17357             }
17358             
17359             if (!crow) {
17360                 crow = {
17361                     start : cells[i],
17362                     end :  cells[i]
17363                 };
17364                 continue;
17365             }
17366             if (crow.start.getY() == cells[i].getY()) {
17367                 // on same row.
17368                 crow.end = cells[i];
17369                 continue;
17370             }
17371             // different row.
17372             rows.push(crow);
17373             crow = {
17374                 start: cells[i],
17375                 end : cells[i]
17376             };
17377             
17378         }
17379         
17380         rows.push(crow);
17381         ev.els = [];
17382         ev.rows = rows;
17383         ev.cells = cells;
17384         
17385         cells[0].events.push(ev);
17386         
17387         this.calevents.push(ev);
17388     },
17389     
17390     clearEvents: function() {
17391         
17392         if(!this.calevents){
17393             return;
17394         }
17395         
17396         Roo.each(this.cells.elements, function(c){
17397             c.row = 0;
17398             c.events = [];
17399             c.more = [];
17400         });
17401         
17402         Roo.each(this.calevents, function(e) {
17403             Roo.each(e.els, function(el) {
17404                 el.un('mouseenter' ,this.onEventEnter, this);
17405                 el.un('mouseleave' ,this.onEventLeave, this);
17406                 el.remove();
17407             },this);
17408         },this);
17409         
17410         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17411             e.remove();
17412         });
17413         
17414     },
17415     
17416     renderEvents: function()
17417     {   
17418         var _this = this;
17419         
17420         this.cells.each(function(c) {
17421             
17422             if(c.row < 5){
17423                 return;
17424             }
17425             
17426             var ev = c.events;
17427             
17428             var r = 4;
17429             if(c.row != c.events.length){
17430                 r = 4 - (4 - (c.row - c.events.length));
17431             }
17432             
17433             c.events = ev.slice(0, r);
17434             c.more = ev.slice(r);
17435             
17436             if(c.more.length && c.more.length == 1){
17437                 c.events.push(c.more.pop());
17438             }
17439             
17440             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17441             
17442         });
17443             
17444         this.cells.each(function(c) {
17445             
17446             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17447             
17448             
17449             for (var e = 0; e < c.events.length; e++){
17450                 var ev = c.events[e];
17451                 var rows = ev.rows;
17452                 
17453                 for(var i = 0; i < rows.length; i++) {
17454                 
17455                     // how many rows should it span..
17456
17457                     var  cfg = {
17458                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17459                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17460
17461                         unselectable : "on",
17462                         cn : [
17463                             {
17464                                 cls: 'fc-event-inner',
17465                                 cn : [
17466     //                                {
17467     //                                  tag:'span',
17468     //                                  cls: 'fc-event-time',
17469     //                                  html : cells.length > 1 ? '' : ev.time
17470     //                                },
17471                                     {
17472                                       tag:'span',
17473                                       cls: 'fc-event-title',
17474                                       html : String.format('{0}', ev.title)
17475                                     }
17476
17477
17478                                 ]
17479                             },
17480                             {
17481                                 cls: 'ui-resizable-handle ui-resizable-e',
17482                                 html : '&nbsp;&nbsp;&nbsp'
17483                             }
17484
17485                         ]
17486                     };
17487
17488                     if (i == 0) {
17489                         cfg.cls += ' fc-event-start';
17490                     }
17491                     if ((i+1) == rows.length) {
17492                         cfg.cls += ' fc-event-end';
17493                     }
17494
17495                     var ctr = _this.el.select('.fc-event-container',true).first();
17496                     var cg = ctr.createChild(cfg);
17497
17498                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17499                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17500
17501                     var r = (c.more.length) ? 1 : 0;
17502                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17503                     cg.setWidth(ebox.right - sbox.x -2);
17504
17505                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17506                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17507                     cg.on('click', _this.onEventClick, _this, ev);
17508
17509                     ev.els.push(cg);
17510                     
17511                 }
17512                 
17513             }
17514             
17515             
17516             if(c.more.length){
17517                 var  cfg = {
17518                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17519                     style : 'position: absolute',
17520                     unselectable : "on",
17521                     cn : [
17522                         {
17523                             cls: 'fc-event-inner',
17524                             cn : [
17525                                 {
17526                                   tag:'span',
17527                                   cls: 'fc-event-title',
17528                                   html : 'More'
17529                                 }
17530
17531
17532                             ]
17533                         },
17534                         {
17535                             cls: 'ui-resizable-handle ui-resizable-e',
17536                             html : '&nbsp;&nbsp;&nbsp'
17537                         }
17538
17539                     ]
17540                 };
17541
17542                 var ctr = _this.el.select('.fc-event-container',true).first();
17543                 var cg = ctr.createChild(cfg);
17544
17545                 var sbox = c.select('.fc-day-content',true).first().getBox();
17546                 var ebox = c.select('.fc-day-content',true).first().getBox();
17547                 //Roo.log(cg);
17548                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17549                 cg.setWidth(ebox.right - sbox.x -2);
17550
17551                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17552                 
17553             }
17554             
17555         });
17556         
17557         
17558         
17559     },
17560     
17561     onEventEnter: function (e, el,event,d) {
17562         this.fireEvent('evententer', this, el, event);
17563     },
17564     
17565     onEventLeave: function (e, el,event,d) {
17566         this.fireEvent('eventleave', this, el, event);
17567     },
17568     
17569     onEventClick: function (e, el,event,d) {
17570         this.fireEvent('eventclick', this, el, event);
17571     },
17572     
17573     onMonthChange: function () {
17574         this.store.load();
17575     },
17576     
17577     onMoreEventClick: function(e, el, more)
17578     {
17579         var _this = this;
17580         
17581         this.calpopover.placement = 'right';
17582         this.calpopover.setTitle('More');
17583         
17584         this.calpopover.setContent('');
17585         
17586         var ctr = this.calpopover.el.select('.popover-content', true).first();
17587         
17588         Roo.each(more, function(m){
17589             var cfg = {
17590                 cls : 'fc-event-hori fc-event-draggable',
17591                 html : m.title
17592             };
17593             var cg = ctr.createChild(cfg);
17594             
17595             cg.on('click', _this.onEventClick, _this, m);
17596         });
17597         
17598         this.calpopover.show(el);
17599         
17600         
17601     },
17602     
17603     onLoad: function () 
17604     {   
17605         this.calevents = [];
17606         var cal = this;
17607         
17608         if(this.store.getCount() > 0){
17609             this.store.data.each(function(d){
17610                cal.addItem({
17611                     id : d.data.id,
17612                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17613                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17614                     time : d.data.start_time,
17615                     title : d.data.title,
17616                     description : d.data.description,
17617                     venue : d.data.venue
17618                 });
17619             });
17620         }
17621         
17622         this.renderEvents();
17623         
17624         if(this.calevents.length && this.loadMask){
17625             this.maskEl.hide();
17626         }
17627     },
17628     
17629     onBeforeLoad: function()
17630     {
17631         this.clearEvents();
17632         if(this.loadMask){
17633             this.maskEl.show();
17634         }
17635     }
17636 });
17637
17638  
17639  /*
17640  * - LGPL
17641  *
17642  * element
17643  * 
17644  */
17645
17646 /**
17647  * @class Roo.bootstrap.Popover
17648  * @extends Roo.bootstrap.Component
17649  * Bootstrap Popover class
17650  * @cfg {String} html contents of the popover   (or false to use children..)
17651  * @cfg {String} title of popover (or false to hide)
17652  * @cfg {String} placement how it is placed
17653  * @cfg {String} trigger click || hover (or false to trigger manually)
17654  * @cfg {String} over what (parent or false to trigger manually.)
17655  * @cfg {Number} delay - delay before showing
17656  
17657  * @constructor
17658  * Create a new Popover
17659  * @param {Object} config The config object
17660  */
17661
17662 Roo.bootstrap.Popover = function(config){
17663     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17664     
17665     this.addEvents({
17666         // raw events
17667          /**
17668          * @event show
17669          * After the popover show
17670          * 
17671          * @param {Roo.bootstrap.Popover} this
17672          */
17673         "show" : true,
17674         /**
17675          * @event hide
17676          * After the popover hide
17677          * 
17678          * @param {Roo.bootstrap.Popover} this
17679          */
17680         "hide" : true
17681     });
17682 };
17683
17684 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17685     
17686     title: 'Fill in a title',
17687     html: false,
17688     
17689     placement : 'right',
17690     trigger : 'hover', // hover
17691     
17692     delay : 0,
17693     
17694     over: 'parent',
17695     
17696     can_build_overlaid : false,
17697     
17698     getChildContainer : function()
17699     {
17700         return this.el.select('.popover-content',true).first();
17701     },
17702     
17703     getAutoCreate : function(){
17704          
17705         var cfg = {
17706            cls : 'popover roo-dynamic',
17707            style: 'display:block',
17708            cn : [
17709                 {
17710                     cls : 'arrow'
17711                 },
17712                 {
17713                     cls : 'popover-inner',
17714                     cn : [
17715                         {
17716                             tag: 'h3',
17717                             cls: 'popover-title popover-header',
17718                             html : this.title
17719                         },
17720                         {
17721                             cls : 'popover-content popover-body',
17722                             html : this.html
17723                         }
17724                     ]
17725                     
17726                 }
17727            ]
17728         };
17729         
17730         return cfg;
17731     },
17732     setTitle: function(str)
17733     {
17734         this.title = str;
17735         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17736     },
17737     setContent: function(str)
17738     {
17739         this.html = str;
17740         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17741     },
17742     // as it get's added to the bottom of the page.
17743     onRender : function(ct, position)
17744     {
17745         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17746         if(!this.el){
17747             var cfg = Roo.apply({},  this.getAutoCreate());
17748             cfg.id = Roo.id();
17749             
17750             if (this.cls) {
17751                 cfg.cls += ' ' + this.cls;
17752             }
17753             if (this.style) {
17754                 cfg.style = this.style;
17755             }
17756             //Roo.log("adding to ");
17757             this.el = Roo.get(document.body).createChild(cfg, position);
17758 //            Roo.log(this.el);
17759         }
17760         this.initEvents();
17761     },
17762     
17763     initEvents : function()
17764     {
17765         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17766         this.el.enableDisplayMode('block');
17767         this.el.hide();
17768         if (this.over === false) {
17769             return; 
17770         }
17771         if (this.triggers === false) {
17772             return;
17773         }
17774         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17775         var triggers = this.trigger ? this.trigger.split(' ') : [];
17776         Roo.each(triggers, function(trigger) {
17777         
17778             if (trigger == 'click') {
17779                 on_el.on('click', this.toggle, this);
17780             } else if (trigger != 'manual') {
17781                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17782                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17783       
17784                 on_el.on(eventIn  ,this.enter, this);
17785                 on_el.on(eventOut, this.leave, this);
17786             }
17787         }, this);
17788         
17789     },
17790     
17791     
17792     // private
17793     timeout : null,
17794     hoverState : null,
17795     
17796     toggle : function () {
17797         this.hoverState == 'in' ? this.leave() : this.enter();
17798     },
17799     
17800     enter : function () {
17801         
17802         clearTimeout(this.timeout);
17803     
17804         this.hoverState = 'in';
17805     
17806         if (!this.delay || !this.delay.show) {
17807             this.show();
17808             return;
17809         }
17810         var _t = this;
17811         this.timeout = setTimeout(function () {
17812             if (_t.hoverState == 'in') {
17813                 _t.show();
17814             }
17815         }, this.delay.show)
17816     },
17817     
17818     leave : function() {
17819         clearTimeout(this.timeout);
17820     
17821         this.hoverState = 'out';
17822     
17823         if (!this.delay || !this.delay.hide) {
17824             this.hide();
17825             return;
17826         }
17827         var _t = this;
17828         this.timeout = setTimeout(function () {
17829             if (_t.hoverState == 'out') {
17830                 _t.hide();
17831             }
17832         }, this.delay.hide)
17833     },
17834     
17835     show : function (on_el)
17836     {
17837         if (!on_el) {
17838             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17839         }
17840         
17841         // set content.
17842         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17843         if (this.html !== false) {
17844             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17845         }
17846         this.el.removeClass([
17847             'fade','top','bottom', 'left', 'right','in',
17848             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17849         ]);
17850         if (!this.title.length) {
17851             this.el.select('.popover-title',true).hide();
17852         }
17853         
17854         var placement = typeof this.placement == 'function' ?
17855             this.placement.call(this, this.el, on_el) :
17856             this.placement;
17857             
17858         var autoToken = /\s?auto?\s?/i;
17859         var autoPlace = autoToken.test(placement);
17860         if (autoPlace) {
17861             placement = placement.replace(autoToken, '') || 'top';
17862         }
17863         
17864         //this.el.detach()
17865         //this.el.setXY([0,0]);
17866         this.el.show();
17867         this.el.dom.style.display='block';
17868         this.el.addClass(placement);
17869         
17870         //this.el.appendTo(on_el);
17871         
17872         var p = this.getPosition();
17873         var box = this.el.getBox();
17874         
17875         if (autoPlace) {
17876             // fixme..
17877         }
17878         var align = Roo.bootstrap.Popover.alignment[placement];
17879         
17880 //        Roo.log(align);
17881         this.el.alignTo(on_el, align[0],align[1]);
17882         //var arrow = this.el.select('.arrow',true).first();
17883         //arrow.set(align[2], 
17884         
17885         this.el.addClass('in');
17886         
17887         
17888         if (this.el.hasClass('fade')) {
17889             // fade it?
17890         }
17891         
17892         this.hoverState = 'in';
17893         
17894         this.fireEvent('show', this);
17895         
17896     },
17897     hide : function()
17898     {
17899         this.el.setXY([0,0]);
17900         this.el.removeClass('in');
17901         this.el.hide();
17902         this.hoverState = null;
17903         
17904         this.fireEvent('hide', this);
17905     }
17906     
17907 });
17908
17909 Roo.bootstrap.Popover.alignment = {
17910     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17911     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17912     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17913     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17914 };
17915
17916  /*
17917  * - LGPL
17918  *
17919  * Progress
17920  * 
17921  */
17922
17923 /**
17924  * @class Roo.bootstrap.Progress
17925  * @extends Roo.bootstrap.Component
17926  * Bootstrap Progress class
17927  * @cfg {Boolean} striped striped of the progress bar
17928  * @cfg {Boolean} active animated of the progress bar
17929  * 
17930  * 
17931  * @constructor
17932  * Create a new Progress
17933  * @param {Object} config The config object
17934  */
17935
17936 Roo.bootstrap.Progress = function(config){
17937     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17938 };
17939
17940 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17941     
17942     striped : false,
17943     active: false,
17944     
17945     getAutoCreate : function(){
17946         var cfg = {
17947             tag: 'div',
17948             cls: 'progress'
17949         };
17950         
17951         
17952         if(this.striped){
17953             cfg.cls += ' progress-striped';
17954         }
17955       
17956         if(this.active){
17957             cfg.cls += ' active';
17958         }
17959         
17960         
17961         return cfg;
17962     }
17963    
17964 });
17965
17966  
17967
17968  /*
17969  * - LGPL
17970  *
17971  * ProgressBar
17972  * 
17973  */
17974
17975 /**
17976  * @class Roo.bootstrap.ProgressBar
17977  * @extends Roo.bootstrap.Component
17978  * Bootstrap ProgressBar class
17979  * @cfg {Number} aria_valuenow aria-value now
17980  * @cfg {Number} aria_valuemin aria-value min
17981  * @cfg {Number} aria_valuemax aria-value max
17982  * @cfg {String} label label for the progress bar
17983  * @cfg {String} panel (success | info | warning | danger )
17984  * @cfg {String} role role of the progress bar
17985  * @cfg {String} sr_only text
17986  * 
17987  * 
17988  * @constructor
17989  * Create a new ProgressBar
17990  * @param {Object} config The config object
17991  */
17992
17993 Roo.bootstrap.ProgressBar = function(config){
17994     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17995 };
17996
17997 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17998     
17999     aria_valuenow : 0,
18000     aria_valuemin : 0,
18001     aria_valuemax : 100,
18002     label : false,
18003     panel : false,
18004     role : false,
18005     sr_only: false,
18006     
18007     getAutoCreate : function()
18008     {
18009         
18010         var cfg = {
18011             tag: 'div',
18012             cls: 'progress-bar',
18013             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18014         };
18015         
18016         if(this.sr_only){
18017             cfg.cn = {
18018                 tag: 'span',
18019                 cls: 'sr-only',
18020                 html: this.sr_only
18021             }
18022         }
18023         
18024         if(this.role){
18025             cfg.role = this.role;
18026         }
18027         
18028         if(this.aria_valuenow){
18029             cfg['aria-valuenow'] = this.aria_valuenow;
18030         }
18031         
18032         if(this.aria_valuemin){
18033             cfg['aria-valuemin'] = this.aria_valuemin;
18034         }
18035         
18036         if(this.aria_valuemax){
18037             cfg['aria-valuemax'] = this.aria_valuemax;
18038         }
18039         
18040         if(this.label && !this.sr_only){
18041             cfg.html = this.label;
18042         }
18043         
18044         if(this.panel){
18045             cfg.cls += ' progress-bar-' + this.panel;
18046         }
18047         
18048         return cfg;
18049     },
18050     
18051     update : function(aria_valuenow)
18052     {
18053         this.aria_valuenow = aria_valuenow;
18054         
18055         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18056     }
18057    
18058 });
18059
18060  
18061
18062  /*
18063  * - LGPL
18064  *
18065  * column
18066  * 
18067  */
18068
18069 /**
18070  * @class Roo.bootstrap.TabGroup
18071  * @extends Roo.bootstrap.Column
18072  * Bootstrap Column class
18073  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18074  * @cfg {Boolean} carousel true to make the group behave like a carousel
18075  * @cfg {Boolean} bullets show bullets for the panels
18076  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18077  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18078  * @cfg {Boolean} showarrow (true|false) show arrow default true
18079  * 
18080  * @constructor
18081  * Create a new TabGroup
18082  * @param {Object} config The config object
18083  */
18084
18085 Roo.bootstrap.TabGroup = function(config){
18086     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18087     if (!this.navId) {
18088         this.navId = Roo.id();
18089     }
18090     this.tabs = [];
18091     Roo.bootstrap.TabGroup.register(this);
18092     
18093 };
18094
18095 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18096     
18097     carousel : false,
18098     transition : false,
18099     bullets : 0,
18100     timer : 0,
18101     autoslide : false,
18102     slideFn : false,
18103     slideOnTouch : false,
18104     showarrow : true,
18105     
18106     getAutoCreate : function()
18107     {
18108         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18109         
18110         cfg.cls += ' tab-content';
18111         
18112         if (this.carousel) {
18113             cfg.cls += ' carousel slide';
18114             
18115             cfg.cn = [{
18116                cls : 'carousel-inner',
18117                cn : []
18118             }];
18119         
18120             if(this.bullets  && !Roo.isTouch){
18121                 
18122                 var bullets = {
18123                     cls : 'carousel-bullets',
18124                     cn : []
18125                 };
18126                
18127                 if(this.bullets_cls){
18128                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18129                 }
18130                 
18131                 bullets.cn.push({
18132                     cls : 'clear'
18133                 });
18134                 
18135                 cfg.cn[0].cn.push(bullets);
18136             }
18137             
18138             if(this.showarrow){
18139                 cfg.cn[0].cn.push({
18140                     tag : 'div',
18141                     class : 'carousel-arrow',
18142                     cn : [
18143                         {
18144                             tag : 'div',
18145                             class : 'carousel-prev',
18146                             cn : [
18147                                 {
18148                                     tag : 'i',
18149                                     class : 'fa fa-chevron-left'
18150                                 }
18151                             ]
18152                         },
18153                         {
18154                             tag : 'div',
18155                             class : 'carousel-next',
18156                             cn : [
18157                                 {
18158                                     tag : 'i',
18159                                     class : 'fa fa-chevron-right'
18160                                 }
18161                             ]
18162                         }
18163                     ]
18164                 });
18165             }
18166             
18167         }
18168         
18169         return cfg;
18170     },
18171     
18172     initEvents:  function()
18173     {
18174 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18175 //            this.el.on("touchstart", this.onTouchStart, this);
18176 //        }
18177         
18178         if(this.autoslide){
18179             var _this = this;
18180             
18181             this.slideFn = window.setInterval(function() {
18182                 _this.showPanelNext();
18183             }, this.timer);
18184         }
18185         
18186         if(this.showarrow){
18187             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18188             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18189         }
18190         
18191         
18192     },
18193     
18194 //    onTouchStart : function(e, el, o)
18195 //    {
18196 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18197 //            return;
18198 //        }
18199 //        
18200 //        this.showPanelNext();
18201 //    },
18202     
18203     
18204     getChildContainer : function()
18205     {
18206         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18207     },
18208     
18209     /**
18210     * register a Navigation item
18211     * @param {Roo.bootstrap.NavItem} the navitem to add
18212     */
18213     register : function(item)
18214     {
18215         this.tabs.push( item);
18216         item.navId = this.navId; // not really needed..
18217         this.addBullet();
18218     
18219     },
18220     
18221     getActivePanel : function()
18222     {
18223         var r = false;
18224         Roo.each(this.tabs, function(t) {
18225             if (t.active) {
18226                 r = t;
18227                 return false;
18228             }
18229             return null;
18230         });
18231         return r;
18232         
18233     },
18234     getPanelByName : function(n)
18235     {
18236         var r = false;
18237         Roo.each(this.tabs, function(t) {
18238             if (t.tabId == n) {
18239                 r = t;
18240                 return false;
18241             }
18242             return null;
18243         });
18244         return r;
18245     },
18246     indexOfPanel : function(p)
18247     {
18248         var r = false;
18249         Roo.each(this.tabs, function(t,i) {
18250             if (t.tabId == p.tabId) {
18251                 r = i;
18252                 return false;
18253             }
18254             return null;
18255         });
18256         return r;
18257     },
18258     /**
18259      * show a specific panel
18260      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18261      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18262      */
18263     showPanel : function (pan)
18264     {
18265         if(this.transition || typeof(pan) == 'undefined'){
18266             Roo.log("waiting for the transitionend");
18267             return;
18268         }
18269         
18270         if (typeof(pan) == 'number') {
18271             pan = this.tabs[pan];
18272         }
18273         
18274         if (typeof(pan) == 'string') {
18275             pan = this.getPanelByName(pan);
18276         }
18277         
18278         var cur = this.getActivePanel();
18279         
18280         if(!pan || !cur){
18281             Roo.log('pan or acitve pan is undefined');
18282             return false;
18283         }
18284         
18285         if (pan.tabId == this.getActivePanel().tabId) {
18286             return true;
18287         }
18288         
18289         if (false === cur.fireEvent('beforedeactivate')) {
18290             return false;
18291         }
18292         
18293         if(this.bullets > 0 && !Roo.isTouch){
18294             this.setActiveBullet(this.indexOfPanel(pan));
18295         }
18296         
18297         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18298             
18299             this.transition = true;
18300             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18301             var lr = dir == 'next' ? 'left' : 'right';
18302             pan.el.addClass(dir); // or prev
18303             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18304             cur.el.addClass(lr); // or right
18305             pan.el.addClass(lr);
18306             
18307             var _this = this;
18308             cur.el.on('transitionend', function() {
18309                 Roo.log("trans end?");
18310                 
18311                 pan.el.removeClass([lr,dir]);
18312                 pan.setActive(true);
18313                 
18314                 cur.el.removeClass([lr]);
18315                 cur.setActive(false);
18316                 
18317                 _this.transition = false;
18318                 
18319             }, this, { single:  true } );
18320             
18321             return true;
18322         }
18323         
18324         cur.setActive(false);
18325         pan.setActive(true);
18326         
18327         return true;
18328         
18329     },
18330     showPanelNext : function()
18331     {
18332         var i = this.indexOfPanel(this.getActivePanel());
18333         
18334         if (i >= this.tabs.length - 1 && !this.autoslide) {
18335             return;
18336         }
18337         
18338         if (i >= this.tabs.length - 1 && this.autoslide) {
18339             i = -1;
18340         }
18341         
18342         this.showPanel(this.tabs[i+1]);
18343     },
18344     
18345     showPanelPrev : function()
18346     {
18347         var i = this.indexOfPanel(this.getActivePanel());
18348         
18349         if (i  < 1 && !this.autoslide) {
18350             return;
18351         }
18352         
18353         if (i < 1 && this.autoslide) {
18354             i = this.tabs.length;
18355         }
18356         
18357         this.showPanel(this.tabs[i-1]);
18358     },
18359     
18360     
18361     addBullet: function()
18362     {
18363         if(!this.bullets || Roo.isTouch){
18364             return;
18365         }
18366         var ctr = this.el.select('.carousel-bullets',true).first();
18367         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18368         var bullet = ctr.createChild({
18369             cls : 'bullet bullet-' + i
18370         },ctr.dom.lastChild);
18371         
18372         
18373         var _this = this;
18374         
18375         bullet.on('click', (function(e, el, o, ii, t){
18376
18377             e.preventDefault();
18378
18379             this.showPanel(ii);
18380
18381             if(this.autoslide && this.slideFn){
18382                 clearInterval(this.slideFn);
18383                 this.slideFn = window.setInterval(function() {
18384                     _this.showPanelNext();
18385                 }, this.timer);
18386             }
18387
18388         }).createDelegate(this, [i, bullet], true));
18389                 
18390         
18391     },
18392      
18393     setActiveBullet : function(i)
18394     {
18395         if(Roo.isTouch){
18396             return;
18397         }
18398         
18399         Roo.each(this.el.select('.bullet', true).elements, function(el){
18400             el.removeClass('selected');
18401         });
18402
18403         var bullet = this.el.select('.bullet-' + i, true).first();
18404         
18405         if(!bullet){
18406             return;
18407         }
18408         
18409         bullet.addClass('selected');
18410     }
18411     
18412     
18413   
18414 });
18415
18416  
18417
18418  
18419  
18420 Roo.apply(Roo.bootstrap.TabGroup, {
18421     
18422     groups: {},
18423      /**
18424     * register a Navigation Group
18425     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18426     */
18427     register : function(navgrp)
18428     {
18429         this.groups[navgrp.navId] = navgrp;
18430         
18431     },
18432     /**
18433     * fetch a Navigation Group based on the navigation ID
18434     * if one does not exist , it will get created.
18435     * @param {string} the navgroup to add
18436     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18437     */
18438     get: function(navId) {
18439         if (typeof(this.groups[navId]) == 'undefined') {
18440             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18441         }
18442         return this.groups[navId] ;
18443     }
18444     
18445     
18446     
18447 });
18448
18449  /*
18450  * - LGPL
18451  *
18452  * TabPanel
18453  * 
18454  */
18455
18456 /**
18457  * @class Roo.bootstrap.TabPanel
18458  * @extends Roo.bootstrap.Component
18459  * Bootstrap TabPanel class
18460  * @cfg {Boolean} active panel active
18461  * @cfg {String} html panel content
18462  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18463  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18464  * @cfg {String} href click to link..
18465  * 
18466  * 
18467  * @constructor
18468  * Create a new TabPanel
18469  * @param {Object} config The config object
18470  */
18471
18472 Roo.bootstrap.TabPanel = function(config){
18473     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18474     this.addEvents({
18475         /**
18476              * @event changed
18477              * Fires when the active status changes
18478              * @param {Roo.bootstrap.TabPanel} this
18479              * @param {Boolean} state the new state
18480             
18481          */
18482         'changed': true,
18483         /**
18484              * @event beforedeactivate
18485              * Fires before a tab is de-activated - can be used to do validation on a form.
18486              * @param {Roo.bootstrap.TabPanel} this
18487              * @return {Boolean} false if there is an error
18488             
18489          */
18490         'beforedeactivate': true
18491      });
18492     
18493     this.tabId = this.tabId || Roo.id();
18494   
18495 };
18496
18497 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18498     
18499     active: false,
18500     html: false,
18501     tabId: false,
18502     navId : false,
18503     href : '',
18504     
18505     getAutoCreate : function(){
18506         var cfg = {
18507             tag: 'div',
18508             // item is needed for carousel - not sure if it has any effect otherwise
18509             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18510             html: this.html || ''
18511         };
18512         
18513         if(this.active){
18514             cfg.cls += ' active';
18515         }
18516         
18517         if(this.tabId){
18518             cfg.tabId = this.tabId;
18519         }
18520         
18521         
18522         return cfg;
18523     },
18524     
18525     initEvents:  function()
18526     {
18527         var p = this.parent();
18528         
18529         this.navId = this.navId || p.navId;
18530         
18531         if (typeof(this.navId) != 'undefined') {
18532             // not really needed.. but just in case.. parent should be a NavGroup.
18533             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18534             
18535             tg.register(this);
18536             
18537             var i = tg.tabs.length - 1;
18538             
18539             if(this.active && tg.bullets > 0 && i < tg.bullets){
18540                 tg.setActiveBullet(i);
18541             }
18542         }
18543         
18544         this.el.on('click', this.onClick, this);
18545         
18546         if(Roo.isTouch){
18547             this.el.on("touchstart", this.onTouchStart, this);
18548             this.el.on("touchmove", this.onTouchMove, this);
18549             this.el.on("touchend", this.onTouchEnd, this);
18550         }
18551         
18552     },
18553     
18554     onRender : function(ct, position)
18555     {
18556         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18557     },
18558     
18559     setActive : function(state)
18560     {
18561         Roo.log("panel - set active " + this.tabId + "=" + state);
18562         
18563         this.active = state;
18564         if (!state) {
18565             this.el.removeClass('active');
18566             
18567         } else  if (!this.el.hasClass('active')) {
18568             this.el.addClass('active');
18569         }
18570         
18571         this.fireEvent('changed', this, state);
18572     },
18573     
18574     onClick : function(e)
18575     {
18576         e.preventDefault();
18577         
18578         if(!this.href.length){
18579             return;
18580         }
18581         
18582         window.location.href = this.href;
18583     },
18584     
18585     startX : 0,
18586     startY : 0,
18587     endX : 0,
18588     endY : 0,
18589     swiping : false,
18590     
18591     onTouchStart : function(e)
18592     {
18593         this.swiping = false;
18594         
18595         this.startX = e.browserEvent.touches[0].clientX;
18596         this.startY = e.browserEvent.touches[0].clientY;
18597     },
18598     
18599     onTouchMove : function(e)
18600     {
18601         this.swiping = true;
18602         
18603         this.endX = e.browserEvent.touches[0].clientX;
18604         this.endY = e.browserEvent.touches[0].clientY;
18605     },
18606     
18607     onTouchEnd : function(e)
18608     {
18609         if(!this.swiping){
18610             this.onClick(e);
18611             return;
18612         }
18613         
18614         var tabGroup = this.parent();
18615         
18616         if(this.endX > this.startX){ // swiping right
18617             tabGroup.showPanelPrev();
18618             return;
18619         }
18620         
18621         if(this.startX > this.endX){ // swiping left
18622             tabGroup.showPanelNext();
18623             return;
18624         }
18625     }
18626     
18627     
18628 });
18629  
18630
18631  
18632
18633  /*
18634  * - LGPL
18635  *
18636  * DateField
18637  * 
18638  */
18639
18640 /**
18641  * @class Roo.bootstrap.DateField
18642  * @extends Roo.bootstrap.Input
18643  * Bootstrap DateField class
18644  * @cfg {Number} weekStart default 0
18645  * @cfg {String} viewMode default empty, (months|years)
18646  * @cfg {String} minViewMode default empty, (months|years)
18647  * @cfg {Number} startDate default -Infinity
18648  * @cfg {Number} endDate default Infinity
18649  * @cfg {Boolean} todayHighlight default false
18650  * @cfg {Boolean} todayBtn default false
18651  * @cfg {Boolean} calendarWeeks default false
18652  * @cfg {Object} daysOfWeekDisabled default empty
18653  * @cfg {Boolean} singleMode default false (true | false)
18654  * 
18655  * @cfg {Boolean} keyboardNavigation default true
18656  * @cfg {String} language default en
18657  * 
18658  * @constructor
18659  * Create a new DateField
18660  * @param {Object} config The config object
18661  */
18662
18663 Roo.bootstrap.DateField = function(config){
18664     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18665      this.addEvents({
18666             /**
18667              * @event show
18668              * Fires when this field show.
18669              * @param {Roo.bootstrap.DateField} this
18670              * @param {Mixed} date The date value
18671              */
18672             show : true,
18673             /**
18674              * @event show
18675              * Fires when this field hide.
18676              * @param {Roo.bootstrap.DateField} this
18677              * @param {Mixed} date The date value
18678              */
18679             hide : true,
18680             /**
18681              * @event select
18682              * Fires when select a date.
18683              * @param {Roo.bootstrap.DateField} this
18684              * @param {Mixed} date The date value
18685              */
18686             select : true,
18687             /**
18688              * @event beforeselect
18689              * Fires when before select a date.
18690              * @param {Roo.bootstrap.DateField} this
18691              * @param {Mixed} date The date value
18692              */
18693             beforeselect : true
18694         });
18695 };
18696
18697 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18698     
18699     /**
18700      * @cfg {String} format
18701      * The default date format string which can be overriden for localization support.  The format must be
18702      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18703      */
18704     format : "m/d/y",
18705     /**
18706      * @cfg {String} altFormats
18707      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18708      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18709      */
18710     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18711     
18712     weekStart : 0,
18713     
18714     viewMode : '',
18715     
18716     minViewMode : '',
18717     
18718     todayHighlight : false,
18719     
18720     todayBtn: false,
18721     
18722     language: 'en',
18723     
18724     keyboardNavigation: true,
18725     
18726     calendarWeeks: false,
18727     
18728     startDate: -Infinity,
18729     
18730     endDate: Infinity,
18731     
18732     daysOfWeekDisabled: [],
18733     
18734     _events: [],
18735     
18736     singleMode : false,
18737     
18738     UTCDate: function()
18739     {
18740         return new Date(Date.UTC.apply(Date, arguments));
18741     },
18742     
18743     UTCToday: function()
18744     {
18745         var today = new Date();
18746         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18747     },
18748     
18749     getDate: function() {
18750             var d = this.getUTCDate();
18751             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18752     },
18753     
18754     getUTCDate: function() {
18755             return this.date;
18756     },
18757     
18758     setDate: function(d) {
18759             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18760     },
18761     
18762     setUTCDate: function(d) {
18763             this.date = d;
18764             this.setValue(this.formatDate(this.date));
18765     },
18766         
18767     onRender: function(ct, position)
18768     {
18769         
18770         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18771         
18772         this.language = this.language || 'en';
18773         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18774         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18775         
18776         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18777         this.format = this.format || 'm/d/y';
18778         this.isInline = false;
18779         this.isInput = true;
18780         this.component = this.el.select('.add-on', true).first() || false;
18781         this.component = (this.component && this.component.length === 0) ? false : this.component;
18782         this.hasInput = this.component && this.inputEl().length;
18783         
18784         if (typeof(this.minViewMode === 'string')) {
18785             switch (this.minViewMode) {
18786                 case 'months':
18787                     this.minViewMode = 1;
18788                     break;
18789                 case 'years':
18790                     this.minViewMode = 2;
18791                     break;
18792                 default:
18793                     this.minViewMode = 0;
18794                     break;
18795             }
18796         }
18797         
18798         if (typeof(this.viewMode === 'string')) {
18799             switch (this.viewMode) {
18800                 case 'months':
18801                     this.viewMode = 1;
18802                     break;
18803                 case 'years':
18804                     this.viewMode = 2;
18805                     break;
18806                 default:
18807                     this.viewMode = 0;
18808                     break;
18809             }
18810         }
18811                 
18812         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18813         
18814 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18815         
18816         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18817         
18818         this.picker().on('mousedown', this.onMousedown, this);
18819         this.picker().on('click', this.onClick, this);
18820         
18821         this.picker().addClass('datepicker-dropdown');
18822         
18823         this.startViewMode = this.viewMode;
18824         
18825         if(this.singleMode){
18826             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18827                 v.setVisibilityMode(Roo.Element.DISPLAY);
18828                 v.hide();
18829             });
18830             
18831             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18832                 v.setStyle('width', '189px');
18833             });
18834         }
18835         
18836         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18837             if(!this.calendarWeeks){
18838                 v.remove();
18839                 return;
18840             }
18841             
18842             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18843             v.attr('colspan', function(i, val){
18844                 return parseInt(val) + 1;
18845             });
18846         });
18847                         
18848         
18849         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18850         
18851         this.setStartDate(this.startDate);
18852         this.setEndDate(this.endDate);
18853         
18854         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18855         
18856         this.fillDow();
18857         this.fillMonths();
18858         this.update();
18859         this.showMode();
18860         
18861         if(this.isInline) {
18862             this.showPopup();
18863         }
18864     },
18865     
18866     picker : function()
18867     {
18868         return this.pickerEl;
18869 //        return this.el.select('.datepicker', true).first();
18870     },
18871     
18872     fillDow: function()
18873     {
18874         var dowCnt = this.weekStart;
18875         
18876         var dow = {
18877             tag: 'tr',
18878             cn: [
18879                 
18880             ]
18881         };
18882         
18883         if(this.calendarWeeks){
18884             dow.cn.push({
18885                 tag: 'th',
18886                 cls: 'cw',
18887                 html: '&nbsp;'
18888             })
18889         }
18890         
18891         while (dowCnt < this.weekStart + 7) {
18892             dow.cn.push({
18893                 tag: 'th',
18894                 cls: 'dow',
18895                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18896             });
18897         }
18898         
18899         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18900     },
18901     
18902     fillMonths: function()
18903     {    
18904         var i = 0;
18905         var months = this.picker().select('>.datepicker-months td', true).first();
18906         
18907         months.dom.innerHTML = '';
18908         
18909         while (i < 12) {
18910             var month = {
18911                 tag: 'span',
18912                 cls: 'month',
18913                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18914             };
18915             
18916             months.createChild(month);
18917         }
18918         
18919     },
18920     
18921     update: function()
18922     {
18923         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;
18924         
18925         if (this.date < this.startDate) {
18926             this.viewDate = new Date(this.startDate);
18927         } else if (this.date > this.endDate) {
18928             this.viewDate = new Date(this.endDate);
18929         } else {
18930             this.viewDate = new Date(this.date);
18931         }
18932         
18933         this.fill();
18934     },
18935     
18936     fill: function() 
18937     {
18938         var d = new Date(this.viewDate),
18939                 year = d.getUTCFullYear(),
18940                 month = d.getUTCMonth(),
18941                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18942                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18943                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18944                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18945                 currentDate = this.date && this.date.valueOf(),
18946                 today = this.UTCToday();
18947         
18948         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18949         
18950 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18951         
18952 //        this.picker.select('>tfoot th.today').
18953 //                                              .text(dates[this.language].today)
18954 //                                              .toggle(this.todayBtn !== false);
18955     
18956         this.updateNavArrows();
18957         this.fillMonths();
18958                                                 
18959         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18960         
18961         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18962          
18963         prevMonth.setUTCDate(day);
18964         
18965         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18966         
18967         var nextMonth = new Date(prevMonth);
18968         
18969         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18970         
18971         nextMonth = nextMonth.valueOf();
18972         
18973         var fillMonths = false;
18974         
18975         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18976         
18977         while(prevMonth.valueOf() <= nextMonth) {
18978             var clsName = '';
18979             
18980             if (prevMonth.getUTCDay() === this.weekStart) {
18981                 if(fillMonths){
18982                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18983                 }
18984                     
18985                 fillMonths = {
18986                     tag: 'tr',
18987                     cn: []
18988                 };
18989                 
18990                 if(this.calendarWeeks){
18991                     // ISO 8601: First week contains first thursday.
18992                     // ISO also states week starts on Monday, but we can be more abstract here.
18993                     var
18994                     // Start of current week: based on weekstart/current date
18995                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18996                     // Thursday of this week
18997                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18998                     // First Thursday of year, year from thursday
18999                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19000                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19001                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19002                     
19003                     fillMonths.cn.push({
19004                         tag: 'td',
19005                         cls: 'cw',
19006                         html: calWeek
19007                     });
19008                 }
19009             }
19010             
19011             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19012                 clsName += ' old';
19013             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19014                 clsName += ' new';
19015             }
19016             if (this.todayHighlight &&
19017                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19018                 prevMonth.getUTCMonth() == today.getMonth() &&
19019                 prevMonth.getUTCDate() == today.getDate()) {
19020                 clsName += ' today';
19021             }
19022             
19023             if (currentDate && prevMonth.valueOf() === currentDate) {
19024                 clsName += ' active';
19025             }
19026             
19027             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19028                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19029                     clsName += ' disabled';
19030             }
19031             
19032             fillMonths.cn.push({
19033                 tag: 'td',
19034                 cls: 'day ' + clsName,
19035                 html: prevMonth.getDate()
19036             });
19037             
19038             prevMonth.setDate(prevMonth.getDate()+1);
19039         }
19040           
19041         var currentYear = this.date && this.date.getUTCFullYear();
19042         var currentMonth = this.date && this.date.getUTCMonth();
19043         
19044         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19045         
19046         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19047             v.removeClass('active');
19048             
19049             if(currentYear === year && k === currentMonth){
19050                 v.addClass('active');
19051             }
19052             
19053             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19054                 v.addClass('disabled');
19055             }
19056             
19057         });
19058         
19059         
19060         year = parseInt(year/10, 10) * 10;
19061         
19062         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19063         
19064         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19065         
19066         year -= 1;
19067         for (var i = -1; i < 11; i++) {
19068             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19069                 tag: 'span',
19070                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19071                 html: year
19072             });
19073             
19074             year += 1;
19075         }
19076     },
19077     
19078     showMode: function(dir) 
19079     {
19080         if (dir) {
19081             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19082         }
19083         
19084         Roo.each(this.picker().select('>div',true).elements, function(v){
19085             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19086             v.hide();
19087         });
19088         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19089     },
19090     
19091     place: function()
19092     {
19093         if(this.isInline) {
19094             return;
19095         }
19096         
19097         this.picker().removeClass(['bottom', 'top']);
19098         
19099         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19100             /*
19101              * place to the top of element!
19102              *
19103              */
19104             
19105             this.picker().addClass('top');
19106             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19107             
19108             return;
19109         }
19110         
19111         this.picker().addClass('bottom');
19112         
19113         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19114     },
19115     
19116     parseDate : function(value)
19117     {
19118         if(!value || value instanceof Date){
19119             return value;
19120         }
19121         var v = Date.parseDate(value, this.format);
19122         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19123             v = Date.parseDate(value, 'Y-m-d');
19124         }
19125         if(!v && this.altFormats){
19126             if(!this.altFormatsArray){
19127                 this.altFormatsArray = this.altFormats.split("|");
19128             }
19129             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19130                 v = Date.parseDate(value, this.altFormatsArray[i]);
19131             }
19132         }
19133         return v;
19134     },
19135     
19136     formatDate : function(date, fmt)
19137     {   
19138         return (!date || !(date instanceof Date)) ?
19139         date : date.dateFormat(fmt || this.format);
19140     },
19141     
19142     onFocus : function()
19143     {
19144         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19145         this.showPopup();
19146     },
19147     
19148     onBlur : function()
19149     {
19150         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19151         
19152         var d = this.inputEl().getValue();
19153         
19154         this.setValue(d);
19155                 
19156         this.hidePopup();
19157     },
19158     
19159     showPopup : function()
19160     {
19161         this.picker().show();
19162         this.update();
19163         this.place();
19164         
19165         this.fireEvent('showpopup', this, this.date);
19166     },
19167     
19168     hidePopup : function()
19169     {
19170         if(this.isInline) {
19171             return;
19172         }
19173         this.picker().hide();
19174         this.viewMode = this.startViewMode;
19175         this.showMode();
19176         
19177         this.fireEvent('hidepopup', this, this.date);
19178         
19179     },
19180     
19181     onMousedown: function(e)
19182     {
19183         e.stopPropagation();
19184         e.preventDefault();
19185     },
19186     
19187     keyup: function(e)
19188     {
19189         Roo.bootstrap.DateField.superclass.keyup.call(this);
19190         this.update();
19191     },
19192
19193     setValue: function(v)
19194     {
19195         if(this.fireEvent('beforeselect', this, v) !== false){
19196             var d = new Date(this.parseDate(v) ).clearTime();
19197         
19198             if(isNaN(d.getTime())){
19199                 this.date = this.viewDate = '';
19200                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19201                 return;
19202             }
19203
19204             v = this.formatDate(d);
19205
19206             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19207
19208             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19209
19210             this.update();
19211
19212             this.fireEvent('select', this, this.date);
19213         }
19214     },
19215     
19216     getValue: function()
19217     {
19218         return this.formatDate(this.date);
19219     },
19220     
19221     fireKey: function(e)
19222     {
19223         if (!this.picker().isVisible()){
19224             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19225                 this.showPopup();
19226             }
19227             return;
19228         }
19229         
19230         var dateChanged = false,
19231         dir, day, month,
19232         newDate, newViewDate;
19233         
19234         switch(e.keyCode){
19235             case 27: // escape
19236                 this.hidePopup();
19237                 e.preventDefault();
19238                 break;
19239             case 37: // left
19240             case 39: // right
19241                 if (!this.keyboardNavigation) {
19242                     break;
19243                 }
19244                 dir = e.keyCode == 37 ? -1 : 1;
19245                 
19246                 if (e.ctrlKey){
19247                     newDate = this.moveYear(this.date, dir);
19248                     newViewDate = this.moveYear(this.viewDate, dir);
19249                 } else if (e.shiftKey){
19250                     newDate = this.moveMonth(this.date, dir);
19251                     newViewDate = this.moveMonth(this.viewDate, dir);
19252                 } else {
19253                     newDate = new Date(this.date);
19254                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19255                     newViewDate = new Date(this.viewDate);
19256                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19257                 }
19258                 if (this.dateWithinRange(newDate)){
19259                     this.date = newDate;
19260                     this.viewDate = newViewDate;
19261                     this.setValue(this.formatDate(this.date));
19262 //                    this.update();
19263                     e.preventDefault();
19264                     dateChanged = true;
19265                 }
19266                 break;
19267             case 38: // up
19268             case 40: // down
19269                 if (!this.keyboardNavigation) {
19270                     break;
19271                 }
19272                 dir = e.keyCode == 38 ? -1 : 1;
19273                 if (e.ctrlKey){
19274                     newDate = this.moveYear(this.date, dir);
19275                     newViewDate = this.moveYear(this.viewDate, dir);
19276                 } else if (e.shiftKey){
19277                     newDate = this.moveMonth(this.date, dir);
19278                     newViewDate = this.moveMonth(this.viewDate, dir);
19279                 } else {
19280                     newDate = new Date(this.date);
19281                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19282                     newViewDate = new Date(this.viewDate);
19283                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19284                 }
19285                 if (this.dateWithinRange(newDate)){
19286                     this.date = newDate;
19287                     this.viewDate = newViewDate;
19288                     this.setValue(this.formatDate(this.date));
19289 //                    this.update();
19290                     e.preventDefault();
19291                     dateChanged = true;
19292                 }
19293                 break;
19294             case 13: // enter
19295                 this.setValue(this.formatDate(this.date));
19296                 this.hidePopup();
19297                 e.preventDefault();
19298                 break;
19299             case 9: // tab
19300                 this.setValue(this.formatDate(this.date));
19301                 this.hidePopup();
19302                 break;
19303             case 16: // shift
19304             case 17: // ctrl
19305             case 18: // alt
19306                 break;
19307             default :
19308                 this.hidePopup();
19309                 
19310         }
19311     },
19312     
19313     
19314     onClick: function(e) 
19315     {
19316         e.stopPropagation();
19317         e.preventDefault();
19318         
19319         var target = e.getTarget();
19320         
19321         if(target.nodeName.toLowerCase() === 'i'){
19322             target = Roo.get(target).dom.parentNode;
19323         }
19324         
19325         var nodeName = target.nodeName;
19326         var className = target.className;
19327         var html = target.innerHTML;
19328         //Roo.log(nodeName);
19329         
19330         switch(nodeName.toLowerCase()) {
19331             case 'th':
19332                 switch(className) {
19333                     case 'switch':
19334                         this.showMode(1);
19335                         break;
19336                     case 'prev':
19337                     case 'next':
19338                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19339                         switch(this.viewMode){
19340                                 case 0:
19341                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19342                                         break;
19343                                 case 1:
19344                                 case 2:
19345                                         this.viewDate = this.moveYear(this.viewDate, dir);
19346                                         break;
19347                         }
19348                         this.fill();
19349                         break;
19350                     case 'today':
19351                         var date = new Date();
19352                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19353 //                        this.fill()
19354                         this.setValue(this.formatDate(this.date));
19355                         
19356                         this.hidePopup();
19357                         break;
19358                 }
19359                 break;
19360             case 'span':
19361                 if (className.indexOf('disabled') < 0) {
19362                     this.viewDate.setUTCDate(1);
19363                     if (className.indexOf('month') > -1) {
19364                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19365                     } else {
19366                         var year = parseInt(html, 10) || 0;
19367                         this.viewDate.setUTCFullYear(year);
19368                         
19369                     }
19370                     
19371                     if(this.singleMode){
19372                         this.setValue(this.formatDate(this.viewDate));
19373                         this.hidePopup();
19374                         return;
19375                     }
19376                     
19377                     this.showMode(-1);
19378                     this.fill();
19379                 }
19380                 break;
19381                 
19382             case 'td':
19383                 //Roo.log(className);
19384                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19385                     var day = parseInt(html, 10) || 1;
19386                     var year = this.viewDate.getUTCFullYear(),
19387                         month = this.viewDate.getUTCMonth();
19388
19389                     if (className.indexOf('old') > -1) {
19390                         if(month === 0 ){
19391                             month = 11;
19392                             year -= 1;
19393                         }else{
19394                             month -= 1;
19395                         }
19396                     } else if (className.indexOf('new') > -1) {
19397                         if (month == 11) {
19398                             month = 0;
19399                             year += 1;
19400                         } else {
19401                             month += 1;
19402                         }
19403                     }
19404                     //Roo.log([year,month,day]);
19405                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19406                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19407 //                    this.fill();
19408                     //Roo.log(this.formatDate(this.date));
19409                     this.setValue(this.formatDate(this.date));
19410                     this.hidePopup();
19411                 }
19412                 break;
19413         }
19414     },
19415     
19416     setStartDate: function(startDate)
19417     {
19418         this.startDate = startDate || -Infinity;
19419         if (this.startDate !== -Infinity) {
19420             this.startDate = this.parseDate(this.startDate);
19421         }
19422         this.update();
19423         this.updateNavArrows();
19424     },
19425
19426     setEndDate: function(endDate)
19427     {
19428         this.endDate = endDate || Infinity;
19429         if (this.endDate !== Infinity) {
19430             this.endDate = this.parseDate(this.endDate);
19431         }
19432         this.update();
19433         this.updateNavArrows();
19434     },
19435     
19436     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19437     {
19438         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19439         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19440             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19441         }
19442         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19443             return parseInt(d, 10);
19444         });
19445         this.update();
19446         this.updateNavArrows();
19447     },
19448     
19449     updateNavArrows: function() 
19450     {
19451         if(this.singleMode){
19452             return;
19453         }
19454         
19455         var d = new Date(this.viewDate),
19456         year = d.getUTCFullYear(),
19457         month = d.getUTCMonth();
19458         
19459         Roo.each(this.picker().select('.prev', true).elements, function(v){
19460             v.show();
19461             switch (this.viewMode) {
19462                 case 0:
19463
19464                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19465                         v.hide();
19466                     }
19467                     break;
19468                 case 1:
19469                 case 2:
19470                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19471                         v.hide();
19472                     }
19473                     break;
19474             }
19475         });
19476         
19477         Roo.each(this.picker().select('.next', true).elements, function(v){
19478             v.show();
19479             switch (this.viewMode) {
19480                 case 0:
19481
19482                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19483                         v.hide();
19484                     }
19485                     break;
19486                 case 1:
19487                 case 2:
19488                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19489                         v.hide();
19490                     }
19491                     break;
19492             }
19493         })
19494     },
19495     
19496     moveMonth: function(date, dir)
19497     {
19498         if (!dir) {
19499             return date;
19500         }
19501         var new_date = new Date(date.valueOf()),
19502         day = new_date.getUTCDate(),
19503         month = new_date.getUTCMonth(),
19504         mag = Math.abs(dir),
19505         new_month, test;
19506         dir = dir > 0 ? 1 : -1;
19507         if (mag == 1){
19508             test = dir == -1
19509             // If going back one month, make sure month is not current month
19510             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19511             ? function(){
19512                 return new_date.getUTCMonth() == month;
19513             }
19514             // If going forward one month, make sure month is as expected
19515             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19516             : function(){
19517                 return new_date.getUTCMonth() != new_month;
19518             };
19519             new_month = month + dir;
19520             new_date.setUTCMonth(new_month);
19521             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19522             if (new_month < 0 || new_month > 11) {
19523                 new_month = (new_month + 12) % 12;
19524             }
19525         } else {
19526             // For magnitudes >1, move one month at a time...
19527             for (var i=0; i<mag; i++) {
19528                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19529                 new_date = this.moveMonth(new_date, dir);
19530             }
19531             // ...then reset the day, keeping it in the new month
19532             new_month = new_date.getUTCMonth();
19533             new_date.setUTCDate(day);
19534             test = function(){
19535                 return new_month != new_date.getUTCMonth();
19536             };
19537         }
19538         // Common date-resetting loop -- if date is beyond end of month, make it
19539         // end of month
19540         while (test()){
19541             new_date.setUTCDate(--day);
19542             new_date.setUTCMonth(new_month);
19543         }
19544         return new_date;
19545     },
19546
19547     moveYear: function(date, dir)
19548     {
19549         return this.moveMonth(date, dir*12);
19550     },
19551
19552     dateWithinRange: function(date)
19553     {
19554         return date >= this.startDate && date <= this.endDate;
19555     },
19556
19557     
19558     remove: function() 
19559     {
19560         this.picker().remove();
19561     },
19562     
19563     validateValue : function(value)
19564     {
19565         if(this.getVisibilityEl().hasClass('hidden')){
19566             return true;
19567         }
19568         
19569         if(value.length < 1)  {
19570             if(this.allowBlank){
19571                 return true;
19572             }
19573             return false;
19574         }
19575         
19576         if(value.length < this.minLength){
19577             return false;
19578         }
19579         if(value.length > this.maxLength){
19580             return false;
19581         }
19582         if(this.vtype){
19583             var vt = Roo.form.VTypes;
19584             if(!vt[this.vtype](value, this)){
19585                 return false;
19586             }
19587         }
19588         if(typeof this.validator == "function"){
19589             var msg = this.validator(value);
19590             if(msg !== true){
19591                 return false;
19592             }
19593         }
19594         
19595         if(this.regex && !this.regex.test(value)){
19596             return false;
19597         }
19598         
19599         if(typeof(this.parseDate(value)) == 'undefined'){
19600             return false;
19601         }
19602         
19603         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19604             return false;
19605         }      
19606         
19607         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19608             return false;
19609         } 
19610         
19611         
19612         return true;
19613     },
19614     
19615     reset : function()
19616     {
19617         this.date = this.viewDate = '';
19618         
19619         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19620     }
19621    
19622 });
19623
19624 Roo.apply(Roo.bootstrap.DateField,  {
19625     
19626     head : {
19627         tag: 'thead',
19628         cn: [
19629         {
19630             tag: 'tr',
19631             cn: [
19632             {
19633                 tag: 'th',
19634                 cls: 'prev',
19635                 html: '<i class="fa fa-arrow-left"/>'
19636             },
19637             {
19638                 tag: 'th',
19639                 cls: 'switch',
19640                 colspan: '5'
19641             },
19642             {
19643                 tag: 'th',
19644                 cls: 'next',
19645                 html: '<i class="fa fa-arrow-right"/>'
19646             }
19647
19648             ]
19649         }
19650         ]
19651     },
19652     
19653     content : {
19654         tag: 'tbody',
19655         cn: [
19656         {
19657             tag: 'tr',
19658             cn: [
19659             {
19660                 tag: 'td',
19661                 colspan: '7'
19662             }
19663             ]
19664         }
19665         ]
19666     },
19667     
19668     footer : {
19669         tag: 'tfoot',
19670         cn: [
19671         {
19672             tag: 'tr',
19673             cn: [
19674             {
19675                 tag: 'th',
19676                 colspan: '7',
19677                 cls: 'today'
19678             }
19679                     
19680             ]
19681         }
19682         ]
19683     },
19684     
19685     dates:{
19686         en: {
19687             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19688             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19689             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19690             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19691             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19692             today: "Today"
19693         }
19694     },
19695     
19696     modes: [
19697     {
19698         clsName: 'days',
19699         navFnc: 'Month',
19700         navStep: 1
19701     },
19702     {
19703         clsName: 'months',
19704         navFnc: 'FullYear',
19705         navStep: 1
19706     },
19707     {
19708         clsName: 'years',
19709         navFnc: 'FullYear',
19710         navStep: 10
19711     }]
19712 });
19713
19714 Roo.apply(Roo.bootstrap.DateField,  {
19715   
19716     template : {
19717         tag: 'div',
19718         cls: 'datepicker dropdown-menu roo-dynamic',
19719         cn: [
19720         {
19721             tag: 'div',
19722             cls: 'datepicker-days',
19723             cn: [
19724             {
19725                 tag: 'table',
19726                 cls: 'table-condensed',
19727                 cn:[
19728                 Roo.bootstrap.DateField.head,
19729                 {
19730                     tag: 'tbody'
19731                 },
19732                 Roo.bootstrap.DateField.footer
19733                 ]
19734             }
19735             ]
19736         },
19737         {
19738             tag: 'div',
19739             cls: 'datepicker-months',
19740             cn: [
19741             {
19742                 tag: 'table',
19743                 cls: 'table-condensed',
19744                 cn:[
19745                 Roo.bootstrap.DateField.head,
19746                 Roo.bootstrap.DateField.content,
19747                 Roo.bootstrap.DateField.footer
19748                 ]
19749             }
19750             ]
19751         },
19752         {
19753             tag: 'div',
19754             cls: 'datepicker-years',
19755             cn: [
19756             {
19757                 tag: 'table',
19758                 cls: 'table-condensed',
19759                 cn:[
19760                 Roo.bootstrap.DateField.head,
19761                 Roo.bootstrap.DateField.content,
19762                 Roo.bootstrap.DateField.footer
19763                 ]
19764             }
19765             ]
19766         }
19767         ]
19768     }
19769 });
19770
19771  
19772
19773  /*
19774  * - LGPL
19775  *
19776  * TimeField
19777  * 
19778  */
19779
19780 /**
19781  * @class Roo.bootstrap.TimeField
19782  * @extends Roo.bootstrap.Input
19783  * Bootstrap DateField class
19784  * 
19785  * 
19786  * @constructor
19787  * Create a new TimeField
19788  * @param {Object} config The config object
19789  */
19790
19791 Roo.bootstrap.TimeField = function(config){
19792     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19793     this.addEvents({
19794             /**
19795              * @event show
19796              * Fires when this field show.
19797              * @param {Roo.bootstrap.DateField} thisthis
19798              * @param {Mixed} date The date value
19799              */
19800             show : true,
19801             /**
19802              * @event show
19803              * Fires when this field hide.
19804              * @param {Roo.bootstrap.DateField} this
19805              * @param {Mixed} date The date value
19806              */
19807             hide : true,
19808             /**
19809              * @event select
19810              * Fires when select a date.
19811              * @param {Roo.bootstrap.DateField} this
19812              * @param {Mixed} date The date value
19813              */
19814             select : true
19815         });
19816 };
19817
19818 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19819     
19820     /**
19821      * @cfg {String} format
19822      * The default time format string which can be overriden for localization support.  The format must be
19823      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19824      */
19825     format : "H:i",
19826        
19827     onRender: function(ct, position)
19828     {
19829         
19830         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19831                 
19832         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19833         
19834         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19835         
19836         this.pop = this.picker().select('>.datepicker-time',true).first();
19837         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19838         
19839         this.picker().on('mousedown', this.onMousedown, this);
19840         this.picker().on('click', this.onClick, this);
19841         
19842         this.picker().addClass('datepicker-dropdown');
19843     
19844         this.fillTime();
19845         this.update();
19846             
19847         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19848         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19849         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19850         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19851         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19852         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19853
19854     },
19855     
19856     fireKey: function(e){
19857         if (!this.picker().isVisible()){
19858             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19859                 this.show();
19860             }
19861             return;
19862         }
19863
19864         e.preventDefault();
19865         
19866         switch(e.keyCode){
19867             case 27: // escape
19868                 this.hide();
19869                 break;
19870             case 37: // left
19871             case 39: // right
19872                 this.onTogglePeriod();
19873                 break;
19874             case 38: // up
19875                 this.onIncrementMinutes();
19876                 break;
19877             case 40: // down
19878                 this.onDecrementMinutes();
19879                 break;
19880             case 13: // enter
19881             case 9: // tab
19882                 this.setTime();
19883                 break;
19884         }
19885     },
19886     
19887     onClick: function(e) {
19888         e.stopPropagation();
19889         e.preventDefault();
19890     },
19891     
19892     picker : function()
19893     {
19894         return this.el.select('.datepicker', true).first();
19895     },
19896     
19897     fillTime: function()
19898     {    
19899         var time = this.pop.select('tbody', true).first();
19900         
19901         time.dom.innerHTML = '';
19902         
19903         time.createChild({
19904             tag: 'tr',
19905             cn: [
19906                 {
19907                     tag: 'td',
19908                     cn: [
19909                         {
19910                             tag: 'a',
19911                             href: '#',
19912                             cls: 'btn',
19913                             cn: [
19914                                 {
19915                                     tag: 'span',
19916                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19917                                 }
19918                             ]
19919                         } 
19920                     ]
19921                 },
19922                 {
19923                     tag: 'td',
19924                     cls: 'separator'
19925                 },
19926                 {
19927                     tag: 'td',
19928                     cn: [
19929                         {
19930                             tag: 'a',
19931                             href: '#',
19932                             cls: 'btn',
19933                             cn: [
19934                                 {
19935                                     tag: 'span',
19936                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19937                                 }
19938                             ]
19939                         }
19940                     ]
19941                 },
19942                 {
19943                     tag: 'td',
19944                     cls: 'separator'
19945                 }
19946             ]
19947         });
19948         
19949         time.createChild({
19950             tag: 'tr',
19951             cn: [
19952                 {
19953                     tag: 'td',
19954                     cn: [
19955                         {
19956                             tag: 'span',
19957                             cls: 'timepicker-hour',
19958                             html: '00'
19959                         }  
19960                     ]
19961                 },
19962                 {
19963                     tag: 'td',
19964                     cls: 'separator',
19965                     html: ':'
19966                 },
19967                 {
19968                     tag: 'td',
19969                     cn: [
19970                         {
19971                             tag: 'span',
19972                             cls: 'timepicker-minute',
19973                             html: '00'
19974                         }  
19975                     ]
19976                 },
19977                 {
19978                     tag: 'td',
19979                     cls: 'separator'
19980                 },
19981                 {
19982                     tag: 'td',
19983                     cn: [
19984                         {
19985                             tag: 'button',
19986                             type: 'button',
19987                             cls: 'btn btn-primary period',
19988                             html: 'AM'
19989                             
19990                         }
19991                     ]
19992                 }
19993             ]
19994         });
19995         
19996         time.createChild({
19997             tag: 'tr',
19998             cn: [
19999                 {
20000                     tag: 'td',
20001                     cn: [
20002                         {
20003                             tag: 'a',
20004                             href: '#',
20005                             cls: 'btn',
20006                             cn: [
20007                                 {
20008                                     tag: 'span',
20009                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20010                                 }
20011                             ]
20012                         }
20013                     ]
20014                 },
20015                 {
20016                     tag: 'td',
20017                     cls: 'separator'
20018                 },
20019                 {
20020                     tag: 'td',
20021                     cn: [
20022                         {
20023                             tag: 'a',
20024                             href: '#',
20025                             cls: 'btn',
20026                             cn: [
20027                                 {
20028                                     tag: 'span',
20029                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20030                                 }
20031                             ]
20032                         }
20033                     ]
20034                 },
20035                 {
20036                     tag: 'td',
20037                     cls: 'separator'
20038                 }
20039             ]
20040         });
20041         
20042     },
20043     
20044     update: function()
20045     {
20046         
20047         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20048         
20049         this.fill();
20050     },
20051     
20052     fill: function() 
20053     {
20054         var hours = this.time.getHours();
20055         var minutes = this.time.getMinutes();
20056         var period = 'AM';
20057         
20058         if(hours > 11){
20059             period = 'PM';
20060         }
20061         
20062         if(hours == 0){
20063             hours = 12;
20064         }
20065         
20066         
20067         if(hours > 12){
20068             hours = hours - 12;
20069         }
20070         
20071         if(hours < 10){
20072             hours = '0' + hours;
20073         }
20074         
20075         if(minutes < 10){
20076             minutes = '0' + minutes;
20077         }
20078         
20079         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20080         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20081         this.pop.select('button', true).first().dom.innerHTML = period;
20082         
20083     },
20084     
20085     place: function()
20086     {   
20087         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20088         
20089         var cls = ['bottom'];
20090         
20091         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20092             cls.pop();
20093             cls.push('top');
20094         }
20095         
20096         cls.push('right');
20097         
20098         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20099             cls.pop();
20100             cls.push('left');
20101         }
20102         
20103         this.picker().addClass(cls.join('-'));
20104         
20105         var _this = this;
20106         
20107         Roo.each(cls, function(c){
20108             if(c == 'bottom'){
20109                 _this.picker().setTop(_this.inputEl().getHeight());
20110                 return;
20111             }
20112             if(c == 'top'){
20113                 _this.picker().setTop(0 - _this.picker().getHeight());
20114                 return;
20115             }
20116             
20117             if(c == 'left'){
20118                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20119                 return;
20120             }
20121             if(c == 'right'){
20122                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20123                 return;
20124             }
20125         });
20126         
20127     },
20128   
20129     onFocus : function()
20130     {
20131         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20132         this.show();
20133     },
20134     
20135     onBlur : function()
20136     {
20137         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20138         this.hide();
20139     },
20140     
20141     show : function()
20142     {
20143         this.picker().show();
20144         this.pop.show();
20145         this.update();
20146         this.place();
20147         
20148         this.fireEvent('show', this, this.date);
20149     },
20150     
20151     hide : function()
20152     {
20153         this.picker().hide();
20154         this.pop.hide();
20155         
20156         this.fireEvent('hide', this, this.date);
20157     },
20158     
20159     setTime : function()
20160     {
20161         this.hide();
20162         this.setValue(this.time.format(this.format));
20163         
20164         this.fireEvent('select', this, this.date);
20165         
20166         
20167     },
20168     
20169     onMousedown: function(e){
20170         e.stopPropagation();
20171         e.preventDefault();
20172     },
20173     
20174     onIncrementHours: function()
20175     {
20176         Roo.log('onIncrementHours');
20177         this.time = this.time.add(Date.HOUR, 1);
20178         this.update();
20179         
20180     },
20181     
20182     onDecrementHours: function()
20183     {
20184         Roo.log('onDecrementHours');
20185         this.time = this.time.add(Date.HOUR, -1);
20186         this.update();
20187     },
20188     
20189     onIncrementMinutes: function()
20190     {
20191         Roo.log('onIncrementMinutes');
20192         this.time = this.time.add(Date.MINUTE, 1);
20193         this.update();
20194     },
20195     
20196     onDecrementMinutes: function()
20197     {
20198         Roo.log('onDecrementMinutes');
20199         this.time = this.time.add(Date.MINUTE, -1);
20200         this.update();
20201     },
20202     
20203     onTogglePeriod: function()
20204     {
20205         Roo.log('onTogglePeriod');
20206         this.time = this.time.add(Date.HOUR, 12);
20207         this.update();
20208     }
20209     
20210    
20211 });
20212
20213 Roo.apply(Roo.bootstrap.TimeField,  {
20214     
20215     content : {
20216         tag: 'tbody',
20217         cn: [
20218             {
20219                 tag: 'tr',
20220                 cn: [
20221                 {
20222                     tag: 'td',
20223                     colspan: '7'
20224                 }
20225                 ]
20226             }
20227         ]
20228     },
20229     
20230     footer : {
20231         tag: 'tfoot',
20232         cn: [
20233             {
20234                 tag: 'tr',
20235                 cn: [
20236                 {
20237                     tag: 'th',
20238                     colspan: '7',
20239                     cls: '',
20240                     cn: [
20241                         {
20242                             tag: 'button',
20243                             cls: 'btn btn-info ok',
20244                             html: 'OK'
20245                         }
20246                     ]
20247                 }
20248
20249                 ]
20250             }
20251         ]
20252     }
20253 });
20254
20255 Roo.apply(Roo.bootstrap.TimeField,  {
20256   
20257     template : {
20258         tag: 'div',
20259         cls: 'datepicker dropdown-menu',
20260         cn: [
20261             {
20262                 tag: 'div',
20263                 cls: 'datepicker-time',
20264                 cn: [
20265                 {
20266                     tag: 'table',
20267                     cls: 'table-condensed',
20268                     cn:[
20269                     Roo.bootstrap.TimeField.content,
20270                     Roo.bootstrap.TimeField.footer
20271                     ]
20272                 }
20273                 ]
20274             }
20275         ]
20276     }
20277 });
20278
20279  
20280
20281  /*
20282  * - LGPL
20283  *
20284  * MonthField
20285  * 
20286  */
20287
20288 /**
20289  * @class Roo.bootstrap.MonthField
20290  * @extends Roo.bootstrap.Input
20291  * Bootstrap MonthField class
20292  * 
20293  * @cfg {String} language default en
20294  * 
20295  * @constructor
20296  * Create a new MonthField
20297  * @param {Object} config The config object
20298  */
20299
20300 Roo.bootstrap.MonthField = function(config){
20301     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20302     
20303     this.addEvents({
20304         /**
20305          * @event show
20306          * Fires when this field show.
20307          * @param {Roo.bootstrap.MonthField} this
20308          * @param {Mixed} date The date value
20309          */
20310         show : true,
20311         /**
20312          * @event show
20313          * Fires when this field hide.
20314          * @param {Roo.bootstrap.MonthField} this
20315          * @param {Mixed} date The date value
20316          */
20317         hide : true,
20318         /**
20319          * @event select
20320          * Fires when select a date.
20321          * @param {Roo.bootstrap.MonthField} this
20322          * @param {String} oldvalue The old value
20323          * @param {String} newvalue The new value
20324          */
20325         select : true
20326     });
20327 };
20328
20329 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20330     
20331     onRender: function(ct, position)
20332     {
20333         
20334         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20335         
20336         this.language = this.language || 'en';
20337         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20338         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20339         
20340         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20341         this.isInline = false;
20342         this.isInput = true;
20343         this.component = this.el.select('.add-on', true).first() || false;
20344         this.component = (this.component && this.component.length === 0) ? false : this.component;
20345         this.hasInput = this.component && this.inputEL().length;
20346         
20347         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20348         
20349         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20350         
20351         this.picker().on('mousedown', this.onMousedown, this);
20352         this.picker().on('click', this.onClick, this);
20353         
20354         this.picker().addClass('datepicker-dropdown');
20355         
20356         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20357             v.setStyle('width', '189px');
20358         });
20359         
20360         this.fillMonths();
20361         
20362         this.update();
20363         
20364         if(this.isInline) {
20365             this.show();
20366         }
20367         
20368     },
20369     
20370     setValue: function(v, suppressEvent)
20371     {   
20372         var o = this.getValue();
20373         
20374         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20375         
20376         this.update();
20377
20378         if(suppressEvent !== true){
20379             this.fireEvent('select', this, o, v);
20380         }
20381         
20382     },
20383     
20384     getValue: function()
20385     {
20386         return this.value;
20387     },
20388     
20389     onClick: function(e) 
20390     {
20391         e.stopPropagation();
20392         e.preventDefault();
20393         
20394         var target = e.getTarget();
20395         
20396         if(target.nodeName.toLowerCase() === 'i'){
20397             target = Roo.get(target).dom.parentNode;
20398         }
20399         
20400         var nodeName = target.nodeName;
20401         var className = target.className;
20402         var html = target.innerHTML;
20403         
20404         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20405             return;
20406         }
20407         
20408         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20409         
20410         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20411         
20412         this.hide();
20413                         
20414     },
20415     
20416     picker : function()
20417     {
20418         return this.pickerEl;
20419     },
20420     
20421     fillMonths: function()
20422     {    
20423         var i = 0;
20424         var months = this.picker().select('>.datepicker-months td', true).first();
20425         
20426         months.dom.innerHTML = '';
20427         
20428         while (i < 12) {
20429             var month = {
20430                 tag: 'span',
20431                 cls: 'month',
20432                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20433             };
20434             
20435             months.createChild(month);
20436         }
20437         
20438     },
20439     
20440     update: function()
20441     {
20442         var _this = this;
20443         
20444         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20445             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20446         }
20447         
20448         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20449             e.removeClass('active');
20450             
20451             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20452                 e.addClass('active');
20453             }
20454         })
20455     },
20456     
20457     place: function()
20458     {
20459         if(this.isInline) {
20460             return;
20461         }
20462         
20463         this.picker().removeClass(['bottom', 'top']);
20464         
20465         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20466             /*
20467              * place to the top of element!
20468              *
20469              */
20470             
20471             this.picker().addClass('top');
20472             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20473             
20474             return;
20475         }
20476         
20477         this.picker().addClass('bottom');
20478         
20479         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20480     },
20481     
20482     onFocus : function()
20483     {
20484         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20485         this.show();
20486     },
20487     
20488     onBlur : function()
20489     {
20490         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20491         
20492         var d = this.inputEl().getValue();
20493         
20494         this.setValue(d);
20495                 
20496         this.hide();
20497     },
20498     
20499     show : function()
20500     {
20501         this.picker().show();
20502         this.picker().select('>.datepicker-months', true).first().show();
20503         this.update();
20504         this.place();
20505         
20506         this.fireEvent('show', this, this.date);
20507     },
20508     
20509     hide : function()
20510     {
20511         if(this.isInline) {
20512             return;
20513         }
20514         this.picker().hide();
20515         this.fireEvent('hide', this, this.date);
20516         
20517     },
20518     
20519     onMousedown: function(e)
20520     {
20521         e.stopPropagation();
20522         e.preventDefault();
20523     },
20524     
20525     keyup: function(e)
20526     {
20527         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20528         this.update();
20529     },
20530
20531     fireKey: function(e)
20532     {
20533         if (!this.picker().isVisible()){
20534             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20535                 this.show();
20536             }
20537             return;
20538         }
20539         
20540         var dir;
20541         
20542         switch(e.keyCode){
20543             case 27: // escape
20544                 this.hide();
20545                 e.preventDefault();
20546                 break;
20547             case 37: // left
20548             case 39: // right
20549                 dir = e.keyCode == 37 ? -1 : 1;
20550                 
20551                 this.vIndex = this.vIndex + dir;
20552                 
20553                 if(this.vIndex < 0){
20554                     this.vIndex = 0;
20555                 }
20556                 
20557                 if(this.vIndex > 11){
20558                     this.vIndex = 11;
20559                 }
20560                 
20561                 if(isNaN(this.vIndex)){
20562                     this.vIndex = 0;
20563                 }
20564                 
20565                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20566                 
20567                 break;
20568             case 38: // up
20569             case 40: // down
20570                 
20571                 dir = e.keyCode == 38 ? -1 : 1;
20572                 
20573                 this.vIndex = this.vIndex + dir * 4;
20574                 
20575                 if(this.vIndex < 0){
20576                     this.vIndex = 0;
20577                 }
20578                 
20579                 if(this.vIndex > 11){
20580                     this.vIndex = 11;
20581                 }
20582                 
20583                 if(isNaN(this.vIndex)){
20584                     this.vIndex = 0;
20585                 }
20586                 
20587                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20588                 break;
20589                 
20590             case 13: // enter
20591                 
20592                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20593                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20594                 }
20595                 
20596                 this.hide();
20597                 e.preventDefault();
20598                 break;
20599             case 9: // tab
20600                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20601                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20602                 }
20603                 this.hide();
20604                 break;
20605             case 16: // shift
20606             case 17: // ctrl
20607             case 18: // alt
20608                 break;
20609             default :
20610                 this.hide();
20611                 
20612         }
20613     },
20614     
20615     remove: function() 
20616     {
20617         this.picker().remove();
20618     }
20619    
20620 });
20621
20622 Roo.apply(Roo.bootstrap.MonthField,  {
20623     
20624     content : {
20625         tag: 'tbody',
20626         cn: [
20627         {
20628             tag: 'tr',
20629             cn: [
20630             {
20631                 tag: 'td',
20632                 colspan: '7'
20633             }
20634             ]
20635         }
20636         ]
20637     },
20638     
20639     dates:{
20640         en: {
20641             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20642             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20643         }
20644     }
20645 });
20646
20647 Roo.apply(Roo.bootstrap.MonthField,  {
20648   
20649     template : {
20650         tag: 'div',
20651         cls: 'datepicker dropdown-menu roo-dynamic',
20652         cn: [
20653             {
20654                 tag: 'div',
20655                 cls: 'datepicker-months',
20656                 cn: [
20657                 {
20658                     tag: 'table',
20659                     cls: 'table-condensed',
20660                     cn:[
20661                         Roo.bootstrap.DateField.content
20662                     ]
20663                 }
20664                 ]
20665             }
20666         ]
20667     }
20668 });
20669
20670  
20671
20672  
20673  /*
20674  * - LGPL
20675  *
20676  * CheckBox
20677  * 
20678  */
20679
20680 /**
20681  * @class Roo.bootstrap.CheckBox
20682  * @extends Roo.bootstrap.Input
20683  * Bootstrap CheckBox class
20684  * 
20685  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20686  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20687  * @cfg {String} boxLabel The text that appears beside the checkbox
20688  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20689  * @cfg {Boolean} checked initnal the element
20690  * @cfg {Boolean} inline inline the element (default false)
20691  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20692  * @cfg {String} tooltip label tooltip
20693  * 
20694  * @constructor
20695  * Create a new CheckBox
20696  * @param {Object} config The config object
20697  */
20698
20699 Roo.bootstrap.CheckBox = function(config){
20700     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20701    
20702     this.addEvents({
20703         /**
20704         * @event check
20705         * Fires when the element is checked or unchecked.
20706         * @param {Roo.bootstrap.CheckBox} this This input
20707         * @param {Boolean} checked The new checked value
20708         */
20709        check : true,
20710        /**
20711         * @event click
20712         * Fires when the element is click.
20713         * @param {Roo.bootstrap.CheckBox} this This input
20714         */
20715        click : true
20716     });
20717     
20718 };
20719
20720 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20721   
20722     inputType: 'checkbox',
20723     inputValue: 1,
20724     valueOff: 0,
20725     boxLabel: false,
20726     checked: false,
20727     weight : false,
20728     inline: false,
20729     tooltip : '',
20730     
20731     getAutoCreate : function()
20732     {
20733         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20734         
20735         var id = Roo.id();
20736         
20737         var cfg = {};
20738         
20739         cfg.cls = 'form-group ' + this.inputType; //input-group
20740         
20741         if(this.inline){
20742             cfg.cls += ' ' + this.inputType + '-inline';
20743         }
20744         
20745         var input =  {
20746             tag: 'input',
20747             id : id,
20748             type : this.inputType,
20749             value : this.inputValue,
20750             cls : 'roo-' + this.inputType, //'form-box',
20751             placeholder : this.placeholder || ''
20752             
20753         };
20754         
20755         if(this.inputType != 'radio'){
20756             var hidden =  {
20757                 tag: 'input',
20758                 type : 'hidden',
20759                 cls : 'roo-hidden-value',
20760                 value : this.checked ? this.inputValue : this.valueOff
20761             };
20762         }
20763         
20764             
20765         if (this.weight) { // Validity check?
20766             cfg.cls += " " + this.inputType + "-" + this.weight;
20767         }
20768         
20769         if (this.disabled) {
20770             input.disabled=true;
20771         }
20772         
20773         if(this.checked){
20774             input.checked = this.checked;
20775         }
20776         
20777         if (this.name) {
20778             
20779             input.name = this.name;
20780             
20781             if(this.inputType != 'radio'){
20782                 hidden.name = this.name;
20783                 input.name = '_hidden_' + this.name;
20784             }
20785         }
20786         
20787         if (this.size) {
20788             input.cls += ' input-' + this.size;
20789         }
20790         
20791         var settings=this;
20792         
20793         ['xs','sm','md','lg'].map(function(size){
20794             if (settings[size]) {
20795                 cfg.cls += ' col-' + size + '-' + settings[size];
20796             }
20797         });
20798         
20799         var inputblock = input;
20800          
20801         if (this.before || this.after) {
20802             
20803             inputblock = {
20804                 cls : 'input-group',
20805                 cn :  [] 
20806             };
20807             
20808             if (this.before) {
20809                 inputblock.cn.push({
20810                     tag :'span',
20811                     cls : 'input-group-addon',
20812                     html : this.before
20813                 });
20814             }
20815             
20816             inputblock.cn.push(input);
20817             
20818             if(this.inputType != 'radio'){
20819                 inputblock.cn.push(hidden);
20820             }
20821             
20822             if (this.after) {
20823                 inputblock.cn.push({
20824                     tag :'span',
20825                     cls : 'input-group-addon',
20826                     html : this.after
20827                 });
20828             }
20829             
20830         }
20831         
20832         if (align ==='left' && this.fieldLabel.length) {
20833 //                Roo.log("left and has label");
20834             cfg.cn = [
20835                 {
20836                     tag: 'label',
20837                     'for' :  id,
20838                     cls : 'control-label',
20839                     html : this.fieldLabel
20840                 },
20841                 {
20842                     cls : "", 
20843                     cn: [
20844                         inputblock
20845                     ]
20846                 }
20847             ];
20848             
20849             if(this.labelWidth > 12){
20850                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20851             }
20852             
20853             if(this.labelWidth < 13 && this.labelmd == 0){
20854                 this.labelmd = this.labelWidth;
20855             }
20856             
20857             if(this.labellg > 0){
20858                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20859                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20860             }
20861             
20862             if(this.labelmd > 0){
20863                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20864                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20865             }
20866             
20867             if(this.labelsm > 0){
20868                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20869                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20870             }
20871             
20872             if(this.labelxs > 0){
20873                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20874                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20875             }
20876             
20877         } else if ( this.fieldLabel.length) {
20878 //                Roo.log(" label");
20879                 cfg.cn = [
20880                    
20881                     {
20882                         tag: this.boxLabel ? 'span' : 'label',
20883                         'for': id,
20884                         cls: 'control-label box-input-label',
20885                         //cls : 'input-group-addon',
20886                         html : this.fieldLabel
20887                     },
20888                     
20889                     inputblock
20890                     
20891                 ];
20892
20893         } else {
20894             
20895 //                Roo.log(" no label && no align");
20896                 cfg.cn = [  inputblock ] ;
20897                 
20898                 
20899         }
20900         
20901         if(this.boxLabel){
20902              var boxLabelCfg = {
20903                 tag: 'label',
20904                 //'for': id, // box label is handled by onclick - so no for...
20905                 cls: 'box-label',
20906                 html: this.boxLabel
20907             };
20908             
20909             if(this.tooltip){
20910                 boxLabelCfg.tooltip = this.tooltip;
20911             }
20912              
20913             cfg.cn.push(boxLabelCfg);
20914         }
20915         
20916         if(this.inputType != 'radio'){
20917             cfg.cn.push(hidden);
20918         }
20919         
20920         return cfg;
20921         
20922     },
20923     
20924     /**
20925      * return the real input element.
20926      */
20927     inputEl: function ()
20928     {
20929         return this.el.select('input.roo-' + this.inputType,true).first();
20930     },
20931     hiddenEl: function ()
20932     {
20933         return this.el.select('input.roo-hidden-value',true).first();
20934     },
20935     
20936     labelEl: function()
20937     {
20938         return this.el.select('label.control-label',true).first();
20939     },
20940     /* depricated... */
20941     
20942     label: function()
20943     {
20944         return this.labelEl();
20945     },
20946     
20947     boxLabelEl: function()
20948     {
20949         return this.el.select('label.box-label',true).first();
20950     },
20951     
20952     initEvents : function()
20953     {
20954 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20955         
20956         this.inputEl().on('click', this.onClick,  this);
20957         
20958         if (this.boxLabel) { 
20959             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20960         }
20961         
20962         this.startValue = this.getValue();
20963         
20964         if(this.groupId){
20965             Roo.bootstrap.CheckBox.register(this);
20966         }
20967     },
20968     
20969     onClick : function(e)
20970     {   
20971         if(this.fireEvent('click', this, e) !== false){
20972             this.setChecked(!this.checked);
20973         }
20974         
20975     },
20976     
20977     setChecked : function(state,suppressEvent)
20978     {
20979         this.startValue = this.getValue();
20980
20981         if(this.inputType == 'radio'){
20982             
20983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20984                 e.dom.checked = false;
20985             });
20986             
20987             this.inputEl().dom.checked = true;
20988             
20989             this.inputEl().dom.value = this.inputValue;
20990             
20991             if(suppressEvent !== true){
20992                 this.fireEvent('check', this, true);
20993             }
20994             
20995             this.validate();
20996             
20997             return;
20998         }
20999         
21000         this.checked = state;
21001         
21002         this.inputEl().dom.checked = state;
21003         
21004         
21005         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21006         
21007         if(suppressEvent !== true){
21008             this.fireEvent('check', this, state);
21009         }
21010         
21011         this.validate();
21012     },
21013     
21014     getValue : function()
21015     {
21016         if(this.inputType == 'radio'){
21017             return this.getGroupValue();
21018         }
21019         
21020         return this.hiddenEl().dom.value;
21021         
21022     },
21023     
21024     getGroupValue : function()
21025     {
21026         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21027             return '';
21028         }
21029         
21030         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21031     },
21032     
21033     setValue : function(v,suppressEvent)
21034     {
21035         if(this.inputType == 'radio'){
21036             this.setGroupValue(v, suppressEvent);
21037             return;
21038         }
21039         
21040         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21041         
21042         this.validate();
21043     },
21044     
21045     setGroupValue : function(v, suppressEvent)
21046     {
21047         this.startValue = this.getValue();
21048         
21049         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21050             e.dom.checked = false;
21051             
21052             if(e.dom.value == v){
21053                 e.dom.checked = true;
21054             }
21055         });
21056         
21057         if(suppressEvent !== true){
21058             this.fireEvent('check', this, true);
21059         }
21060
21061         this.validate();
21062         
21063         return;
21064     },
21065     
21066     validate : function()
21067     {
21068         if(this.getVisibilityEl().hasClass('hidden')){
21069             return true;
21070         }
21071         
21072         if(
21073                 this.disabled || 
21074                 (this.inputType == 'radio' && this.validateRadio()) ||
21075                 (this.inputType == 'checkbox' && this.validateCheckbox())
21076         ){
21077             this.markValid();
21078             return true;
21079         }
21080         
21081         this.markInvalid();
21082         return false;
21083     },
21084     
21085     validateRadio : function()
21086     {
21087         if(this.getVisibilityEl().hasClass('hidden')){
21088             return true;
21089         }
21090         
21091         if(this.allowBlank){
21092             return true;
21093         }
21094         
21095         var valid = false;
21096         
21097         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21098             if(!e.dom.checked){
21099                 return;
21100             }
21101             
21102             valid = true;
21103             
21104             return false;
21105         });
21106         
21107         return valid;
21108     },
21109     
21110     validateCheckbox : function()
21111     {
21112         if(!this.groupId){
21113             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21114             //return (this.getValue() == this.inputValue) ? true : false;
21115         }
21116         
21117         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21118         
21119         if(!group){
21120             return false;
21121         }
21122         
21123         var r = false;
21124         
21125         for(var i in group){
21126             if(group[i].el.isVisible(true)){
21127                 r = false;
21128                 break;
21129             }
21130             
21131             r = true;
21132         }
21133         
21134         for(var i in group){
21135             if(r){
21136                 break;
21137             }
21138             
21139             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21140         }
21141         
21142         return r;
21143     },
21144     
21145     /**
21146      * Mark this field as valid
21147      */
21148     markValid : function()
21149     {
21150         var _this = this;
21151         
21152         this.fireEvent('valid', this);
21153         
21154         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21155         
21156         if(this.groupId){
21157             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21158         }
21159         
21160         if(label){
21161             label.markValid();
21162         }
21163
21164         if(this.inputType == 'radio'){
21165             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21166                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21167                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21168             });
21169             
21170             return;
21171         }
21172
21173         if(!this.groupId){
21174             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21175             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21176             return;
21177         }
21178         
21179         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21180         
21181         if(!group){
21182             return;
21183         }
21184         
21185         for(var i in group){
21186             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21187             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21188         }
21189     },
21190     
21191      /**
21192      * Mark this field as invalid
21193      * @param {String} msg The validation message
21194      */
21195     markInvalid : function(msg)
21196     {
21197         if(this.allowBlank){
21198             return;
21199         }
21200         
21201         var _this = this;
21202         
21203         this.fireEvent('invalid', this, msg);
21204         
21205         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21206         
21207         if(this.groupId){
21208             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21209         }
21210         
21211         if(label){
21212             label.markInvalid();
21213         }
21214             
21215         if(this.inputType == 'radio'){
21216             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21217                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21218                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21219             });
21220             
21221             return;
21222         }
21223         
21224         if(!this.groupId){
21225             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21226             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21227             return;
21228         }
21229         
21230         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21231         
21232         if(!group){
21233             return;
21234         }
21235         
21236         for(var i in group){
21237             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21238             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21239         }
21240         
21241     },
21242     
21243     clearInvalid : function()
21244     {
21245         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21246         
21247         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21248         
21249         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21250         
21251         if (label && label.iconEl) {
21252             label.iconEl.removeClass(label.validClass);
21253             label.iconEl.removeClass(label.invalidClass);
21254         }
21255     },
21256     
21257     disable : function()
21258     {
21259         if(this.inputType != 'radio'){
21260             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21261             return;
21262         }
21263         
21264         var _this = this;
21265         
21266         if(this.rendered){
21267             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21268                 _this.getActionEl().addClass(this.disabledClass);
21269                 e.dom.disabled = true;
21270             });
21271         }
21272         
21273         this.disabled = true;
21274         this.fireEvent("disable", this);
21275         return this;
21276     },
21277
21278     enable : function()
21279     {
21280         if(this.inputType != 'radio'){
21281             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21282             return;
21283         }
21284         
21285         var _this = this;
21286         
21287         if(this.rendered){
21288             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21289                 _this.getActionEl().removeClass(this.disabledClass);
21290                 e.dom.disabled = false;
21291             });
21292         }
21293         
21294         this.disabled = false;
21295         this.fireEvent("enable", this);
21296         return this;
21297     },
21298     
21299     setBoxLabel : function(v)
21300     {
21301         this.boxLabel = v;
21302         
21303         if(this.rendered){
21304             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21305         }
21306     }
21307
21308 });
21309
21310 Roo.apply(Roo.bootstrap.CheckBox, {
21311     
21312     groups: {},
21313     
21314      /**
21315     * register a CheckBox Group
21316     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21317     */
21318     register : function(checkbox)
21319     {
21320         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21321             this.groups[checkbox.groupId] = {};
21322         }
21323         
21324         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21325             return;
21326         }
21327         
21328         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21329         
21330     },
21331     /**
21332     * fetch a CheckBox Group based on the group ID
21333     * @param {string} the group ID
21334     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21335     */
21336     get: function(groupId) {
21337         if (typeof(this.groups[groupId]) == 'undefined') {
21338             return false;
21339         }
21340         
21341         return this.groups[groupId] ;
21342     }
21343     
21344     
21345 });
21346 /*
21347  * - LGPL
21348  *
21349  * RadioItem
21350  * 
21351  */
21352
21353 /**
21354  * @class Roo.bootstrap.Radio
21355  * @extends Roo.bootstrap.Component
21356  * Bootstrap Radio class
21357  * @cfg {String} boxLabel - the label associated
21358  * @cfg {String} value - the value of radio
21359  * 
21360  * @constructor
21361  * Create a new Radio
21362  * @param {Object} config The config object
21363  */
21364 Roo.bootstrap.Radio = function(config){
21365     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21366     
21367 };
21368
21369 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21370     
21371     boxLabel : '',
21372     
21373     value : '',
21374     
21375     getAutoCreate : function()
21376     {
21377         var cfg = {
21378             tag : 'div',
21379             cls : 'form-group radio',
21380             cn : [
21381                 {
21382                     tag : 'label',
21383                     cls : 'box-label',
21384                     html : this.boxLabel
21385                 }
21386             ]
21387         };
21388         
21389         return cfg;
21390     },
21391     
21392     initEvents : function() 
21393     {
21394         this.parent().register(this);
21395         
21396         this.el.on('click', this.onClick, this);
21397         
21398     },
21399     
21400     onClick : function(e)
21401     {
21402         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21403             this.setChecked(true);
21404         }
21405     },
21406     
21407     setChecked : function(state, suppressEvent)
21408     {
21409         this.parent().setValue(this.value, suppressEvent);
21410         
21411     },
21412     
21413     setBoxLabel : function(v)
21414     {
21415         this.boxLabel = v;
21416         
21417         if(this.rendered){
21418             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21419         }
21420     }
21421     
21422 });
21423  
21424
21425  /*
21426  * - LGPL
21427  *
21428  * Input
21429  * 
21430  */
21431
21432 /**
21433  * @class Roo.bootstrap.SecurePass
21434  * @extends Roo.bootstrap.Input
21435  * Bootstrap SecurePass class
21436  *
21437  * 
21438  * @constructor
21439  * Create a new SecurePass
21440  * @param {Object} config The config object
21441  */
21442  
21443 Roo.bootstrap.SecurePass = function (config) {
21444     // these go here, so the translation tool can replace them..
21445     this.errors = {
21446         PwdEmpty: "Please type a password, and then retype it to confirm.",
21447         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21448         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21449         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21450         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21451         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21452         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21453         TooWeak: "Your password is Too Weak."
21454     },
21455     this.meterLabel = "Password strength:";
21456     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21457     this.meterClass = [
21458         "roo-password-meter-tooweak", 
21459         "roo-password-meter-weak", 
21460         "roo-password-meter-medium", 
21461         "roo-password-meter-strong", 
21462         "roo-password-meter-grey"
21463     ];
21464     
21465     this.errors = {};
21466     
21467     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21468 }
21469
21470 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21471     /**
21472      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21473      * {
21474      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21475      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21476      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21477      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21478      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21479      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21480      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21481      * })
21482      */
21483     // private
21484     
21485     meterWidth: 300,
21486     errorMsg :'',    
21487     errors: false,
21488     imageRoot: '/',
21489     /**
21490      * @cfg {String/Object} Label for the strength meter (defaults to
21491      * 'Password strength:')
21492      */
21493     // private
21494     meterLabel: '',
21495     /**
21496      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21497      * ['Weak', 'Medium', 'Strong'])
21498      */
21499     // private    
21500     pwdStrengths: false,    
21501     // private
21502     strength: 0,
21503     // private
21504     _lastPwd: null,
21505     // private
21506     kCapitalLetter: 0,
21507     kSmallLetter: 1,
21508     kDigit: 2,
21509     kPunctuation: 3,
21510     
21511     insecure: false,
21512     // private
21513     initEvents: function ()
21514     {
21515         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21516
21517         if (this.el.is('input[type=password]') && Roo.isSafari) {
21518             this.el.on('keydown', this.SafariOnKeyDown, this);
21519         }
21520
21521         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21522     },
21523     // private
21524     onRender: function (ct, position)
21525     {
21526         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21527         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21528         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21529
21530         this.trigger.createChild({
21531                    cn: [
21532                     {
21533                     //id: 'PwdMeter',
21534                     tag: 'div',
21535                     cls: 'roo-password-meter-grey col-xs-12',
21536                     style: {
21537                         //width: 0,
21538                         //width: this.meterWidth + 'px'                                                
21539                         }
21540                     },
21541                     {                            
21542                          cls: 'roo-password-meter-text'                          
21543                     }
21544                 ]            
21545         });
21546
21547          
21548         if (this.hideTrigger) {
21549             this.trigger.setDisplayed(false);
21550         }
21551         this.setSize(this.width || '', this.height || '');
21552     },
21553     // private
21554     onDestroy: function ()
21555     {
21556         if (this.trigger) {
21557             this.trigger.removeAllListeners();
21558             this.trigger.remove();
21559         }
21560         if (this.wrap) {
21561             this.wrap.remove();
21562         }
21563         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21564     },
21565     // private
21566     checkStrength: function ()
21567     {
21568         var pwd = this.inputEl().getValue();
21569         if (pwd == this._lastPwd) {
21570             return;
21571         }
21572
21573         var strength;
21574         if (this.ClientSideStrongPassword(pwd)) {
21575             strength = 3;
21576         } else if (this.ClientSideMediumPassword(pwd)) {
21577             strength = 2;
21578         } else if (this.ClientSideWeakPassword(pwd)) {
21579             strength = 1;
21580         } else {
21581             strength = 0;
21582         }
21583         
21584         Roo.log('strength1: ' + strength);
21585         
21586         //var pm = this.trigger.child('div/div/div').dom;
21587         var pm = this.trigger.child('div/div');
21588         pm.removeClass(this.meterClass);
21589         pm.addClass(this.meterClass[strength]);
21590                 
21591         
21592         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21593                 
21594         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21595         
21596         this._lastPwd = pwd;
21597     },
21598     reset: function ()
21599     {
21600         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21601         
21602         this._lastPwd = '';
21603         
21604         var pm = this.trigger.child('div/div');
21605         pm.removeClass(this.meterClass);
21606         pm.addClass('roo-password-meter-grey');        
21607         
21608         
21609         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21610         
21611         pt.innerHTML = '';
21612         this.inputEl().dom.type='password';
21613     },
21614     // private
21615     validateValue: function (value)
21616     {
21617         
21618         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21619             return false;
21620         }
21621         if (value.length == 0) {
21622             if (this.allowBlank) {
21623                 this.clearInvalid();
21624                 return true;
21625             }
21626
21627             this.markInvalid(this.errors.PwdEmpty);
21628             this.errorMsg = this.errors.PwdEmpty;
21629             return false;
21630         }
21631         
21632         if(this.insecure){
21633             return true;
21634         }
21635         
21636         if ('[\x21-\x7e]*'.match(value)) {
21637             this.markInvalid(this.errors.PwdBadChar);
21638             this.errorMsg = this.errors.PwdBadChar;
21639             return false;
21640         }
21641         if (value.length < 6) {
21642             this.markInvalid(this.errors.PwdShort);
21643             this.errorMsg = this.errors.PwdShort;
21644             return false;
21645         }
21646         if (value.length > 16) {
21647             this.markInvalid(this.errors.PwdLong);
21648             this.errorMsg = this.errors.PwdLong;
21649             return false;
21650         }
21651         var strength;
21652         if (this.ClientSideStrongPassword(value)) {
21653             strength = 3;
21654         } else if (this.ClientSideMediumPassword(value)) {
21655             strength = 2;
21656         } else if (this.ClientSideWeakPassword(value)) {
21657             strength = 1;
21658         } else {
21659             strength = 0;
21660         }
21661
21662         
21663         if (strength < 2) {
21664             //this.markInvalid(this.errors.TooWeak);
21665             this.errorMsg = this.errors.TooWeak;
21666             //return false;
21667         }
21668         
21669         
21670         console.log('strength2: ' + strength);
21671         
21672         //var pm = this.trigger.child('div/div/div').dom;
21673         
21674         var pm = this.trigger.child('div/div');
21675         pm.removeClass(this.meterClass);
21676         pm.addClass(this.meterClass[strength]);
21677                 
21678         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21679                 
21680         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21681         
21682         this.errorMsg = ''; 
21683         return true;
21684     },
21685     // private
21686     CharacterSetChecks: function (type)
21687     {
21688         this.type = type;
21689         this.fResult = false;
21690     },
21691     // private
21692     isctype: function (character, type)
21693     {
21694         switch (type) {  
21695             case this.kCapitalLetter:
21696                 if (character >= 'A' && character <= 'Z') {
21697                     return true;
21698                 }
21699                 break;
21700             
21701             case this.kSmallLetter:
21702                 if (character >= 'a' && character <= 'z') {
21703                     return true;
21704                 }
21705                 break;
21706             
21707             case this.kDigit:
21708                 if (character >= '0' && character <= '9') {
21709                     return true;
21710                 }
21711                 break;
21712             
21713             case this.kPunctuation:
21714                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21715                     return true;
21716                 }
21717                 break;
21718             
21719             default:
21720                 return false;
21721         }
21722
21723     },
21724     // private
21725     IsLongEnough: function (pwd, size)
21726     {
21727         return !(pwd == null || isNaN(size) || pwd.length < size);
21728     },
21729     // private
21730     SpansEnoughCharacterSets: function (word, nb)
21731     {
21732         if (!this.IsLongEnough(word, nb))
21733         {
21734             return false;
21735         }
21736
21737         var characterSetChecks = new Array(
21738             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21739             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21740         );
21741         
21742         for (var index = 0; index < word.length; ++index) {
21743             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21744                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21745                     characterSetChecks[nCharSet].fResult = true;
21746                     break;
21747                 }
21748             }
21749         }
21750
21751         var nCharSets = 0;
21752         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21753             if (characterSetChecks[nCharSet].fResult) {
21754                 ++nCharSets;
21755             }
21756         }
21757
21758         if (nCharSets < nb) {
21759             return false;
21760         }
21761         return true;
21762     },
21763     // private
21764     ClientSideStrongPassword: function (pwd)
21765     {
21766         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21767     },
21768     // private
21769     ClientSideMediumPassword: function (pwd)
21770     {
21771         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21772     },
21773     // private
21774     ClientSideWeakPassword: function (pwd)
21775     {
21776         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21777     }
21778           
21779 })//<script type="text/javascript">
21780
21781 /*
21782  * Based  Ext JS Library 1.1.1
21783  * Copyright(c) 2006-2007, Ext JS, LLC.
21784  * LGPL
21785  *
21786  */
21787  
21788 /**
21789  * @class Roo.HtmlEditorCore
21790  * @extends Roo.Component
21791  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21792  *
21793  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21794  */
21795
21796 Roo.HtmlEditorCore = function(config){
21797     
21798     
21799     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21800     
21801     
21802     this.addEvents({
21803         /**
21804          * @event initialize
21805          * Fires when the editor is fully initialized (including the iframe)
21806          * @param {Roo.HtmlEditorCore} this
21807          */
21808         initialize: true,
21809         /**
21810          * @event activate
21811          * Fires when the editor is first receives the focus. Any insertion must wait
21812          * until after this event.
21813          * @param {Roo.HtmlEditorCore} this
21814          */
21815         activate: true,
21816          /**
21817          * @event beforesync
21818          * Fires before the textarea is updated with content from the editor iframe. Return false
21819          * to cancel the sync.
21820          * @param {Roo.HtmlEditorCore} this
21821          * @param {String} html
21822          */
21823         beforesync: true,
21824          /**
21825          * @event beforepush
21826          * Fires before the iframe editor is updated with content from the textarea. Return false
21827          * to cancel the push.
21828          * @param {Roo.HtmlEditorCore} this
21829          * @param {String} html
21830          */
21831         beforepush: true,
21832          /**
21833          * @event sync
21834          * Fires when the textarea is updated with content from the editor iframe.
21835          * @param {Roo.HtmlEditorCore} this
21836          * @param {String} html
21837          */
21838         sync: true,
21839          /**
21840          * @event push
21841          * Fires when the iframe editor is updated with content from the textarea.
21842          * @param {Roo.HtmlEditorCore} this
21843          * @param {String} html
21844          */
21845         push: true,
21846         
21847         /**
21848          * @event editorevent
21849          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21850          * @param {Roo.HtmlEditorCore} this
21851          */
21852         editorevent: true
21853         
21854     });
21855     
21856     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21857     
21858     // defaults : white / black...
21859     this.applyBlacklists();
21860     
21861     
21862     
21863 };
21864
21865
21866 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21867
21868
21869      /**
21870      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21871      */
21872     
21873     owner : false,
21874     
21875      /**
21876      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21877      *                        Roo.resizable.
21878      */
21879     resizable : false,
21880      /**
21881      * @cfg {Number} height (in pixels)
21882      */   
21883     height: 300,
21884    /**
21885      * @cfg {Number} width (in pixels)
21886      */   
21887     width: 500,
21888     
21889     /**
21890      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21891      * 
21892      */
21893     stylesheets: false,
21894     
21895     // id of frame..
21896     frameId: false,
21897     
21898     // private properties
21899     validationEvent : false,
21900     deferHeight: true,
21901     initialized : false,
21902     activated : false,
21903     sourceEditMode : false,
21904     onFocus : Roo.emptyFn,
21905     iframePad:3,
21906     hideMode:'offsets',
21907     
21908     clearUp: true,
21909     
21910     // blacklist + whitelisted elements..
21911     black: false,
21912     white: false,
21913      
21914     bodyCls : '',
21915
21916     /**
21917      * Protected method that will not generally be called directly. It
21918      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21919      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21920      */
21921     getDocMarkup : function(){
21922         // body styles..
21923         var st = '';
21924         
21925         // inherit styels from page...?? 
21926         if (this.stylesheets === false) {
21927             
21928             Roo.get(document.head).select('style').each(function(node) {
21929                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21930             });
21931             
21932             Roo.get(document.head).select('link').each(function(node) { 
21933                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21934             });
21935             
21936         } else if (!this.stylesheets.length) {
21937                 // simple..
21938                 st = '<style type="text/css">' +
21939                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21940                    '</style>';
21941         } else { 
21942             st = '<style type="text/css">' +
21943                     this.stylesheets +
21944                 '</style>';
21945         }
21946         
21947         st +=  '<style type="text/css">' +
21948             'IMG { cursor: pointer } ' +
21949         '</style>';
21950
21951         var cls = 'roo-htmleditor-body';
21952         
21953         if(this.bodyCls.length){
21954             cls += ' ' + this.bodyCls;
21955         }
21956         
21957         return '<html><head>' + st  +
21958             //<style type="text/css">' +
21959             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21960             //'</style>' +
21961             ' </head><body class="' +  cls + '"></body></html>';
21962     },
21963
21964     // private
21965     onRender : function(ct, position)
21966     {
21967         var _t = this;
21968         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21969         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21970         
21971         
21972         this.el.dom.style.border = '0 none';
21973         this.el.dom.setAttribute('tabIndex', -1);
21974         this.el.addClass('x-hidden hide');
21975         
21976         
21977         
21978         if(Roo.isIE){ // fix IE 1px bogus margin
21979             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21980         }
21981        
21982         
21983         this.frameId = Roo.id();
21984         
21985          
21986         
21987         var iframe = this.owner.wrap.createChild({
21988             tag: 'iframe',
21989             cls: 'form-control', // bootstrap..
21990             id: this.frameId,
21991             name: this.frameId,
21992             frameBorder : 'no',
21993             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21994         }, this.el
21995         );
21996         
21997         
21998         this.iframe = iframe.dom;
21999
22000          this.assignDocWin();
22001         
22002         this.doc.designMode = 'on';
22003        
22004         this.doc.open();
22005         this.doc.write(this.getDocMarkup());
22006         this.doc.close();
22007
22008         
22009         var task = { // must defer to wait for browser to be ready
22010             run : function(){
22011                 //console.log("run task?" + this.doc.readyState);
22012                 this.assignDocWin();
22013                 if(this.doc.body || this.doc.readyState == 'complete'){
22014                     try {
22015                         this.doc.designMode="on";
22016                     } catch (e) {
22017                         return;
22018                     }
22019                     Roo.TaskMgr.stop(task);
22020                     this.initEditor.defer(10, this);
22021                 }
22022             },
22023             interval : 10,
22024             duration: 10000,
22025             scope: this
22026         };
22027         Roo.TaskMgr.start(task);
22028
22029     },
22030
22031     // private
22032     onResize : function(w, h)
22033     {
22034          Roo.log('resize: ' +w + ',' + h );
22035         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22036         if(!this.iframe){
22037             return;
22038         }
22039         if(typeof w == 'number'){
22040             
22041             this.iframe.style.width = w + 'px';
22042         }
22043         if(typeof h == 'number'){
22044             
22045             this.iframe.style.height = h + 'px';
22046             if(this.doc){
22047                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22048             }
22049         }
22050         
22051     },
22052
22053     /**
22054      * Toggles the editor between standard and source edit mode.
22055      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22056      */
22057     toggleSourceEdit : function(sourceEditMode){
22058         
22059         this.sourceEditMode = sourceEditMode === true;
22060         
22061         if(this.sourceEditMode){
22062  
22063             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22064             
22065         }else{
22066             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22067             //this.iframe.className = '';
22068             this.deferFocus();
22069         }
22070         //this.setSize(this.owner.wrap.getSize());
22071         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22072     },
22073
22074     
22075   
22076
22077     /**
22078      * Protected method that will not generally be called directly. If you need/want
22079      * custom HTML cleanup, this is the method you should override.
22080      * @param {String} html The HTML to be cleaned
22081      * return {String} The cleaned HTML
22082      */
22083     cleanHtml : function(html){
22084         html = String(html);
22085         if(html.length > 5){
22086             if(Roo.isSafari){ // strip safari nonsense
22087                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22088             }
22089         }
22090         if(html == '&nbsp;'){
22091             html = '';
22092         }
22093         return html;
22094     },
22095
22096     /**
22097      * HTML Editor -> Textarea
22098      * Protected method that will not generally be called directly. Syncs the contents
22099      * of the editor iframe with the textarea.
22100      */
22101     syncValue : function(){
22102         if(this.initialized){
22103             var bd = (this.doc.body || this.doc.documentElement);
22104             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22105             var html = bd.innerHTML;
22106             if(Roo.isSafari){
22107                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22108                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22109                 if(m && m[1]){
22110                     html = '<div style="'+m[0]+'">' + html + '</div>';
22111                 }
22112             }
22113             html = this.cleanHtml(html);
22114             // fix up the special chars.. normaly like back quotes in word...
22115             // however we do not want to do this with chinese..
22116             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22117                 var cc = b.charCodeAt();
22118                 if (
22119                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22120                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22121                     (cc >= 0xf900 && cc < 0xfb00 )
22122                 ) {
22123                         return b;
22124                 }
22125                 return "&#"+cc+";" 
22126             });
22127             if(this.owner.fireEvent('beforesync', this, html) !== false){
22128                 this.el.dom.value = html;
22129                 this.owner.fireEvent('sync', this, html);
22130             }
22131         }
22132     },
22133
22134     /**
22135      * Protected method that will not generally be called directly. Pushes the value of the textarea
22136      * into the iframe editor.
22137      */
22138     pushValue : function(){
22139         if(this.initialized){
22140             var v = this.el.dom.value.trim();
22141             
22142 //            if(v.length < 1){
22143 //                v = '&#160;';
22144 //            }
22145             
22146             if(this.owner.fireEvent('beforepush', this, v) !== false){
22147                 var d = (this.doc.body || this.doc.documentElement);
22148                 d.innerHTML = v;
22149                 this.cleanUpPaste();
22150                 this.el.dom.value = d.innerHTML;
22151                 this.owner.fireEvent('push', this, v);
22152             }
22153         }
22154     },
22155
22156     // private
22157     deferFocus : function(){
22158         this.focus.defer(10, this);
22159     },
22160
22161     // doc'ed in Field
22162     focus : function(){
22163         if(this.win && !this.sourceEditMode){
22164             this.win.focus();
22165         }else{
22166             this.el.focus();
22167         }
22168     },
22169     
22170     assignDocWin: function()
22171     {
22172         var iframe = this.iframe;
22173         
22174          if(Roo.isIE){
22175             this.doc = iframe.contentWindow.document;
22176             this.win = iframe.contentWindow;
22177         } else {
22178 //            if (!Roo.get(this.frameId)) {
22179 //                return;
22180 //            }
22181 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22182 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22183             
22184             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22185                 return;
22186             }
22187             
22188             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22189             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22190         }
22191     },
22192     
22193     // private
22194     initEditor : function(){
22195         //console.log("INIT EDITOR");
22196         this.assignDocWin();
22197         
22198         
22199         
22200         this.doc.designMode="on";
22201         this.doc.open();
22202         this.doc.write(this.getDocMarkup());
22203         this.doc.close();
22204         
22205         var dbody = (this.doc.body || this.doc.documentElement);
22206         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22207         // this copies styles from the containing element into thsi one..
22208         // not sure why we need all of this..
22209         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22210         
22211         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22212         //ss['background-attachment'] = 'fixed'; // w3c
22213         dbody.bgProperties = 'fixed'; // ie
22214         //Roo.DomHelper.applyStyles(dbody, ss);
22215         Roo.EventManager.on(this.doc, {
22216             //'mousedown': this.onEditorEvent,
22217             'mouseup': this.onEditorEvent,
22218             'dblclick': this.onEditorEvent,
22219             'click': this.onEditorEvent,
22220             'keyup': this.onEditorEvent,
22221             buffer:100,
22222             scope: this
22223         });
22224         if(Roo.isGecko){
22225             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22226         }
22227         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22228             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22229         }
22230         this.initialized = true;
22231
22232         this.owner.fireEvent('initialize', this);
22233         this.pushValue();
22234     },
22235
22236     // private
22237     onDestroy : function(){
22238         
22239         
22240         
22241         if(this.rendered){
22242             
22243             //for (var i =0; i < this.toolbars.length;i++) {
22244             //    // fixme - ask toolbars for heights?
22245             //    this.toolbars[i].onDestroy();
22246            // }
22247             
22248             //this.wrap.dom.innerHTML = '';
22249             //this.wrap.remove();
22250         }
22251     },
22252
22253     // private
22254     onFirstFocus : function(){
22255         
22256         this.assignDocWin();
22257         
22258         
22259         this.activated = true;
22260          
22261     
22262         if(Roo.isGecko){ // prevent silly gecko errors
22263             this.win.focus();
22264             var s = this.win.getSelection();
22265             if(!s.focusNode || s.focusNode.nodeType != 3){
22266                 var r = s.getRangeAt(0);
22267                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22268                 r.collapse(true);
22269                 this.deferFocus();
22270             }
22271             try{
22272                 this.execCmd('useCSS', true);
22273                 this.execCmd('styleWithCSS', false);
22274             }catch(e){}
22275         }
22276         this.owner.fireEvent('activate', this);
22277     },
22278
22279     // private
22280     adjustFont: function(btn){
22281         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22282         //if(Roo.isSafari){ // safari
22283         //    adjust *= 2;
22284        // }
22285         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22286         if(Roo.isSafari){ // safari
22287             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22288             v =  (v < 10) ? 10 : v;
22289             v =  (v > 48) ? 48 : v;
22290             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22291             
22292         }
22293         
22294         
22295         v = Math.max(1, v+adjust);
22296         
22297         this.execCmd('FontSize', v  );
22298     },
22299
22300     onEditorEvent : function(e)
22301     {
22302         this.owner.fireEvent('editorevent', this, e);
22303       //  this.updateToolbar();
22304         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22305     },
22306
22307     insertTag : function(tg)
22308     {
22309         // could be a bit smarter... -> wrap the current selected tRoo..
22310         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22311             
22312             range = this.createRange(this.getSelection());
22313             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22314             wrappingNode.appendChild(range.extractContents());
22315             range.insertNode(wrappingNode);
22316
22317             return;
22318             
22319             
22320             
22321         }
22322         this.execCmd("formatblock",   tg);
22323         
22324     },
22325     
22326     insertText : function(txt)
22327     {
22328         
22329         
22330         var range = this.createRange();
22331         range.deleteContents();
22332                //alert(Sender.getAttribute('label'));
22333                
22334         range.insertNode(this.doc.createTextNode(txt));
22335     } ,
22336     
22337      
22338
22339     /**
22340      * Executes a Midas editor command on the editor document and performs necessary focus and
22341      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22342      * @param {String} cmd The Midas command
22343      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22344      */
22345     relayCmd : function(cmd, value){
22346         this.win.focus();
22347         this.execCmd(cmd, value);
22348         this.owner.fireEvent('editorevent', this);
22349         //this.updateToolbar();
22350         this.owner.deferFocus();
22351     },
22352
22353     /**
22354      * Executes a Midas editor command directly on the editor document.
22355      * For visual commands, you should use {@link #relayCmd} instead.
22356      * <b>This should only be called after the editor is initialized.</b>
22357      * @param {String} cmd The Midas command
22358      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22359      */
22360     execCmd : function(cmd, value){
22361         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22362         this.syncValue();
22363     },
22364  
22365  
22366    
22367     /**
22368      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22369      * to insert tRoo.
22370      * @param {String} text | dom node.. 
22371      */
22372     insertAtCursor : function(text)
22373     {
22374         
22375         if(!this.activated){
22376             return;
22377         }
22378         /*
22379         if(Roo.isIE){
22380             this.win.focus();
22381             var r = this.doc.selection.createRange();
22382             if(r){
22383                 r.collapse(true);
22384                 r.pasteHTML(text);
22385                 this.syncValue();
22386                 this.deferFocus();
22387             
22388             }
22389             return;
22390         }
22391         */
22392         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22393             this.win.focus();
22394             
22395             
22396             // from jquery ui (MIT licenced)
22397             var range, node;
22398             var win = this.win;
22399             
22400             if (win.getSelection && win.getSelection().getRangeAt) {
22401                 range = win.getSelection().getRangeAt(0);
22402                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22403                 range.insertNode(node);
22404             } else if (win.document.selection && win.document.selection.createRange) {
22405                 // no firefox support
22406                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22407                 win.document.selection.createRange().pasteHTML(txt);
22408             } else {
22409                 // no firefox support
22410                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22411                 this.execCmd('InsertHTML', txt);
22412             } 
22413             
22414             this.syncValue();
22415             
22416             this.deferFocus();
22417         }
22418     },
22419  // private
22420     mozKeyPress : function(e){
22421         if(e.ctrlKey){
22422             var c = e.getCharCode(), cmd;
22423           
22424             if(c > 0){
22425                 c = String.fromCharCode(c).toLowerCase();
22426                 switch(c){
22427                     case 'b':
22428                         cmd = 'bold';
22429                         break;
22430                     case 'i':
22431                         cmd = 'italic';
22432                         break;
22433                     
22434                     case 'u':
22435                         cmd = 'underline';
22436                         break;
22437                     
22438                     case 'v':
22439                         this.cleanUpPaste.defer(100, this);
22440                         return;
22441                         
22442                 }
22443                 if(cmd){
22444                     this.win.focus();
22445                     this.execCmd(cmd);
22446                     this.deferFocus();
22447                     e.preventDefault();
22448                 }
22449                 
22450             }
22451         }
22452     },
22453
22454     // private
22455     fixKeys : function(){ // load time branching for fastest keydown performance
22456         if(Roo.isIE){
22457             return function(e){
22458                 var k = e.getKey(), r;
22459                 if(k == e.TAB){
22460                     e.stopEvent();
22461                     r = this.doc.selection.createRange();
22462                     if(r){
22463                         r.collapse(true);
22464                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22465                         this.deferFocus();
22466                     }
22467                     return;
22468                 }
22469                 
22470                 if(k == e.ENTER){
22471                     r = this.doc.selection.createRange();
22472                     if(r){
22473                         var target = r.parentElement();
22474                         if(!target || target.tagName.toLowerCase() != 'li'){
22475                             e.stopEvent();
22476                             r.pasteHTML('<br />');
22477                             r.collapse(false);
22478                             r.select();
22479                         }
22480                     }
22481                 }
22482                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22483                     this.cleanUpPaste.defer(100, this);
22484                     return;
22485                 }
22486                 
22487                 
22488             };
22489         }else if(Roo.isOpera){
22490             return function(e){
22491                 var k = e.getKey();
22492                 if(k == e.TAB){
22493                     e.stopEvent();
22494                     this.win.focus();
22495                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22496                     this.deferFocus();
22497                 }
22498                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22499                     this.cleanUpPaste.defer(100, this);
22500                     return;
22501                 }
22502                 
22503             };
22504         }else if(Roo.isSafari){
22505             return function(e){
22506                 var k = e.getKey();
22507                 
22508                 if(k == e.TAB){
22509                     e.stopEvent();
22510                     this.execCmd('InsertText','\t');
22511                     this.deferFocus();
22512                     return;
22513                 }
22514                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22515                     this.cleanUpPaste.defer(100, this);
22516                     return;
22517                 }
22518                 
22519              };
22520         }
22521     }(),
22522     
22523     getAllAncestors: function()
22524     {
22525         var p = this.getSelectedNode();
22526         var a = [];
22527         if (!p) {
22528             a.push(p); // push blank onto stack..
22529             p = this.getParentElement();
22530         }
22531         
22532         
22533         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22534             a.push(p);
22535             p = p.parentNode;
22536         }
22537         a.push(this.doc.body);
22538         return a;
22539     },
22540     lastSel : false,
22541     lastSelNode : false,
22542     
22543     
22544     getSelection : function() 
22545     {
22546         this.assignDocWin();
22547         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22548     },
22549     
22550     getSelectedNode: function() 
22551     {
22552         // this may only work on Gecko!!!
22553         
22554         // should we cache this!!!!
22555         
22556         
22557         
22558          
22559         var range = this.createRange(this.getSelection()).cloneRange();
22560         
22561         if (Roo.isIE) {
22562             var parent = range.parentElement();
22563             while (true) {
22564                 var testRange = range.duplicate();
22565                 testRange.moveToElementText(parent);
22566                 if (testRange.inRange(range)) {
22567                     break;
22568                 }
22569                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22570                     break;
22571                 }
22572                 parent = parent.parentElement;
22573             }
22574             return parent;
22575         }
22576         
22577         // is ancestor a text element.
22578         var ac =  range.commonAncestorContainer;
22579         if (ac.nodeType == 3) {
22580             ac = ac.parentNode;
22581         }
22582         
22583         var ar = ac.childNodes;
22584          
22585         var nodes = [];
22586         var other_nodes = [];
22587         var has_other_nodes = false;
22588         for (var i=0;i<ar.length;i++) {
22589             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22590                 continue;
22591             }
22592             // fullly contained node.
22593             
22594             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22595                 nodes.push(ar[i]);
22596                 continue;
22597             }
22598             
22599             // probably selected..
22600             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22601                 other_nodes.push(ar[i]);
22602                 continue;
22603             }
22604             // outer..
22605             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22606                 continue;
22607             }
22608             
22609             
22610             has_other_nodes = true;
22611         }
22612         if (!nodes.length && other_nodes.length) {
22613             nodes= other_nodes;
22614         }
22615         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22616             return false;
22617         }
22618         
22619         return nodes[0];
22620     },
22621     createRange: function(sel)
22622     {
22623         // this has strange effects when using with 
22624         // top toolbar - not sure if it's a great idea.
22625         //this.editor.contentWindow.focus();
22626         if (typeof sel != "undefined") {
22627             try {
22628                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22629             } catch(e) {
22630                 return this.doc.createRange();
22631             }
22632         } else {
22633             return this.doc.createRange();
22634         }
22635     },
22636     getParentElement: function()
22637     {
22638         
22639         this.assignDocWin();
22640         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22641         
22642         var range = this.createRange(sel);
22643          
22644         try {
22645             var p = range.commonAncestorContainer;
22646             while (p.nodeType == 3) { // text node
22647                 p = p.parentNode;
22648             }
22649             return p;
22650         } catch (e) {
22651             return null;
22652         }
22653     
22654     },
22655     /***
22656      *
22657      * Range intersection.. the hard stuff...
22658      *  '-1' = before
22659      *  '0' = hits..
22660      *  '1' = after.
22661      *         [ -- selected range --- ]
22662      *   [fail]                        [fail]
22663      *
22664      *    basically..
22665      *      if end is before start or  hits it. fail.
22666      *      if start is after end or hits it fail.
22667      *
22668      *   if either hits (but other is outside. - then it's not 
22669      *   
22670      *    
22671      **/
22672     
22673     
22674     // @see http://www.thismuchiknow.co.uk/?p=64.
22675     rangeIntersectsNode : function(range, node)
22676     {
22677         var nodeRange = node.ownerDocument.createRange();
22678         try {
22679             nodeRange.selectNode(node);
22680         } catch (e) {
22681             nodeRange.selectNodeContents(node);
22682         }
22683     
22684         var rangeStartRange = range.cloneRange();
22685         rangeStartRange.collapse(true);
22686     
22687         var rangeEndRange = range.cloneRange();
22688         rangeEndRange.collapse(false);
22689     
22690         var nodeStartRange = nodeRange.cloneRange();
22691         nodeStartRange.collapse(true);
22692     
22693         var nodeEndRange = nodeRange.cloneRange();
22694         nodeEndRange.collapse(false);
22695     
22696         return rangeStartRange.compareBoundaryPoints(
22697                  Range.START_TO_START, nodeEndRange) == -1 &&
22698                rangeEndRange.compareBoundaryPoints(
22699                  Range.START_TO_START, nodeStartRange) == 1;
22700         
22701          
22702     },
22703     rangeCompareNode : function(range, node)
22704     {
22705         var nodeRange = node.ownerDocument.createRange();
22706         try {
22707             nodeRange.selectNode(node);
22708         } catch (e) {
22709             nodeRange.selectNodeContents(node);
22710         }
22711         
22712         
22713         range.collapse(true);
22714     
22715         nodeRange.collapse(true);
22716      
22717         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22718         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22719          
22720         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22721         
22722         var nodeIsBefore   =  ss == 1;
22723         var nodeIsAfter    = ee == -1;
22724         
22725         if (nodeIsBefore && nodeIsAfter) {
22726             return 0; // outer
22727         }
22728         if (!nodeIsBefore && nodeIsAfter) {
22729             return 1; //right trailed.
22730         }
22731         
22732         if (nodeIsBefore && !nodeIsAfter) {
22733             return 2;  // left trailed.
22734         }
22735         // fully contined.
22736         return 3;
22737     },
22738
22739     // private? - in a new class?
22740     cleanUpPaste :  function()
22741     {
22742         // cleans up the whole document..
22743         Roo.log('cleanuppaste');
22744         
22745         this.cleanUpChildren(this.doc.body);
22746         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22747         if (clean != this.doc.body.innerHTML) {
22748             this.doc.body.innerHTML = clean;
22749         }
22750         
22751     },
22752     
22753     cleanWordChars : function(input) {// change the chars to hex code
22754         var he = Roo.HtmlEditorCore;
22755         
22756         var output = input;
22757         Roo.each(he.swapCodes, function(sw) { 
22758             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22759             
22760             output = output.replace(swapper, sw[1]);
22761         });
22762         
22763         return output;
22764     },
22765     
22766     
22767     cleanUpChildren : function (n)
22768     {
22769         if (!n.childNodes.length) {
22770             return;
22771         }
22772         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22773            this.cleanUpChild(n.childNodes[i]);
22774         }
22775     },
22776     
22777     
22778         
22779     
22780     cleanUpChild : function (node)
22781     {
22782         var ed = this;
22783         //console.log(node);
22784         if (node.nodeName == "#text") {
22785             // clean up silly Windows -- stuff?
22786             return; 
22787         }
22788         if (node.nodeName == "#comment") {
22789             node.parentNode.removeChild(node);
22790             // clean up silly Windows -- stuff?
22791             return; 
22792         }
22793         var lcname = node.tagName.toLowerCase();
22794         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22795         // whitelist of tags..
22796         
22797         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22798             // remove node.
22799             node.parentNode.removeChild(node);
22800             return;
22801             
22802         }
22803         
22804         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22805         
22806         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22807         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22808         
22809         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22810         //    remove_keep_children = true;
22811         //}
22812         
22813         if (remove_keep_children) {
22814             this.cleanUpChildren(node);
22815             // inserts everything just before this node...
22816             while (node.childNodes.length) {
22817                 var cn = node.childNodes[0];
22818                 node.removeChild(cn);
22819                 node.parentNode.insertBefore(cn, node);
22820             }
22821             node.parentNode.removeChild(node);
22822             return;
22823         }
22824         
22825         if (!node.attributes || !node.attributes.length) {
22826             this.cleanUpChildren(node);
22827             return;
22828         }
22829         
22830         function cleanAttr(n,v)
22831         {
22832             
22833             if (v.match(/^\./) || v.match(/^\//)) {
22834                 return;
22835             }
22836             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22837                 return;
22838             }
22839             if (v.match(/^#/)) {
22840                 return;
22841             }
22842 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22843             node.removeAttribute(n);
22844             
22845         }
22846         
22847         var cwhite = this.cwhite;
22848         var cblack = this.cblack;
22849             
22850         function cleanStyle(n,v)
22851         {
22852             if (v.match(/expression/)) { //XSS?? should we even bother..
22853                 node.removeAttribute(n);
22854                 return;
22855             }
22856             
22857             var parts = v.split(/;/);
22858             var clean = [];
22859             
22860             Roo.each(parts, function(p) {
22861                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22862                 if (!p.length) {
22863                     return true;
22864                 }
22865                 var l = p.split(':').shift().replace(/\s+/g,'');
22866                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22867                 
22868                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22869 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22870                     //node.removeAttribute(n);
22871                     return true;
22872                 }
22873                 //Roo.log()
22874                 // only allow 'c whitelisted system attributes'
22875                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22876 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22877                     //node.removeAttribute(n);
22878                     return true;
22879                 }
22880                 
22881                 
22882                  
22883                 
22884                 clean.push(p);
22885                 return true;
22886             });
22887             if (clean.length) { 
22888                 node.setAttribute(n, clean.join(';'));
22889             } else {
22890                 node.removeAttribute(n);
22891             }
22892             
22893         }
22894         
22895         
22896         for (var i = node.attributes.length-1; i > -1 ; i--) {
22897             var a = node.attributes[i];
22898             //console.log(a);
22899             
22900             if (a.name.toLowerCase().substr(0,2)=='on')  {
22901                 node.removeAttribute(a.name);
22902                 continue;
22903             }
22904             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22905                 node.removeAttribute(a.name);
22906                 continue;
22907             }
22908             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22909                 cleanAttr(a.name,a.value); // fixme..
22910                 continue;
22911             }
22912             if (a.name == 'style') {
22913                 cleanStyle(a.name,a.value);
22914                 continue;
22915             }
22916             /// clean up MS crap..
22917             // tecnically this should be a list of valid class'es..
22918             
22919             
22920             if (a.name == 'class') {
22921                 if (a.value.match(/^Mso/)) {
22922                     node.className = '';
22923                 }
22924                 
22925                 if (a.value.match(/^body$/)) {
22926                     node.className = '';
22927                 }
22928                 continue;
22929             }
22930             
22931             // style cleanup!?
22932             // class cleanup?
22933             
22934         }
22935         
22936         
22937         this.cleanUpChildren(node);
22938         
22939         
22940     },
22941     
22942     /**
22943      * Clean up MS wordisms...
22944      */
22945     cleanWord : function(node)
22946     {
22947         
22948         
22949         if (!node) {
22950             this.cleanWord(this.doc.body);
22951             return;
22952         }
22953         if (node.nodeName == "#text") {
22954             // clean up silly Windows -- stuff?
22955             return; 
22956         }
22957         if (node.nodeName == "#comment") {
22958             node.parentNode.removeChild(node);
22959             // clean up silly Windows -- stuff?
22960             return; 
22961         }
22962         
22963         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22964             node.parentNode.removeChild(node);
22965             return;
22966         }
22967         
22968         // remove - but keep children..
22969         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22970             while (node.childNodes.length) {
22971                 var cn = node.childNodes[0];
22972                 node.removeChild(cn);
22973                 node.parentNode.insertBefore(cn, node);
22974             }
22975             node.parentNode.removeChild(node);
22976             this.iterateChildren(node, this.cleanWord);
22977             return;
22978         }
22979         // clean styles
22980         if (node.className.length) {
22981             
22982             var cn = node.className.split(/\W+/);
22983             var cna = [];
22984             Roo.each(cn, function(cls) {
22985                 if (cls.match(/Mso[a-zA-Z]+/)) {
22986                     return;
22987                 }
22988                 cna.push(cls);
22989             });
22990             node.className = cna.length ? cna.join(' ') : '';
22991             if (!cna.length) {
22992                 node.removeAttribute("class");
22993             }
22994         }
22995         
22996         if (node.hasAttribute("lang")) {
22997             node.removeAttribute("lang");
22998         }
22999         
23000         if (node.hasAttribute("style")) {
23001             
23002             var styles = node.getAttribute("style").split(";");
23003             var nstyle = [];
23004             Roo.each(styles, function(s) {
23005                 if (!s.match(/:/)) {
23006                     return;
23007                 }
23008                 var kv = s.split(":");
23009                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23010                     return;
23011                 }
23012                 // what ever is left... we allow.
23013                 nstyle.push(s);
23014             });
23015             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23016             if (!nstyle.length) {
23017                 node.removeAttribute('style');
23018             }
23019         }
23020         this.iterateChildren(node, this.cleanWord);
23021         
23022         
23023         
23024     },
23025     /**
23026      * iterateChildren of a Node, calling fn each time, using this as the scole..
23027      * @param {DomNode} node node to iterate children of.
23028      * @param {Function} fn method of this class to call on each item.
23029      */
23030     iterateChildren : function(node, fn)
23031     {
23032         if (!node.childNodes.length) {
23033                 return;
23034         }
23035         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23036            fn.call(this, node.childNodes[i])
23037         }
23038     },
23039     
23040     
23041     /**
23042      * cleanTableWidths.
23043      *
23044      * Quite often pasting from word etc.. results in tables with column and widths.
23045      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23046      *
23047      */
23048     cleanTableWidths : function(node)
23049     {
23050          
23051          
23052         if (!node) {
23053             this.cleanTableWidths(this.doc.body);
23054             return;
23055         }
23056         
23057         // ignore list...
23058         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23059             return; 
23060         }
23061         Roo.log(node.tagName);
23062         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23063             this.iterateChildren(node, this.cleanTableWidths);
23064             return;
23065         }
23066         if (node.hasAttribute('width')) {
23067             node.removeAttribute('width');
23068         }
23069         
23070          
23071         if (node.hasAttribute("style")) {
23072             // pretty basic...
23073             
23074             var styles = node.getAttribute("style").split(";");
23075             var nstyle = [];
23076             Roo.each(styles, function(s) {
23077                 if (!s.match(/:/)) {
23078                     return;
23079                 }
23080                 var kv = s.split(":");
23081                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23082                     return;
23083                 }
23084                 // what ever is left... we allow.
23085                 nstyle.push(s);
23086             });
23087             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23088             if (!nstyle.length) {
23089                 node.removeAttribute('style');
23090             }
23091         }
23092         
23093         this.iterateChildren(node, this.cleanTableWidths);
23094         
23095         
23096     },
23097     
23098     
23099     
23100     
23101     domToHTML : function(currentElement, depth, nopadtext) {
23102         
23103         depth = depth || 0;
23104         nopadtext = nopadtext || false;
23105     
23106         if (!currentElement) {
23107             return this.domToHTML(this.doc.body);
23108         }
23109         
23110         //Roo.log(currentElement);
23111         var j;
23112         var allText = false;
23113         var nodeName = currentElement.nodeName;
23114         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23115         
23116         if  (nodeName == '#text') {
23117             
23118             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23119         }
23120         
23121         
23122         var ret = '';
23123         if (nodeName != 'BODY') {
23124              
23125             var i = 0;
23126             // Prints the node tagName, such as <A>, <IMG>, etc
23127             if (tagName) {
23128                 var attr = [];
23129                 for(i = 0; i < currentElement.attributes.length;i++) {
23130                     // quoting?
23131                     var aname = currentElement.attributes.item(i).name;
23132                     if (!currentElement.attributes.item(i).value.length) {
23133                         continue;
23134                     }
23135                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23136                 }
23137                 
23138                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23139             } 
23140             else {
23141                 
23142                 // eack
23143             }
23144         } else {
23145             tagName = false;
23146         }
23147         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23148             return ret;
23149         }
23150         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23151             nopadtext = true;
23152         }
23153         
23154         
23155         // Traverse the tree
23156         i = 0;
23157         var currentElementChild = currentElement.childNodes.item(i);
23158         var allText = true;
23159         var innerHTML  = '';
23160         lastnode = '';
23161         while (currentElementChild) {
23162             // Formatting code (indent the tree so it looks nice on the screen)
23163             var nopad = nopadtext;
23164             if (lastnode == 'SPAN') {
23165                 nopad  = true;
23166             }
23167             // text
23168             if  (currentElementChild.nodeName == '#text') {
23169                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23170                 toadd = nopadtext ? toadd : toadd.trim();
23171                 if (!nopad && toadd.length > 80) {
23172                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23173                 }
23174                 innerHTML  += toadd;
23175                 
23176                 i++;
23177                 currentElementChild = currentElement.childNodes.item(i);
23178                 lastNode = '';
23179                 continue;
23180             }
23181             allText = false;
23182             
23183             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23184                 
23185             // Recursively traverse the tree structure of the child node
23186             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23187             lastnode = currentElementChild.nodeName;
23188             i++;
23189             currentElementChild=currentElement.childNodes.item(i);
23190         }
23191         
23192         ret += innerHTML;
23193         
23194         if (!allText) {
23195                 // The remaining code is mostly for formatting the tree
23196             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23197         }
23198         
23199         
23200         if (tagName) {
23201             ret+= "</"+tagName+">";
23202         }
23203         return ret;
23204         
23205     },
23206         
23207     applyBlacklists : function()
23208     {
23209         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23210         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23211         
23212         this.white = [];
23213         this.black = [];
23214         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23215             if (b.indexOf(tag) > -1) {
23216                 return;
23217             }
23218             this.white.push(tag);
23219             
23220         }, this);
23221         
23222         Roo.each(w, function(tag) {
23223             if (b.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             if (this.white.indexOf(tag) > -1) {
23227                 return;
23228             }
23229             this.white.push(tag);
23230             
23231         }, this);
23232         
23233         
23234         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23235             if (w.indexOf(tag) > -1) {
23236                 return;
23237             }
23238             this.black.push(tag);
23239             
23240         }, this);
23241         
23242         Roo.each(b, function(tag) {
23243             if (w.indexOf(tag) > -1) {
23244                 return;
23245             }
23246             if (this.black.indexOf(tag) > -1) {
23247                 return;
23248             }
23249             this.black.push(tag);
23250             
23251         }, this);
23252         
23253         
23254         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23255         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23256         
23257         this.cwhite = [];
23258         this.cblack = [];
23259         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23260             if (b.indexOf(tag) > -1) {
23261                 return;
23262             }
23263             this.cwhite.push(tag);
23264             
23265         }, this);
23266         
23267         Roo.each(w, function(tag) {
23268             if (b.indexOf(tag) > -1) {
23269                 return;
23270             }
23271             if (this.cwhite.indexOf(tag) > -1) {
23272                 return;
23273             }
23274             this.cwhite.push(tag);
23275             
23276         }, this);
23277         
23278         
23279         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23280             if (w.indexOf(tag) > -1) {
23281                 return;
23282             }
23283             this.cblack.push(tag);
23284             
23285         }, this);
23286         
23287         Roo.each(b, function(tag) {
23288             if (w.indexOf(tag) > -1) {
23289                 return;
23290             }
23291             if (this.cblack.indexOf(tag) > -1) {
23292                 return;
23293             }
23294             this.cblack.push(tag);
23295             
23296         }, this);
23297     },
23298     
23299     setStylesheets : function(stylesheets)
23300     {
23301         if(typeof(stylesheets) == 'string'){
23302             Roo.get(this.iframe.contentDocument.head).createChild({
23303                 tag : 'link',
23304                 rel : 'stylesheet',
23305                 type : 'text/css',
23306                 href : stylesheets
23307             });
23308             
23309             return;
23310         }
23311         var _this = this;
23312      
23313         Roo.each(stylesheets, function(s) {
23314             if(!s.length){
23315                 return;
23316             }
23317             
23318             Roo.get(_this.iframe.contentDocument.head).createChild({
23319                 tag : 'link',
23320                 rel : 'stylesheet',
23321                 type : 'text/css',
23322                 href : s
23323             });
23324         });
23325
23326         
23327     },
23328     
23329     removeStylesheets : function()
23330     {
23331         var _this = this;
23332         
23333         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23334             s.remove();
23335         });
23336     },
23337     
23338     setStyle : function(style)
23339     {
23340         Roo.get(this.iframe.contentDocument.head).createChild({
23341             tag : 'style',
23342             type : 'text/css',
23343             html : style
23344         });
23345
23346         return;
23347     }
23348     
23349     // hide stuff that is not compatible
23350     /**
23351      * @event blur
23352      * @hide
23353      */
23354     /**
23355      * @event change
23356      * @hide
23357      */
23358     /**
23359      * @event focus
23360      * @hide
23361      */
23362     /**
23363      * @event specialkey
23364      * @hide
23365      */
23366     /**
23367      * @cfg {String} fieldClass @hide
23368      */
23369     /**
23370      * @cfg {String} focusClass @hide
23371      */
23372     /**
23373      * @cfg {String} autoCreate @hide
23374      */
23375     /**
23376      * @cfg {String} inputType @hide
23377      */
23378     /**
23379      * @cfg {String} invalidClass @hide
23380      */
23381     /**
23382      * @cfg {String} invalidText @hide
23383      */
23384     /**
23385      * @cfg {String} msgFx @hide
23386      */
23387     /**
23388      * @cfg {String} validateOnBlur @hide
23389      */
23390 });
23391
23392 Roo.HtmlEditorCore.white = [
23393         'area', 'br', 'img', 'input', 'hr', 'wbr',
23394         
23395        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23396        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23397        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23398        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23399        'table',   'ul',         'xmp', 
23400        
23401        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23402       'thead',   'tr', 
23403      
23404       'dir', 'menu', 'ol', 'ul', 'dl',
23405        
23406       'embed',  'object'
23407 ];
23408
23409
23410 Roo.HtmlEditorCore.black = [
23411     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23412         'applet', // 
23413         'base',   'basefont', 'bgsound', 'blink',  'body', 
23414         'frame',  'frameset', 'head',    'html',   'ilayer', 
23415         'iframe', 'layer',  'link',     'meta',    'object',   
23416         'script', 'style' ,'title',  'xml' // clean later..
23417 ];
23418 Roo.HtmlEditorCore.clean = [
23419     'script', 'style', 'title', 'xml'
23420 ];
23421 Roo.HtmlEditorCore.remove = [
23422     'font'
23423 ];
23424 // attributes..
23425
23426 Roo.HtmlEditorCore.ablack = [
23427     'on'
23428 ];
23429     
23430 Roo.HtmlEditorCore.aclean = [ 
23431     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23432 ];
23433
23434 // protocols..
23435 Roo.HtmlEditorCore.pwhite= [
23436         'http',  'https',  'mailto'
23437 ];
23438
23439 // white listed style attributes.
23440 Roo.HtmlEditorCore.cwhite= [
23441       //  'text-align', /// default is to allow most things..
23442       
23443          
23444 //        'font-size'//??
23445 ];
23446
23447 // black listed style attributes.
23448 Roo.HtmlEditorCore.cblack= [
23449       //  'font-size' -- this can be set by the project 
23450 ];
23451
23452
23453 Roo.HtmlEditorCore.swapCodes   =[ 
23454     [    8211, "--" ], 
23455     [    8212, "--" ], 
23456     [    8216,  "'" ],  
23457     [    8217, "'" ],  
23458     [    8220, '"' ],  
23459     [    8221, '"' ],  
23460     [    8226, "*" ],  
23461     [    8230, "..." ]
23462 ]; 
23463
23464     /*
23465  * - LGPL
23466  *
23467  * HtmlEditor
23468  * 
23469  */
23470
23471 /**
23472  * @class Roo.bootstrap.HtmlEditor
23473  * @extends Roo.bootstrap.TextArea
23474  * Bootstrap HtmlEditor class
23475
23476  * @constructor
23477  * Create a new HtmlEditor
23478  * @param {Object} config The config object
23479  */
23480
23481 Roo.bootstrap.HtmlEditor = function(config){
23482     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23483     if (!this.toolbars) {
23484         this.toolbars = [];
23485     }
23486     
23487     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23488     this.addEvents({
23489             /**
23490              * @event initialize
23491              * Fires when the editor is fully initialized (including the iframe)
23492              * @param {HtmlEditor} this
23493              */
23494             initialize: true,
23495             /**
23496              * @event activate
23497              * Fires when the editor is first receives the focus. Any insertion must wait
23498              * until after this event.
23499              * @param {HtmlEditor} this
23500              */
23501             activate: true,
23502              /**
23503              * @event beforesync
23504              * Fires before the textarea is updated with content from the editor iframe. Return false
23505              * to cancel the sync.
23506              * @param {HtmlEditor} this
23507              * @param {String} html
23508              */
23509             beforesync: true,
23510              /**
23511              * @event beforepush
23512              * Fires before the iframe editor is updated with content from the textarea. Return false
23513              * to cancel the push.
23514              * @param {HtmlEditor} this
23515              * @param {String} html
23516              */
23517             beforepush: true,
23518              /**
23519              * @event sync
23520              * Fires when the textarea is updated with content from the editor iframe.
23521              * @param {HtmlEditor} this
23522              * @param {String} html
23523              */
23524             sync: true,
23525              /**
23526              * @event push
23527              * Fires when the iframe editor is updated with content from the textarea.
23528              * @param {HtmlEditor} this
23529              * @param {String} html
23530              */
23531             push: true,
23532              /**
23533              * @event editmodechange
23534              * Fires when the editor switches edit modes
23535              * @param {HtmlEditor} this
23536              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23537              */
23538             editmodechange: true,
23539             /**
23540              * @event editorevent
23541              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23542              * @param {HtmlEditor} this
23543              */
23544             editorevent: true,
23545             /**
23546              * @event firstfocus
23547              * Fires when on first focus - needed by toolbars..
23548              * @param {HtmlEditor} this
23549              */
23550             firstfocus: true,
23551             /**
23552              * @event autosave
23553              * Auto save the htmlEditor value as a file into Events
23554              * @param {HtmlEditor} this
23555              */
23556             autosave: true,
23557             /**
23558              * @event savedpreview
23559              * preview the saved version of htmlEditor
23560              * @param {HtmlEditor} this
23561              */
23562             savedpreview: true
23563         });
23564 };
23565
23566
23567 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23568     
23569     
23570       /**
23571      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23572      */
23573     toolbars : false,
23574     
23575      /**
23576     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23577     */
23578     btns : [],
23579    
23580      /**
23581      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23582      *                        Roo.resizable.
23583      */
23584     resizable : false,
23585      /**
23586      * @cfg {Number} height (in pixels)
23587      */   
23588     height: 300,
23589    /**
23590      * @cfg {Number} width (in pixels)
23591      */   
23592     width: false,
23593     
23594     /**
23595      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23596      * 
23597      */
23598     stylesheets: false,
23599     
23600     // id of frame..
23601     frameId: false,
23602     
23603     // private properties
23604     validationEvent : false,
23605     deferHeight: true,
23606     initialized : false,
23607     activated : false,
23608     
23609     onFocus : Roo.emptyFn,
23610     iframePad:3,
23611     hideMode:'offsets',
23612     
23613     tbContainer : false,
23614     
23615     bodyCls : '',
23616     
23617     toolbarContainer :function() {
23618         return this.wrap.select('.x-html-editor-tb',true).first();
23619     },
23620
23621     /**
23622      * Protected method that will not generally be called directly. It
23623      * is called when the editor creates its toolbar. Override this method if you need to
23624      * add custom toolbar buttons.
23625      * @param {HtmlEditor} editor
23626      */
23627     createToolbar : function(){
23628         Roo.log('renewing');
23629         Roo.log("create toolbars");
23630         
23631         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23632         this.toolbars[0].render(this.toolbarContainer());
23633         
23634         return;
23635         
23636 //        if (!editor.toolbars || !editor.toolbars.length) {
23637 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23638 //        }
23639 //        
23640 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23641 //            editor.toolbars[i] = Roo.factory(
23642 //                    typeof(editor.toolbars[i]) == 'string' ?
23643 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23644 //                Roo.bootstrap.HtmlEditor);
23645 //            editor.toolbars[i].init(editor);
23646 //        }
23647     },
23648
23649      
23650     // private
23651     onRender : function(ct, position)
23652     {
23653        // Roo.log("Call onRender: " + this.xtype);
23654         var _t = this;
23655         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23656       
23657         this.wrap = this.inputEl().wrap({
23658             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23659         });
23660         
23661         this.editorcore.onRender(ct, position);
23662          
23663         if (this.resizable) {
23664             this.resizeEl = new Roo.Resizable(this.wrap, {
23665                 pinned : true,
23666                 wrap: true,
23667                 dynamic : true,
23668                 minHeight : this.height,
23669                 height: this.height,
23670                 handles : this.resizable,
23671                 width: this.width,
23672                 listeners : {
23673                     resize : function(r, w, h) {
23674                         _t.onResize(w,h); // -something
23675                     }
23676                 }
23677             });
23678             
23679         }
23680         this.createToolbar(this);
23681        
23682         
23683         if(!this.width && this.resizable){
23684             this.setSize(this.wrap.getSize());
23685         }
23686         if (this.resizeEl) {
23687             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23688             // should trigger onReize..
23689         }
23690         
23691     },
23692
23693     // private
23694     onResize : function(w, h)
23695     {
23696         Roo.log('resize: ' +w + ',' + h );
23697         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23698         var ew = false;
23699         var eh = false;
23700         
23701         if(this.inputEl() ){
23702             if(typeof w == 'number'){
23703                 var aw = w - this.wrap.getFrameWidth('lr');
23704                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23705                 ew = aw;
23706             }
23707             if(typeof h == 'number'){
23708                  var tbh = -11;  // fixme it needs to tool bar size!
23709                 for (var i =0; i < this.toolbars.length;i++) {
23710                     // fixme - ask toolbars for heights?
23711                     tbh += this.toolbars[i].el.getHeight();
23712                     //if (this.toolbars[i].footer) {
23713                     //    tbh += this.toolbars[i].footer.el.getHeight();
23714                     //}
23715                 }
23716               
23717                 
23718                 
23719                 
23720                 
23721                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23722                 ah -= 5; // knock a few pixes off for look..
23723                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23724                 var eh = ah;
23725             }
23726         }
23727         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23728         this.editorcore.onResize(ew,eh);
23729         
23730     },
23731
23732     /**
23733      * Toggles the editor between standard and source edit mode.
23734      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23735      */
23736     toggleSourceEdit : function(sourceEditMode)
23737     {
23738         this.editorcore.toggleSourceEdit(sourceEditMode);
23739         
23740         if(this.editorcore.sourceEditMode){
23741             Roo.log('editor - showing textarea');
23742             
23743 //            Roo.log('in');
23744 //            Roo.log(this.syncValue());
23745             this.syncValue();
23746             this.inputEl().removeClass(['hide', 'x-hidden']);
23747             this.inputEl().dom.removeAttribute('tabIndex');
23748             this.inputEl().focus();
23749         }else{
23750             Roo.log('editor - hiding textarea');
23751 //            Roo.log('out')
23752 //            Roo.log(this.pushValue()); 
23753             this.pushValue();
23754             
23755             this.inputEl().addClass(['hide', 'x-hidden']);
23756             this.inputEl().dom.setAttribute('tabIndex', -1);
23757             //this.deferFocus();
23758         }
23759          
23760         if(this.resizable){
23761             this.setSize(this.wrap.getSize());
23762         }
23763         
23764         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23765     },
23766  
23767     // private (for BoxComponent)
23768     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23769
23770     // private (for BoxComponent)
23771     getResizeEl : function(){
23772         return this.wrap;
23773     },
23774
23775     // private (for BoxComponent)
23776     getPositionEl : function(){
23777         return this.wrap;
23778     },
23779
23780     // private
23781     initEvents : function(){
23782         this.originalValue = this.getValue();
23783     },
23784
23785 //    /**
23786 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23787 //     * @method
23788 //     */
23789 //    markInvalid : Roo.emptyFn,
23790 //    /**
23791 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23792 //     * @method
23793 //     */
23794 //    clearInvalid : Roo.emptyFn,
23795
23796     setValue : function(v){
23797         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23798         this.editorcore.pushValue();
23799     },
23800
23801      
23802     // private
23803     deferFocus : function(){
23804         this.focus.defer(10, this);
23805     },
23806
23807     // doc'ed in Field
23808     focus : function(){
23809         this.editorcore.focus();
23810         
23811     },
23812       
23813
23814     // private
23815     onDestroy : function(){
23816         
23817         
23818         
23819         if(this.rendered){
23820             
23821             for (var i =0; i < this.toolbars.length;i++) {
23822                 // fixme - ask toolbars for heights?
23823                 this.toolbars[i].onDestroy();
23824             }
23825             
23826             this.wrap.dom.innerHTML = '';
23827             this.wrap.remove();
23828         }
23829     },
23830
23831     // private
23832     onFirstFocus : function(){
23833         //Roo.log("onFirstFocus");
23834         this.editorcore.onFirstFocus();
23835          for (var i =0; i < this.toolbars.length;i++) {
23836             this.toolbars[i].onFirstFocus();
23837         }
23838         
23839     },
23840     
23841     // private
23842     syncValue : function()
23843     {   
23844         this.editorcore.syncValue();
23845     },
23846     
23847     pushValue : function()
23848     {   
23849         this.editorcore.pushValue();
23850     }
23851      
23852     
23853     // hide stuff that is not compatible
23854     /**
23855      * @event blur
23856      * @hide
23857      */
23858     /**
23859      * @event change
23860      * @hide
23861      */
23862     /**
23863      * @event focus
23864      * @hide
23865      */
23866     /**
23867      * @event specialkey
23868      * @hide
23869      */
23870     /**
23871      * @cfg {String} fieldClass @hide
23872      */
23873     /**
23874      * @cfg {String} focusClass @hide
23875      */
23876     /**
23877      * @cfg {String} autoCreate @hide
23878      */
23879     /**
23880      * @cfg {String} inputType @hide
23881      */
23882     /**
23883      * @cfg {String} invalidClass @hide
23884      */
23885     /**
23886      * @cfg {String} invalidText @hide
23887      */
23888     /**
23889      * @cfg {String} msgFx @hide
23890      */
23891     /**
23892      * @cfg {String} validateOnBlur @hide
23893      */
23894 });
23895  
23896     
23897    
23898    
23899    
23900       
23901 Roo.namespace('Roo.bootstrap.htmleditor');
23902 /**
23903  * @class Roo.bootstrap.HtmlEditorToolbar1
23904  * Basic Toolbar
23905  * 
23906  * Usage:
23907  *
23908  new Roo.bootstrap.HtmlEditor({
23909     ....
23910     toolbars : [
23911         new Roo.bootstrap.HtmlEditorToolbar1({
23912             disable : { fonts: 1 , format: 1, ..., ... , ...],
23913             btns : [ .... ]
23914         })
23915     }
23916      
23917  * 
23918  * @cfg {Object} disable List of elements to disable..
23919  * @cfg {Array} btns List of additional buttons.
23920  * 
23921  * 
23922  * NEEDS Extra CSS? 
23923  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23924  */
23925  
23926 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23927 {
23928     
23929     Roo.apply(this, config);
23930     
23931     // default disabled, based on 'good practice'..
23932     this.disable = this.disable || {};
23933     Roo.applyIf(this.disable, {
23934         fontSize : true,
23935         colors : true,
23936         specialElements : true
23937     });
23938     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23939     
23940     this.editor = config.editor;
23941     this.editorcore = config.editor.editorcore;
23942     
23943     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23944     
23945     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23946     // dont call parent... till later.
23947 }
23948 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23949      
23950     bar : true,
23951     
23952     editor : false,
23953     editorcore : false,
23954     
23955     
23956     formats : [
23957         "p" ,  
23958         "h1","h2","h3","h4","h5","h6", 
23959         "pre", "code", 
23960         "abbr", "acronym", "address", "cite", "samp", "var",
23961         'div','span'
23962     ],
23963     
23964     onRender : function(ct, position)
23965     {
23966        // Roo.log("Call onRender: " + this.xtype);
23967         
23968        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23969        Roo.log(this.el);
23970        this.el.dom.style.marginBottom = '0';
23971        var _this = this;
23972        var editorcore = this.editorcore;
23973        var editor= this.editor;
23974        
23975        var children = [];
23976        var btn = function(id,cmd , toggle, handler, html){
23977        
23978             var  event = toggle ? 'toggle' : 'click';
23979        
23980             var a = {
23981                 size : 'sm',
23982                 xtype: 'Button',
23983                 xns: Roo.bootstrap,
23984                 //glyphicon : id,
23985                 fa: id,
23986                 cmd : id || cmd,
23987                 enableToggle:toggle !== false,
23988                 html : html || '',
23989                 pressed : toggle ? false : null,
23990                 listeners : {}
23991             };
23992             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23993                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23994             };
23995             children.push(a);
23996             return a;
23997        }
23998        
23999     //    var cb_box = function...
24000         
24001         var style = {
24002                 xtype: 'Button',
24003                 size : 'sm',
24004                 xns: Roo.bootstrap,
24005                 fa : 'font',
24006                 //html : 'submit'
24007                 menu : {
24008                     xtype: 'Menu',
24009                     xns: Roo.bootstrap,
24010                     items:  []
24011                 }
24012         };
24013         Roo.each(this.formats, function(f) {
24014             style.menu.items.push({
24015                 xtype :'MenuItem',
24016                 xns: Roo.bootstrap,
24017                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24018                 tagname : f,
24019                 listeners : {
24020                     click : function()
24021                     {
24022                         editorcore.insertTag(this.tagname);
24023                         editor.focus();
24024                     }
24025                 }
24026                 
24027             });
24028         });
24029         children.push(style);   
24030         
24031         btn('bold',false,true);
24032         btn('italic',false,true);
24033         btn('align-left', 'justifyleft',true);
24034         btn('align-center', 'justifycenter',true);
24035         btn('align-right' , 'justifyright',true);
24036         btn('link', false, false, function(btn) {
24037             //Roo.log("create link?");
24038             var url = prompt(this.createLinkText, this.defaultLinkValue);
24039             if(url && url != 'http:/'+'/'){
24040                 this.editorcore.relayCmd('createlink', url);
24041             }
24042         }),
24043         btn('list','insertunorderedlist',true);
24044         btn('pencil', false,true, function(btn){
24045                 Roo.log(this);
24046                 this.toggleSourceEdit(btn.pressed);
24047         });
24048         
24049         if (this.editor.btns.length > 0) {
24050             for (var i = 0; i<this.editor.btns.length; i++) {
24051                 children.push(this.editor.btns[i]);
24052             }
24053         }
24054         
24055         /*
24056         var cog = {
24057                 xtype: 'Button',
24058                 size : 'sm',
24059                 xns: Roo.bootstrap,
24060                 glyphicon : 'cog',
24061                 //html : 'submit'
24062                 menu : {
24063                     xtype: 'Menu',
24064                     xns: Roo.bootstrap,
24065                     items:  []
24066                 }
24067         };
24068         
24069         cog.menu.items.push({
24070             xtype :'MenuItem',
24071             xns: Roo.bootstrap,
24072             html : Clean styles,
24073             tagname : f,
24074             listeners : {
24075                 click : function()
24076                 {
24077                     editorcore.insertTag(this.tagname);
24078                     editor.focus();
24079                 }
24080             }
24081             
24082         });
24083        */
24084         
24085          
24086        this.xtype = 'NavSimplebar';
24087         
24088         for(var i=0;i< children.length;i++) {
24089             
24090             this.buttons.add(this.addxtypeChild(children[i]));
24091             
24092         }
24093         
24094         editor.on('editorevent', this.updateToolbar, this);
24095     },
24096     onBtnClick : function(id)
24097     {
24098        this.editorcore.relayCmd(id);
24099        this.editorcore.focus();
24100     },
24101     
24102     /**
24103      * Protected method that will not generally be called directly. It triggers
24104      * a toolbar update by reading the markup state of the current selection in the editor.
24105      */
24106     updateToolbar: function(){
24107
24108         if(!this.editorcore.activated){
24109             this.editor.onFirstFocus(); // is this neeed?
24110             return;
24111         }
24112
24113         var btns = this.buttons; 
24114         var doc = this.editorcore.doc;
24115         btns.get('bold').setActive(doc.queryCommandState('bold'));
24116         btns.get('italic').setActive(doc.queryCommandState('italic'));
24117         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24118         
24119         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24120         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24121         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24122         
24123         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24124         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24125          /*
24126         
24127         var ans = this.editorcore.getAllAncestors();
24128         if (this.formatCombo) {
24129             
24130             
24131             var store = this.formatCombo.store;
24132             this.formatCombo.setValue("");
24133             for (var i =0; i < ans.length;i++) {
24134                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24135                     // select it..
24136                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24137                     break;
24138                 }
24139             }
24140         }
24141         
24142         
24143         
24144         // hides menus... - so this cant be on a menu...
24145         Roo.bootstrap.MenuMgr.hideAll();
24146         */
24147         Roo.bootstrap.MenuMgr.hideAll();
24148         //this.editorsyncValue();
24149     },
24150     onFirstFocus: function() {
24151         this.buttons.each(function(item){
24152            item.enable();
24153         });
24154     },
24155     toggleSourceEdit : function(sourceEditMode){
24156         
24157           
24158         if(sourceEditMode){
24159             Roo.log("disabling buttons");
24160            this.buttons.each( function(item){
24161                 if(item.cmd != 'pencil'){
24162                     item.disable();
24163                 }
24164             });
24165           
24166         }else{
24167             Roo.log("enabling buttons");
24168             if(this.editorcore.initialized){
24169                 this.buttons.each( function(item){
24170                     item.enable();
24171                 });
24172             }
24173             
24174         }
24175         Roo.log("calling toggole on editor");
24176         // tell the editor that it's been pressed..
24177         this.editor.toggleSourceEdit(sourceEditMode);
24178        
24179     }
24180 });
24181
24182
24183
24184
24185
24186 /**
24187  * @class Roo.bootstrap.Table.AbstractSelectionModel
24188  * @extends Roo.util.Observable
24189  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24190  * implemented by descendant classes.  This class should not be directly instantiated.
24191  * @constructor
24192  */
24193 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24194     this.locked = false;
24195     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24196 };
24197
24198
24199 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24200     /** @ignore Called by the grid automatically. Do not call directly. */
24201     init : function(grid){
24202         this.grid = grid;
24203         this.initEvents();
24204     },
24205
24206     /**
24207      * Locks the selections.
24208      */
24209     lock : function(){
24210         this.locked = true;
24211     },
24212
24213     /**
24214      * Unlocks the selections.
24215      */
24216     unlock : function(){
24217         this.locked = false;
24218     },
24219
24220     /**
24221      * Returns true if the selections are locked.
24222      * @return {Boolean}
24223      */
24224     isLocked : function(){
24225         return this.locked;
24226     }
24227 });
24228 /**
24229  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24230  * @class Roo.bootstrap.Table.RowSelectionModel
24231  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24232  * It supports multiple selections and keyboard selection/navigation. 
24233  * @constructor
24234  * @param {Object} config
24235  */
24236
24237 Roo.bootstrap.Table.RowSelectionModel = function(config){
24238     Roo.apply(this, config);
24239     this.selections = new Roo.util.MixedCollection(false, function(o){
24240         return o.id;
24241     });
24242
24243     this.last = false;
24244     this.lastActive = false;
24245
24246     this.addEvents({
24247         /**
24248              * @event selectionchange
24249              * Fires when the selection changes
24250              * @param {SelectionModel} this
24251              */
24252             "selectionchange" : true,
24253         /**
24254              * @event afterselectionchange
24255              * Fires after the selection changes (eg. by key press or clicking)
24256              * @param {SelectionModel} this
24257              */
24258             "afterselectionchange" : true,
24259         /**
24260              * @event beforerowselect
24261              * Fires when a row is selected being selected, return false to cancel.
24262              * @param {SelectionModel} this
24263              * @param {Number} rowIndex The selected index
24264              * @param {Boolean} keepExisting False if other selections will be cleared
24265              */
24266             "beforerowselect" : true,
24267         /**
24268              * @event rowselect
24269              * Fires when a row is selected.
24270              * @param {SelectionModel} this
24271              * @param {Number} rowIndex The selected index
24272              * @param {Roo.data.Record} r The record
24273              */
24274             "rowselect" : true,
24275         /**
24276              * @event rowdeselect
24277              * Fires when a row is deselected.
24278              * @param {SelectionModel} this
24279              * @param {Number} rowIndex The selected index
24280              */
24281         "rowdeselect" : true
24282     });
24283     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24284     this.locked = false;
24285  };
24286
24287 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24288     /**
24289      * @cfg {Boolean} singleSelect
24290      * True to allow selection of only one row at a time (defaults to false)
24291      */
24292     singleSelect : false,
24293
24294     // private
24295     initEvents : function()
24296     {
24297
24298         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24299         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24300         //}else{ // allow click to work like normal
24301          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24302         //}
24303         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24304         this.grid.on("rowclick", this.handleMouseDown, this);
24305         
24306         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24307             "up" : function(e){
24308                 if(!e.shiftKey){
24309                     this.selectPrevious(e.shiftKey);
24310                 }else if(this.last !== false && this.lastActive !== false){
24311                     var last = this.last;
24312                     this.selectRange(this.last,  this.lastActive-1);
24313                     this.grid.getView().focusRow(this.lastActive);
24314                     if(last !== false){
24315                         this.last = last;
24316                     }
24317                 }else{
24318                     this.selectFirstRow();
24319                 }
24320                 this.fireEvent("afterselectionchange", this);
24321             },
24322             "down" : function(e){
24323                 if(!e.shiftKey){
24324                     this.selectNext(e.shiftKey);
24325                 }else if(this.last !== false && this.lastActive !== false){
24326                     var last = this.last;
24327                     this.selectRange(this.last,  this.lastActive+1);
24328                     this.grid.getView().focusRow(this.lastActive);
24329                     if(last !== false){
24330                         this.last = last;
24331                     }
24332                 }else{
24333                     this.selectFirstRow();
24334                 }
24335                 this.fireEvent("afterselectionchange", this);
24336             },
24337             scope: this
24338         });
24339         this.grid.store.on('load', function(){
24340             this.selections.clear();
24341         },this);
24342         /*
24343         var view = this.grid.view;
24344         view.on("refresh", this.onRefresh, this);
24345         view.on("rowupdated", this.onRowUpdated, this);
24346         view.on("rowremoved", this.onRemove, this);
24347         */
24348     },
24349
24350     // private
24351     onRefresh : function()
24352     {
24353         var ds = this.grid.store, i, v = this.grid.view;
24354         var s = this.selections;
24355         s.each(function(r){
24356             if((i = ds.indexOfId(r.id)) != -1){
24357                 v.onRowSelect(i);
24358             }else{
24359                 s.remove(r);
24360             }
24361         });
24362     },
24363
24364     // private
24365     onRemove : function(v, index, r){
24366         this.selections.remove(r);
24367     },
24368
24369     // private
24370     onRowUpdated : function(v, index, r){
24371         if(this.isSelected(r)){
24372             v.onRowSelect(index);
24373         }
24374     },
24375
24376     /**
24377      * Select records.
24378      * @param {Array} records The records to select
24379      * @param {Boolean} keepExisting (optional) True to keep existing selections
24380      */
24381     selectRecords : function(records, keepExisting)
24382     {
24383         if(!keepExisting){
24384             this.clearSelections();
24385         }
24386             var ds = this.grid.store;
24387         for(var i = 0, len = records.length; i < len; i++){
24388             this.selectRow(ds.indexOf(records[i]), true);
24389         }
24390     },
24391
24392     /**
24393      * Gets the number of selected rows.
24394      * @return {Number}
24395      */
24396     getCount : function(){
24397         return this.selections.length;
24398     },
24399
24400     /**
24401      * Selects the first row in the grid.
24402      */
24403     selectFirstRow : function(){
24404         this.selectRow(0);
24405     },
24406
24407     /**
24408      * Select the last row.
24409      * @param {Boolean} keepExisting (optional) True to keep existing selections
24410      */
24411     selectLastRow : function(keepExisting){
24412         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24413         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24414     },
24415
24416     /**
24417      * Selects the row immediately following the last selected row.
24418      * @param {Boolean} keepExisting (optional) True to keep existing selections
24419      */
24420     selectNext : function(keepExisting)
24421     {
24422             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24423             this.selectRow(this.last+1, keepExisting);
24424             this.grid.getView().focusRow(this.last);
24425         }
24426     },
24427
24428     /**
24429      * Selects the row that precedes the last selected row.
24430      * @param {Boolean} keepExisting (optional) True to keep existing selections
24431      */
24432     selectPrevious : function(keepExisting){
24433         if(this.last){
24434             this.selectRow(this.last-1, keepExisting);
24435             this.grid.getView().focusRow(this.last);
24436         }
24437     },
24438
24439     /**
24440      * Returns the selected records
24441      * @return {Array} Array of selected records
24442      */
24443     getSelections : function(){
24444         return [].concat(this.selections.items);
24445     },
24446
24447     /**
24448      * Returns the first selected record.
24449      * @return {Record}
24450      */
24451     getSelected : function(){
24452         return this.selections.itemAt(0);
24453     },
24454
24455
24456     /**
24457      * Clears all selections.
24458      */
24459     clearSelections : function(fast)
24460     {
24461         if(this.locked) {
24462             return;
24463         }
24464         if(fast !== true){
24465                 var ds = this.grid.store;
24466             var s = this.selections;
24467             s.each(function(r){
24468                 this.deselectRow(ds.indexOfId(r.id));
24469             }, this);
24470             s.clear();
24471         }else{
24472             this.selections.clear();
24473         }
24474         this.last = false;
24475     },
24476
24477
24478     /**
24479      * Selects all rows.
24480      */
24481     selectAll : function(){
24482         if(this.locked) {
24483             return;
24484         }
24485         this.selections.clear();
24486         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24487             this.selectRow(i, true);
24488         }
24489     },
24490
24491     /**
24492      * Returns True if there is a selection.
24493      * @return {Boolean}
24494      */
24495     hasSelection : function(){
24496         return this.selections.length > 0;
24497     },
24498
24499     /**
24500      * Returns True if the specified row is selected.
24501      * @param {Number/Record} record The record or index of the record to check
24502      * @return {Boolean}
24503      */
24504     isSelected : function(index){
24505             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24506         return (r && this.selections.key(r.id) ? true : false);
24507     },
24508
24509     /**
24510      * Returns True if the specified record id is selected.
24511      * @param {String} id The id of record to check
24512      * @return {Boolean}
24513      */
24514     isIdSelected : function(id){
24515         return (this.selections.key(id) ? true : false);
24516     },
24517
24518
24519     // private
24520     handleMouseDBClick : function(e, t){
24521         
24522     },
24523     // private
24524     handleMouseDown : function(e, t)
24525     {
24526             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24527         if(this.isLocked() || rowIndex < 0 ){
24528             return;
24529         };
24530         if(e.shiftKey && this.last !== false){
24531             var last = this.last;
24532             this.selectRange(last, rowIndex, e.ctrlKey);
24533             this.last = last; // reset the last
24534             t.focus();
24535     
24536         }else{
24537             var isSelected = this.isSelected(rowIndex);
24538             //Roo.log("select row:" + rowIndex);
24539             if(isSelected){
24540                 this.deselectRow(rowIndex);
24541             } else {
24542                         this.selectRow(rowIndex, true);
24543             }
24544     
24545             /*
24546                 if(e.button !== 0 && isSelected){
24547                 alert('rowIndex 2: ' + rowIndex);
24548                     view.focusRow(rowIndex);
24549                 }else if(e.ctrlKey && isSelected){
24550                     this.deselectRow(rowIndex);
24551                 }else if(!isSelected){
24552                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24553                     view.focusRow(rowIndex);
24554                 }
24555             */
24556         }
24557         this.fireEvent("afterselectionchange", this);
24558     },
24559     // private
24560     handleDragableRowClick :  function(grid, rowIndex, e) 
24561     {
24562         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24563             this.selectRow(rowIndex, false);
24564             grid.view.focusRow(rowIndex);
24565              this.fireEvent("afterselectionchange", this);
24566         }
24567     },
24568     
24569     /**
24570      * Selects multiple rows.
24571      * @param {Array} rows Array of the indexes of the row to select
24572      * @param {Boolean} keepExisting (optional) True to keep existing selections
24573      */
24574     selectRows : function(rows, keepExisting){
24575         if(!keepExisting){
24576             this.clearSelections();
24577         }
24578         for(var i = 0, len = rows.length; i < len; i++){
24579             this.selectRow(rows[i], true);
24580         }
24581     },
24582
24583     /**
24584      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24585      * @param {Number} startRow The index of the first row in the range
24586      * @param {Number} endRow The index of the last row in the range
24587      * @param {Boolean} keepExisting (optional) True to retain existing selections
24588      */
24589     selectRange : function(startRow, endRow, keepExisting){
24590         if(this.locked) {
24591             return;
24592         }
24593         if(!keepExisting){
24594             this.clearSelections();
24595         }
24596         if(startRow <= endRow){
24597             for(var i = startRow; i <= endRow; i++){
24598                 this.selectRow(i, true);
24599             }
24600         }else{
24601             for(var i = startRow; i >= endRow; i--){
24602                 this.selectRow(i, true);
24603             }
24604         }
24605     },
24606
24607     /**
24608      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24609      * @param {Number} startRow The index of the first row in the range
24610      * @param {Number} endRow The index of the last row in the range
24611      */
24612     deselectRange : function(startRow, endRow, preventViewNotify){
24613         if(this.locked) {
24614             return;
24615         }
24616         for(var i = startRow; i <= endRow; i++){
24617             this.deselectRow(i, preventViewNotify);
24618         }
24619     },
24620
24621     /**
24622      * Selects a row.
24623      * @param {Number} row The index of the row to select
24624      * @param {Boolean} keepExisting (optional) True to keep existing selections
24625      */
24626     selectRow : function(index, keepExisting, preventViewNotify)
24627     {
24628             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24629             return;
24630         }
24631         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24632             if(!keepExisting || this.singleSelect){
24633                 this.clearSelections();
24634             }
24635             
24636             var r = this.grid.store.getAt(index);
24637             //console.log('selectRow - record id :' + r.id);
24638             
24639             this.selections.add(r);
24640             this.last = this.lastActive = index;
24641             if(!preventViewNotify){
24642                 var proxy = new Roo.Element(
24643                                 this.grid.getRowDom(index)
24644                 );
24645                 proxy.addClass('bg-info info');
24646             }
24647             this.fireEvent("rowselect", this, index, r);
24648             this.fireEvent("selectionchange", this);
24649         }
24650     },
24651
24652     /**
24653      * Deselects a row.
24654      * @param {Number} row The index of the row to deselect
24655      */
24656     deselectRow : function(index, preventViewNotify)
24657     {
24658         if(this.locked) {
24659             return;
24660         }
24661         if(this.last == index){
24662             this.last = false;
24663         }
24664         if(this.lastActive == index){
24665             this.lastActive = false;
24666         }
24667         
24668         var r = this.grid.store.getAt(index);
24669         if (!r) {
24670             return;
24671         }
24672         
24673         this.selections.remove(r);
24674         //.console.log('deselectRow - record id :' + r.id);
24675         if(!preventViewNotify){
24676         
24677             var proxy = new Roo.Element(
24678                 this.grid.getRowDom(index)
24679             );
24680             proxy.removeClass('bg-info info');
24681         }
24682         this.fireEvent("rowdeselect", this, index);
24683         this.fireEvent("selectionchange", this);
24684     },
24685
24686     // private
24687     restoreLast : function(){
24688         if(this._last){
24689             this.last = this._last;
24690         }
24691     },
24692
24693     // private
24694     acceptsNav : function(row, col, cm){
24695         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24696     },
24697
24698     // private
24699     onEditorKey : function(field, e){
24700         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24701         if(k == e.TAB){
24702             e.stopEvent();
24703             ed.completeEdit();
24704             if(e.shiftKey){
24705                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24706             }else{
24707                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24708             }
24709         }else if(k == e.ENTER && !e.ctrlKey){
24710             e.stopEvent();
24711             ed.completeEdit();
24712             if(e.shiftKey){
24713                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24714             }else{
24715                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24716             }
24717         }else if(k == e.ESC){
24718             ed.cancelEdit();
24719         }
24720         if(newCell){
24721             g.startEditing(newCell[0], newCell[1]);
24722         }
24723     }
24724 });
24725 /*
24726  * Based on:
24727  * Ext JS Library 1.1.1
24728  * Copyright(c) 2006-2007, Ext JS, LLC.
24729  *
24730  * Originally Released Under LGPL - original licence link has changed is not relivant.
24731  *
24732  * Fork - LGPL
24733  * <script type="text/javascript">
24734  */
24735  
24736 /**
24737  * @class Roo.bootstrap.PagingToolbar
24738  * @extends Roo.bootstrap.NavSimplebar
24739  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24740  * @constructor
24741  * Create a new PagingToolbar
24742  * @param {Object} config The config object
24743  * @param {Roo.data.Store} store
24744  */
24745 Roo.bootstrap.PagingToolbar = function(config)
24746 {
24747     // old args format still supported... - xtype is prefered..
24748         // created from xtype...
24749     
24750     this.ds = config.dataSource;
24751     
24752     if (config.store && !this.ds) {
24753         this.store= Roo.factory(config.store, Roo.data);
24754         this.ds = this.store;
24755         this.ds.xmodule = this.xmodule || false;
24756     }
24757     
24758     this.toolbarItems = [];
24759     if (config.items) {
24760         this.toolbarItems = config.items;
24761     }
24762     
24763     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24764     
24765     this.cursor = 0;
24766     
24767     if (this.ds) { 
24768         this.bind(this.ds);
24769     }
24770     
24771     if (Roo.bootstrap.version == 4) {
24772         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24773     } else {
24774         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24775     }
24776     
24777 };
24778
24779 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24780     /**
24781      * @cfg {Roo.data.Store} dataSource
24782      * The underlying data store providing the paged data
24783      */
24784     /**
24785      * @cfg {String/HTMLElement/Element} container
24786      * container The id or element that will contain the toolbar
24787      */
24788     /**
24789      * @cfg {Boolean} displayInfo
24790      * True to display the displayMsg (defaults to false)
24791      */
24792     /**
24793      * @cfg {Number} pageSize
24794      * The number of records to display per page (defaults to 20)
24795      */
24796     pageSize: 20,
24797     /**
24798      * @cfg {String} displayMsg
24799      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24800      */
24801     displayMsg : 'Displaying {0} - {1} of {2}',
24802     /**
24803      * @cfg {String} emptyMsg
24804      * The message to display when no records are found (defaults to "No data to display")
24805      */
24806     emptyMsg : 'No data to display',
24807     /**
24808      * Customizable piece of the default paging text (defaults to "Page")
24809      * @type String
24810      */
24811     beforePageText : "Page",
24812     /**
24813      * Customizable piece of the default paging text (defaults to "of %0")
24814      * @type String
24815      */
24816     afterPageText : "of {0}",
24817     /**
24818      * Customizable piece of the default paging text (defaults to "First Page")
24819      * @type String
24820      */
24821     firstText : "First Page",
24822     /**
24823      * Customizable piece of the default paging text (defaults to "Previous Page")
24824      * @type String
24825      */
24826     prevText : "Previous Page",
24827     /**
24828      * Customizable piece of the default paging text (defaults to "Next Page")
24829      * @type String
24830      */
24831     nextText : "Next Page",
24832     /**
24833      * Customizable piece of the default paging text (defaults to "Last Page")
24834      * @type String
24835      */
24836     lastText : "Last Page",
24837     /**
24838      * Customizable piece of the default paging text (defaults to "Refresh")
24839      * @type String
24840      */
24841     refreshText : "Refresh",
24842
24843     buttons : false,
24844     // private
24845     onRender : function(ct, position) 
24846     {
24847         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24848         this.navgroup.parentId = this.id;
24849         this.navgroup.onRender(this.el, null);
24850         // add the buttons to the navgroup
24851         
24852         if(this.displayInfo){
24853             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24854             this.displayEl = this.el.select('.x-paging-info', true).first();
24855 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24856 //            this.displayEl = navel.el.select('span',true).first();
24857         }
24858         
24859         var _this = this;
24860         
24861         if(this.buttons){
24862             Roo.each(_this.buttons, function(e){ // this might need to use render????
24863                Roo.factory(e).render(_this.el);
24864             });
24865         }
24866             
24867         Roo.each(_this.toolbarItems, function(e) {
24868             _this.navgroup.addItem(e);
24869         });
24870         
24871         
24872         this.first = this.navgroup.addItem({
24873             tooltip: this.firstText,
24874             cls: "prev btn-outline-secondary",
24875             html : ' <i class="fa fa-step-backward"></i>',
24876             disabled: true,
24877             preventDefault: true,
24878             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24879         });
24880         
24881         this.prev =  this.navgroup.addItem({
24882             tooltip: this.prevText,
24883             cls: "prev btn-outline-secondary",
24884             html : ' <i class="fa fa-backward"></i>',
24885             disabled: true,
24886             preventDefault: true,
24887             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24888         });
24889     //this.addSeparator();
24890         
24891         
24892         var field = this.navgroup.addItem( {
24893             tagtype : 'span',
24894             cls : 'x-paging-position  btn-outline-secondary',
24895              disabled: true,
24896             html : this.beforePageText  +
24897                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24898                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24899          } ); //?? escaped?
24900         
24901         this.field = field.el.select('input', true).first();
24902         this.field.on("keydown", this.onPagingKeydown, this);
24903         this.field.on("focus", function(){this.dom.select();});
24904     
24905     
24906         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24907         //this.field.setHeight(18);
24908         //this.addSeparator();
24909         this.next = this.navgroup.addItem({
24910             tooltip: this.nextText,
24911             cls: "next btn-outline-secondary",
24912             html : ' <i class="fa fa-forward"></i>',
24913             disabled: true,
24914             preventDefault: true,
24915             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24916         });
24917         this.last = this.navgroup.addItem({
24918             tooltip: this.lastText,
24919             html : ' <i class="fa fa-step-forward"></i>',
24920             cls: "next btn-outline-secondary",
24921             disabled: true,
24922             preventDefault: true,
24923             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24924         });
24925     //this.addSeparator();
24926         this.loading = this.navgroup.addItem({
24927             tooltip: this.refreshText,
24928             cls: "btn-outline-secondary",
24929             html : ' <i class="fa fa-refresh"></i>',
24930             preventDefault: true,
24931             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24932         });
24933         
24934     },
24935
24936     // private
24937     updateInfo : function(){
24938         if(this.displayEl){
24939             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24940             var msg = count == 0 ?
24941                 this.emptyMsg :
24942                 String.format(
24943                     this.displayMsg,
24944                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24945                 );
24946             this.displayEl.update(msg);
24947         }
24948     },
24949
24950     // private
24951     onLoad : function(ds, r, o)
24952     {
24953         this.cursor = o.params.start ? o.params.start : 0;
24954         
24955         var d = this.getPageData(),
24956             ap = d.activePage,
24957             ps = d.pages;
24958         
24959         
24960         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24961         this.field.dom.value = ap;
24962         this.first.setDisabled(ap == 1);
24963         this.prev.setDisabled(ap == 1);
24964         this.next.setDisabled(ap == ps);
24965         this.last.setDisabled(ap == ps);
24966         this.loading.enable();
24967         this.updateInfo();
24968     },
24969
24970     // private
24971     getPageData : function(){
24972         var total = this.ds.getTotalCount();
24973         return {
24974             total : total,
24975             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24976             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24977         };
24978     },
24979
24980     // private
24981     onLoadError : function(){
24982         this.loading.enable();
24983     },
24984
24985     // private
24986     onPagingKeydown : function(e){
24987         var k = e.getKey();
24988         var d = this.getPageData();
24989         if(k == e.RETURN){
24990             var v = this.field.dom.value, pageNum;
24991             if(!v || isNaN(pageNum = parseInt(v, 10))){
24992                 this.field.dom.value = d.activePage;
24993                 return;
24994             }
24995             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24996             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24997             e.stopEvent();
24998         }
24999         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))
25000         {
25001           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25002           this.field.dom.value = pageNum;
25003           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25004           e.stopEvent();
25005         }
25006         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25007         {
25008           var v = this.field.dom.value, pageNum; 
25009           var increment = (e.shiftKey) ? 10 : 1;
25010           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25011                 increment *= -1;
25012           }
25013           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25014             this.field.dom.value = d.activePage;
25015             return;
25016           }
25017           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25018           {
25019             this.field.dom.value = parseInt(v, 10) + increment;
25020             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25021             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25022           }
25023           e.stopEvent();
25024         }
25025     },
25026
25027     // private
25028     beforeLoad : function(){
25029         if(this.loading){
25030             this.loading.disable();
25031         }
25032     },
25033
25034     // private
25035     onClick : function(which){
25036         
25037         var ds = this.ds;
25038         if (!ds) {
25039             return;
25040         }
25041         
25042         switch(which){
25043             case "first":
25044                 ds.load({params:{start: 0, limit: this.pageSize}});
25045             break;
25046             case "prev":
25047                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25048             break;
25049             case "next":
25050                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25051             break;
25052             case "last":
25053                 var total = ds.getTotalCount();
25054                 var extra = total % this.pageSize;
25055                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25056                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25057             break;
25058             case "refresh":
25059                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25060             break;
25061         }
25062     },
25063
25064     /**
25065      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25066      * @param {Roo.data.Store} store The data store to unbind
25067      */
25068     unbind : function(ds){
25069         ds.un("beforeload", this.beforeLoad, this);
25070         ds.un("load", this.onLoad, this);
25071         ds.un("loadexception", this.onLoadError, this);
25072         ds.un("remove", this.updateInfo, this);
25073         ds.un("add", this.updateInfo, this);
25074         this.ds = undefined;
25075     },
25076
25077     /**
25078      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25079      * @param {Roo.data.Store} store The data store to bind
25080      */
25081     bind : function(ds){
25082         ds.on("beforeload", this.beforeLoad, this);
25083         ds.on("load", this.onLoad, this);
25084         ds.on("loadexception", this.onLoadError, this);
25085         ds.on("remove", this.updateInfo, this);
25086         ds.on("add", this.updateInfo, this);
25087         this.ds = ds;
25088     }
25089 });/*
25090  * - LGPL
25091  *
25092  * element
25093  * 
25094  */
25095
25096 /**
25097  * @class Roo.bootstrap.MessageBar
25098  * @extends Roo.bootstrap.Component
25099  * Bootstrap MessageBar class
25100  * @cfg {String} html contents of the MessageBar
25101  * @cfg {String} weight (info | success | warning | danger) default info
25102  * @cfg {String} beforeClass insert the bar before the given class
25103  * @cfg {Boolean} closable (true | false) default false
25104  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25105  * 
25106  * @constructor
25107  * Create a new Element
25108  * @param {Object} config The config object
25109  */
25110
25111 Roo.bootstrap.MessageBar = function(config){
25112     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25113 };
25114
25115 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25116     
25117     html: '',
25118     weight: 'info',
25119     closable: false,
25120     fixed: false,
25121     beforeClass: 'bootstrap-sticky-wrap',
25122     
25123     getAutoCreate : function(){
25124         
25125         var cfg = {
25126             tag: 'div',
25127             cls: 'alert alert-dismissable alert-' + this.weight,
25128             cn: [
25129                 {
25130                     tag: 'span',
25131                     cls: 'message',
25132                     html: this.html || ''
25133                 }
25134             ]
25135         };
25136         
25137         if(this.fixed){
25138             cfg.cls += ' alert-messages-fixed';
25139         }
25140         
25141         if(this.closable){
25142             cfg.cn.push({
25143                 tag: 'button',
25144                 cls: 'close',
25145                 html: 'x'
25146             });
25147         }
25148         
25149         return cfg;
25150     },
25151     
25152     onRender : function(ct, position)
25153     {
25154         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25155         
25156         if(!this.el){
25157             var cfg = Roo.apply({},  this.getAutoCreate());
25158             cfg.id = Roo.id();
25159             
25160             if (this.cls) {
25161                 cfg.cls += ' ' + this.cls;
25162             }
25163             if (this.style) {
25164                 cfg.style = this.style;
25165             }
25166             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25167             
25168             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25169         }
25170         
25171         this.el.select('>button.close').on('click', this.hide, this);
25172         
25173     },
25174     
25175     show : function()
25176     {
25177         if (!this.rendered) {
25178             this.render();
25179         }
25180         
25181         this.el.show();
25182         
25183         this.fireEvent('show', this);
25184         
25185     },
25186     
25187     hide : function()
25188     {
25189         if (!this.rendered) {
25190             this.render();
25191         }
25192         
25193         this.el.hide();
25194         
25195         this.fireEvent('hide', this);
25196     },
25197     
25198     update : function()
25199     {
25200 //        var e = this.el.dom.firstChild;
25201 //        
25202 //        if(this.closable){
25203 //            e = e.nextSibling;
25204 //        }
25205 //        
25206 //        e.data = this.html || '';
25207
25208         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25209     }
25210    
25211 });
25212
25213  
25214
25215      /*
25216  * - LGPL
25217  *
25218  * Graph
25219  * 
25220  */
25221
25222
25223 /**
25224  * @class Roo.bootstrap.Graph
25225  * @extends Roo.bootstrap.Component
25226  * Bootstrap Graph class
25227 > Prameters
25228  -sm {number} sm 4
25229  -md {number} md 5
25230  @cfg {String} graphtype  bar | vbar | pie
25231  @cfg {number} g_x coodinator | centre x (pie)
25232  @cfg {number} g_y coodinator | centre y (pie)
25233  @cfg {number} g_r radius (pie)
25234  @cfg {number} g_height height of the chart (respected by all elements in the set)
25235  @cfg {number} g_width width of the chart (respected by all elements in the set)
25236  @cfg {Object} title The title of the chart
25237     
25238  -{Array}  values
25239  -opts (object) options for the chart 
25240      o {
25241      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25242      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25243      o vgutter (number)
25244      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.
25245      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25246      o to
25247      o stretch (boolean)
25248      o }
25249  -opts (object) options for the pie
25250      o{
25251      o cut
25252      o startAngle (number)
25253      o endAngle (number)
25254      } 
25255  *
25256  * @constructor
25257  * Create a new Input
25258  * @param {Object} config The config object
25259  */
25260
25261 Roo.bootstrap.Graph = function(config){
25262     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25263     
25264     this.addEvents({
25265         // img events
25266         /**
25267          * @event click
25268          * The img click event for the img.
25269          * @param {Roo.EventObject} e
25270          */
25271         "click" : true
25272     });
25273 };
25274
25275 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25276     
25277     sm: 4,
25278     md: 5,
25279     graphtype: 'bar',
25280     g_height: 250,
25281     g_width: 400,
25282     g_x: 50,
25283     g_y: 50,
25284     g_r: 30,
25285     opts:{
25286         //g_colors: this.colors,
25287         g_type: 'soft',
25288         g_gutter: '20%'
25289
25290     },
25291     title : false,
25292
25293     getAutoCreate : function(){
25294         
25295         var cfg = {
25296             tag: 'div',
25297             html : null
25298         };
25299         
25300         
25301         return  cfg;
25302     },
25303
25304     onRender : function(ct,position){
25305         
25306         
25307         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25308         
25309         if (typeof(Raphael) == 'undefined') {
25310             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25311             return;
25312         }
25313         
25314         this.raphael = Raphael(this.el.dom);
25315         
25316                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25317                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25318                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25319                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25320                 /*
25321                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25322                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25323                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25324                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25325                 
25326                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25327                 r.barchart(330, 10, 300, 220, data1);
25328                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25329                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25330                 */
25331                 
25332                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25333                 // r.barchart(30, 30, 560, 250,  xdata, {
25334                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25335                 //     axis : "0 0 1 1",
25336                 //     axisxlabels :  xdata
25337                 //     //yvalues : cols,
25338                    
25339                 // });
25340 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25341 //        
25342 //        this.load(null,xdata,{
25343 //                axis : "0 0 1 1",
25344 //                axisxlabels :  xdata
25345 //                });
25346
25347     },
25348
25349     load : function(graphtype,xdata,opts)
25350     {
25351         this.raphael.clear();
25352         if(!graphtype) {
25353             graphtype = this.graphtype;
25354         }
25355         if(!opts){
25356             opts = this.opts;
25357         }
25358         var r = this.raphael,
25359             fin = function () {
25360                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25361             },
25362             fout = function () {
25363                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25364             },
25365             pfin = function() {
25366                 this.sector.stop();
25367                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25368
25369                 if (this.label) {
25370                     this.label[0].stop();
25371                     this.label[0].attr({ r: 7.5 });
25372                     this.label[1].attr({ "font-weight": 800 });
25373                 }
25374             },
25375             pfout = function() {
25376                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25377
25378                 if (this.label) {
25379                     this.label[0].animate({ r: 5 }, 500, "bounce");
25380                     this.label[1].attr({ "font-weight": 400 });
25381                 }
25382             };
25383
25384         switch(graphtype){
25385             case 'bar':
25386                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25387                 break;
25388             case 'hbar':
25389                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25390                 break;
25391             case 'pie':
25392 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25393 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25394 //            
25395                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25396                 
25397                 break;
25398
25399         }
25400         
25401         if(this.title){
25402             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25403         }
25404         
25405     },
25406     
25407     setTitle: function(o)
25408     {
25409         this.title = o;
25410     },
25411     
25412     initEvents: function() {
25413         
25414         if(!this.href){
25415             this.el.on('click', this.onClick, this);
25416         }
25417     },
25418     
25419     onClick : function(e)
25420     {
25421         Roo.log('img onclick');
25422         this.fireEvent('click', this, e);
25423     }
25424    
25425 });
25426
25427  
25428 /*
25429  * - LGPL
25430  *
25431  * numberBox
25432  * 
25433  */
25434 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25435
25436 /**
25437  * @class Roo.bootstrap.dash.NumberBox
25438  * @extends Roo.bootstrap.Component
25439  * Bootstrap NumberBox class
25440  * @cfg {String} headline Box headline
25441  * @cfg {String} content Box content
25442  * @cfg {String} icon Box icon
25443  * @cfg {String} footer Footer text
25444  * @cfg {String} fhref Footer href
25445  * 
25446  * @constructor
25447  * Create a new NumberBox
25448  * @param {Object} config The config object
25449  */
25450
25451
25452 Roo.bootstrap.dash.NumberBox = function(config){
25453     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25454     
25455 };
25456
25457 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25458     
25459     headline : '',
25460     content : '',
25461     icon : '',
25462     footer : '',
25463     fhref : '',
25464     ficon : '',
25465     
25466     getAutoCreate : function(){
25467         
25468         var cfg = {
25469             tag : 'div',
25470             cls : 'small-box ',
25471             cn : [
25472                 {
25473                     tag : 'div',
25474                     cls : 'inner',
25475                     cn :[
25476                         {
25477                             tag : 'h3',
25478                             cls : 'roo-headline',
25479                             html : this.headline
25480                         },
25481                         {
25482                             tag : 'p',
25483                             cls : 'roo-content',
25484                             html : this.content
25485                         }
25486                     ]
25487                 }
25488             ]
25489         };
25490         
25491         if(this.icon){
25492             cfg.cn.push({
25493                 tag : 'div',
25494                 cls : 'icon',
25495                 cn :[
25496                     {
25497                         tag : 'i',
25498                         cls : 'ion ' + this.icon
25499                     }
25500                 ]
25501             });
25502         }
25503         
25504         if(this.footer){
25505             var footer = {
25506                 tag : 'a',
25507                 cls : 'small-box-footer',
25508                 href : this.fhref || '#',
25509                 html : this.footer
25510             };
25511             
25512             cfg.cn.push(footer);
25513             
25514         }
25515         
25516         return  cfg;
25517     },
25518
25519     onRender : function(ct,position){
25520         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25521
25522
25523        
25524                 
25525     },
25526
25527     setHeadline: function (value)
25528     {
25529         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25530     },
25531     
25532     setFooter: function (value, href)
25533     {
25534         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25535         
25536         if(href){
25537             this.el.select('a.small-box-footer',true).first().attr('href', href);
25538         }
25539         
25540     },
25541
25542     setContent: function (value)
25543     {
25544         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25545     },
25546
25547     initEvents: function() 
25548     {   
25549         
25550     }
25551     
25552 });
25553
25554  
25555 /*
25556  * - LGPL
25557  *
25558  * TabBox
25559  * 
25560  */
25561 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25562
25563 /**
25564  * @class Roo.bootstrap.dash.TabBox
25565  * @extends Roo.bootstrap.Component
25566  * Bootstrap TabBox class
25567  * @cfg {String} title Title of the TabBox
25568  * @cfg {String} icon Icon of the TabBox
25569  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25570  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25571  * 
25572  * @constructor
25573  * Create a new TabBox
25574  * @param {Object} config The config object
25575  */
25576
25577
25578 Roo.bootstrap.dash.TabBox = function(config){
25579     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25580     this.addEvents({
25581         // raw events
25582         /**
25583          * @event addpane
25584          * When a pane is added
25585          * @param {Roo.bootstrap.dash.TabPane} pane
25586          */
25587         "addpane" : true,
25588         /**
25589          * @event activatepane
25590          * When a pane is activated
25591          * @param {Roo.bootstrap.dash.TabPane} pane
25592          */
25593         "activatepane" : true
25594         
25595          
25596     });
25597     
25598     this.panes = [];
25599 };
25600
25601 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25602
25603     title : '',
25604     icon : false,
25605     showtabs : true,
25606     tabScrollable : false,
25607     
25608     getChildContainer : function()
25609     {
25610         return this.el.select('.tab-content', true).first();
25611     },
25612     
25613     getAutoCreate : function(){
25614         
25615         var header = {
25616             tag: 'li',
25617             cls: 'pull-left header',
25618             html: this.title,
25619             cn : []
25620         };
25621         
25622         if(this.icon){
25623             header.cn.push({
25624                 tag: 'i',
25625                 cls: 'fa ' + this.icon
25626             });
25627         }
25628         
25629         var h = {
25630             tag: 'ul',
25631             cls: 'nav nav-tabs pull-right',
25632             cn: [
25633                 header
25634             ]
25635         };
25636         
25637         if(this.tabScrollable){
25638             h = {
25639                 tag: 'div',
25640                 cls: 'tab-header',
25641                 cn: [
25642                     {
25643                         tag: 'ul',
25644                         cls: 'nav nav-tabs pull-right',
25645                         cn: [
25646                             header
25647                         ]
25648                     }
25649                 ]
25650             };
25651         }
25652         
25653         var cfg = {
25654             tag: 'div',
25655             cls: 'nav-tabs-custom',
25656             cn: [
25657                 h,
25658                 {
25659                     tag: 'div',
25660                     cls: 'tab-content no-padding',
25661                     cn: []
25662                 }
25663             ]
25664         };
25665
25666         return  cfg;
25667     },
25668     initEvents : function()
25669     {
25670         //Roo.log('add add pane handler');
25671         this.on('addpane', this.onAddPane, this);
25672     },
25673      /**
25674      * Updates the box title
25675      * @param {String} html to set the title to.
25676      */
25677     setTitle : function(value)
25678     {
25679         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25680     },
25681     onAddPane : function(pane)
25682     {
25683         this.panes.push(pane);
25684         //Roo.log('addpane');
25685         //Roo.log(pane);
25686         // tabs are rendere left to right..
25687         if(!this.showtabs){
25688             return;
25689         }
25690         
25691         var ctr = this.el.select('.nav-tabs', true).first();
25692          
25693          
25694         var existing = ctr.select('.nav-tab',true);
25695         var qty = existing.getCount();;
25696         
25697         
25698         var tab = ctr.createChild({
25699             tag : 'li',
25700             cls : 'nav-tab' + (qty ? '' : ' active'),
25701             cn : [
25702                 {
25703                     tag : 'a',
25704                     href:'#',
25705                     html : pane.title
25706                 }
25707             ]
25708         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25709         pane.tab = tab;
25710         
25711         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25712         if (!qty) {
25713             pane.el.addClass('active');
25714         }
25715         
25716                 
25717     },
25718     onTabClick : function(ev,un,ob,pane)
25719     {
25720         //Roo.log('tab - prev default');
25721         ev.preventDefault();
25722         
25723         
25724         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25725         pane.tab.addClass('active');
25726         //Roo.log(pane.title);
25727         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25728         // technically we should have a deactivate event.. but maybe add later.
25729         // and it should not de-activate the selected tab...
25730         this.fireEvent('activatepane', pane);
25731         pane.el.addClass('active');
25732         pane.fireEvent('activate');
25733         
25734         
25735     },
25736     
25737     getActivePane : function()
25738     {
25739         var r = false;
25740         Roo.each(this.panes, function(p) {
25741             if(p.el.hasClass('active')){
25742                 r = p;
25743                 return false;
25744             }
25745             
25746             return;
25747         });
25748         
25749         return r;
25750     }
25751     
25752     
25753 });
25754
25755  
25756 /*
25757  * - LGPL
25758  *
25759  * Tab pane
25760  * 
25761  */
25762 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25763 /**
25764  * @class Roo.bootstrap.TabPane
25765  * @extends Roo.bootstrap.Component
25766  * Bootstrap TabPane class
25767  * @cfg {Boolean} active (false | true) Default false
25768  * @cfg {String} title title of panel
25769
25770  * 
25771  * @constructor
25772  * Create a new TabPane
25773  * @param {Object} config The config object
25774  */
25775
25776 Roo.bootstrap.dash.TabPane = function(config){
25777     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25778     
25779     this.addEvents({
25780         // raw events
25781         /**
25782          * @event activate
25783          * When a pane is activated
25784          * @param {Roo.bootstrap.dash.TabPane} pane
25785          */
25786         "activate" : true
25787          
25788     });
25789 };
25790
25791 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25792     
25793     active : false,
25794     title : '',
25795     
25796     // the tabBox that this is attached to.
25797     tab : false,
25798      
25799     getAutoCreate : function() 
25800     {
25801         var cfg = {
25802             tag: 'div',
25803             cls: 'tab-pane'
25804         };
25805         
25806         if(this.active){
25807             cfg.cls += ' active';
25808         }
25809         
25810         return cfg;
25811     },
25812     initEvents  : function()
25813     {
25814         //Roo.log('trigger add pane handler');
25815         this.parent().fireEvent('addpane', this)
25816     },
25817     
25818      /**
25819      * Updates the tab title 
25820      * @param {String} html to set the title to.
25821      */
25822     setTitle: function(str)
25823     {
25824         if (!this.tab) {
25825             return;
25826         }
25827         this.title = str;
25828         this.tab.select('a', true).first().dom.innerHTML = str;
25829         
25830     }
25831     
25832     
25833     
25834 });
25835
25836  
25837
25838
25839  /*
25840  * - LGPL
25841  *
25842  * menu
25843  * 
25844  */
25845 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25846
25847 /**
25848  * @class Roo.bootstrap.menu.Menu
25849  * @extends Roo.bootstrap.Component
25850  * Bootstrap Menu class - container for Menu
25851  * @cfg {String} html Text of the menu
25852  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25853  * @cfg {String} icon Font awesome icon
25854  * @cfg {String} pos Menu align to (top | bottom) default bottom
25855  * 
25856  * 
25857  * @constructor
25858  * Create a new Menu
25859  * @param {Object} config The config object
25860  */
25861
25862
25863 Roo.bootstrap.menu.Menu = function(config){
25864     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25865     
25866     this.addEvents({
25867         /**
25868          * @event beforeshow
25869          * Fires before this menu is displayed
25870          * @param {Roo.bootstrap.menu.Menu} this
25871          */
25872         beforeshow : true,
25873         /**
25874          * @event beforehide
25875          * Fires before this menu is hidden
25876          * @param {Roo.bootstrap.menu.Menu} this
25877          */
25878         beforehide : true,
25879         /**
25880          * @event show
25881          * Fires after this menu is displayed
25882          * @param {Roo.bootstrap.menu.Menu} this
25883          */
25884         show : true,
25885         /**
25886          * @event hide
25887          * Fires after this menu is hidden
25888          * @param {Roo.bootstrap.menu.Menu} this
25889          */
25890         hide : true,
25891         /**
25892          * @event click
25893          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25894          * @param {Roo.bootstrap.menu.Menu} this
25895          * @param {Roo.EventObject} e
25896          */
25897         click : true
25898     });
25899     
25900 };
25901
25902 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25903     
25904     submenu : false,
25905     html : '',
25906     weight : 'default',
25907     icon : false,
25908     pos : 'bottom',
25909     
25910     
25911     getChildContainer : function() {
25912         if(this.isSubMenu){
25913             return this.el;
25914         }
25915         
25916         return this.el.select('ul.dropdown-menu', true).first();  
25917     },
25918     
25919     getAutoCreate : function()
25920     {
25921         var text = [
25922             {
25923                 tag : 'span',
25924                 cls : 'roo-menu-text',
25925                 html : this.html
25926             }
25927         ];
25928         
25929         if(this.icon){
25930             text.unshift({
25931                 tag : 'i',
25932                 cls : 'fa ' + this.icon
25933             })
25934         }
25935         
25936         
25937         var cfg = {
25938             tag : 'div',
25939             cls : 'btn-group',
25940             cn : [
25941                 {
25942                     tag : 'button',
25943                     cls : 'dropdown-button btn btn-' + this.weight,
25944                     cn : text
25945                 },
25946                 {
25947                     tag : 'button',
25948                     cls : 'dropdown-toggle btn btn-' + this.weight,
25949                     cn : [
25950                         {
25951                             tag : 'span',
25952                             cls : 'caret'
25953                         }
25954                     ]
25955                 },
25956                 {
25957                     tag : 'ul',
25958                     cls : 'dropdown-menu'
25959                 }
25960             ]
25961             
25962         };
25963         
25964         if(this.pos == 'top'){
25965             cfg.cls += ' dropup';
25966         }
25967         
25968         if(this.isSubMenu){
25969             cfg = {
25970                 tag : 'ul',
25971                 cls : 'dropdown-menu'
25972             }
25973         }
25974         
25975         return cfg;
25976     },
25977     
25978     onRender : function(ct, position)
25979     {
25980         this.isSubMenu = ct.hasClass('dropdown-submenu');
25981         
25982         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25983     },
25984     
25985     initEvents : function() 
25986     {
25987         if(this.isSubMenu){
25988             return;
25989         }
25990         
25991         this.hidden = true;
25992         
25993         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25994         this.triggerEl.on('click', this.onTriggerPress, this);
25995         
25996         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25997         this.buttonEl.on('click', this.onClick, this);
25998         
25999     },
26000     
26001     list : function()
26002     {
26003         if(this.isSubMenu){
26004             return this.el;
26005         }
26006         
26007         return this.el.select('ul.dropdown-menu', true).first();
26008     },
26009     
26010     onClick : function(e)
26011     {
26012         this.fireEvent("click", this, e);
26013     },
26014     
26015     onTriggerPress  : function(e)
26016     {   
26017         if (this.isVisible()) {
26018             this.hide();
26019         } else {
26020             this.show();
26021         }
26022     },
26023     
26024     isVisible : function(){
26025         return !this.hidden;
26026     },
26027     
26028     show : function()
26029     {
26030         this.fireEvent("beforeshow", this);
26031         
26032         this.hidden = false;
26033         this.el.addClass('open');
26034         
26035         Roo.get(document).on("mouseup", this.onMouseUp, this);
26036         
26037         this.fireEvent("show", this);
26038         
26039         
26040     },
26041     
26042     hide : function()
26043     {
26044         this.fireEvent("beforehide", this);
26045         
26046         this.hidden = true;
26047         this.el.removeClass('open');
26048         
26049         Roo.get(document).un("mouseup", this.onMouseUp);
26050         
26051         this.fireEvent("hide", this);
26052     },
26053     
26054     onMouseUp : function()
26055     {
26056         this.hide();
26057     }
26058     
26059 });
26060
26061  
26062  /*
26063  * - LGPL
26064  *
26065  * menu item
26066  * 
26067  */
26068 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26069
26070 /**
26071  * @class Roo.bootstrap.menu.Item
26072  * @extends Roo.bootstrap.Component
26073  * Bootstrap MenuItem class
26074  * @cfg {Boolean} submenu (true | false) default false
26075  * @cfg {String} html text of the item
26076  * @cfg {String} href the link
26077  * @cfg {Boolean} disable (true | false) default false
26078  * @cfg {Boolean} preventDefault (true | false) default true
26079  * @cfg {String} icon Font awesome icon
26080  * @cfg {String} pos Submenu align to (left | right) default right 
26081  * 
26082  * 
26083  * @constructor
26084  * Create a new Item
26085  * @param {Object} config The config object
26086  */
26087
26088
26089 Roo.bootstrap.menu.Item = function(config){
26090     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26091     this.addEvents({
26092         /**
26093          * @event mouseover
26094          * Fires when the mouse is hovering over this menu
26095          * @param {Roo.bootstrap.menu.Item} this
26096          * @param {Roo.EventObject} e
26097          */
26098         mouseover : true,
26099         /**
26100          * @event mouseout
26101          * Fires when the mouse exits this menu
26102          * @param {Roo.bootstrap.menu.Item} this
26103          * @param {Roo.EventObject} e
26104          */
26105         mouseout : true,
26106         // raw events
26107         /**
26108          * @event click
26109          * The raw click event for the entire grid.
26110          * @param {Roo.EventObject} e
26111          */
26112         click : true
26113     });
26114 };
26115
26116 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26117     
26118     submenu : false,
26119     href : '',
26120     html : '',
26121     preventDefault: true,
26122     disable : false,
26123     icon : false,
26124     pos : 'right',
26125     
26126     getAutoCreate : function()
26127     {
26128         var text = [
26129             {
26130                 tag : 'span',
26131                 cls : 'roo-menu-item-text',
26132                 html : this.html
26133             }
26134         ];
26135         
26136         if(this.icon){
26137             text.unshift({
26138                 tag : 'i',
26139                 cls : 'fa ' + this.icon
26140             })
26141         }
26142         
26143         var cfg = {
26144             tag : 'li',
26145             cn : [
26146                 {
26147                     tag : 'a',
26148                     href : this.href || '#',
26149                     cn : text
26150                 }
26151             ]
26152         };
26153         
26154         if(this.disable){
26155             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26156         }
26157         
26158         if(this.submenu){
26159             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26160             
26161             if(this.pos == 'left'){
26162                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26163             }
26164         }
26165         
26166         return cfg;
26167     },
26168     
26169     initEvents : function() 
26170     {
26171         this.el.on('mouseover', this.onMouseOver, this);
26172         this.el.on('mouseout', this.onMouseOut, this);
26173         
26174         this.el.select('a', true).first().on('click', this.onClick, this);
26175         
26176     },
26177     
26178     onClick : function(e)
26179     {
26180         if(this.preventDefault){
26181             e.preventDefault();
26182         }
26183         
26184         this.fireEvent("click", this, e);
26185     },
26186     
26187     onMouseOver : function(e)
26188     {
26189         if(this.submenu && this.pos == 'left'){
26190             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26191         }
26192         
26193         this.fireEvent("mouseover", this, e);
26194     },
26195     
26196     onMouseOut : function(e)
26197     {
26198         this.fireEvent("mouseout", this, e);
26199     }
26200 });
26201
26202  
26203
26204  /*
26205  * - LGPL
26206  *
26207  * menu separator
26208  * 
26209  */
26210 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26211
26212 /**
26213  * @class Roo.bootstrap.menu.Separator
26214  * @extends Roo.bootstrap.Component
26215  * Bootstrap Separator class
26216  * 
26217  * @constructor
26218  * Create a new Separator
26219  * @param {Object} config The config object
26220  */
26221
26222
26223 Roo.bootstrap.menu.Separator = function(config){
26224     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26225 };
26226
26227 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26228     
26229     getAutoCreate : function(){
26230         var cfg = {
26231             tag : 'li',
26232             cls: 'divider'
26233         };
26234         
26235         return cfg;
26236     }
26237    
26238 });
26239
26240  
26241
26242  /*
26243  * - LGPL
26244  *
26245  * Tooltip
26246  * 
26247  */
26248
26249 /**
26250  * @class Roo.bootstrap.Tooltip
26251  * Bootstrap Tooltip class
26252  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26253  * to determine which dom element triggers the tooltip.
26254  * 
26255  * It needs to add support for additional attributes like tooltip-position
26256  * 
26257  * @constructor
26258  * Create a new Toolti
26259  * @param {Object} config The config object
26260  */
26261
26262 Roo.bootstrap.Tooltip = function(config){
26263     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26264     
26265     this.alignment = Roo.bootstrap.Tooltip.alignment;
26266     
26267     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26268         this.alignment = config.alignment;
26269     }
26270     
26271 };
26272
26273 Roo.apply(Roo.bootstrap.Tooltip, {
26274     /**
26275      * @function init initialize tooltip monitoring.
26276      * @static
26277      */
26278     currentEl : false,
26279     currentTip : false,
26280     currentRegion : false,
26281     
26282     //  init : delay?
26283     
26284     init : function()
26285     {
26286         Roo.get(document).on('mouseover', this.enter ,this);
26287         Roo.get(document).on('mouseout', this.leave, this);
26288          
26289         
26290         this.currentTip = new Roo.bootstrap.Tooltip();
26291     },
26292     
26293     enter : function(ev)
26294     {
26295         var dom = ev.getTarget();
26296         
26297         //Roo.log(['enter',dom]);
26298         var el = Roo.fly(dom);
26299         if (this.currentEl) {
26300             //Roo.log(dom);
26301             //Roo.log(this.currentEl);
26302             //Roo.log(this.currentEl.contains(dom));
26303             if (this.currentEl == el) {
26304                 return;
26305             }
26306             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26307                 return;
26308             }
26309
26310         }
26311         
26312         if (this.currentTip.el) {
26313             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26314         }    
26315         //Roo.log(ev);
26316         
26317         if(!el || el.dom == document){
26318             return;
26319         }
26320         
26321         var bindEl = el;
26322         
26323         // you can not look for children, as if el is the body.. then everythign is the child..
26324         if (!el.attr('tooltip')) { //
26325             if (!el.select("[tooltip]").elements.length) {
26326                 return;
26327             }
26328             // is the mouse over this child...?
26329             bindEl = el.select("[tooltip]").first();
26330             var xy = ev.getXY();
26331             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26332                 //Roo.log("not in region.");
26333                 return;
26334             }
26335             //Roo.log("child element over..");
26336             
26337         }
26338         this.currentEl = bindEl;
26339         this.currentTip.bind(bindEl);
26340         this.currentRegion = Roo.lib.Region.getRegion(dom);
26341         this.currentTip.enter();
26342         
26343     },
26344     leave : function(ev)
26345     {
26346         var dom = ev.getTarget();
26347         //Roo.log(['leave',dom]);
26348         if (!this.currentEl) {
26349             return;
26350         }
26351         
26352         
26353         if (dom != this.currentEl.dom) {
26354             return;
26355         }
26356         var xy = ev.getXY();
26357         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26358             return;
26359         }
26360         // only activate leave if mouse cursor is outside... bounding box..
26361         
26362         
26363         
26364         
26365         if (this.currentTip) {
26366             this.currentTip.leave();
26367         }
26368         //Roo.log('clear currentEl');
26369         this.currentEl = false;
26370         
26371         
26372     },
26373     alignment : {
26374         'left' : ['r-l', [-2,0], 'right'],
26375         'right' : ['l-r', [2,0], 'left'],
26376         'bottom' : ['t-b', [0,2], 'top'],
26377         'top' : [ 'b-t', [0,-2], 'bottom']
26378     }
26379     
26380 });
26381
26382
26383 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26384     
26385     
26386     bindEl : false,
26387     
26388     delay : null, // can be { show : 300 , hide: 500}
26389     
26390     timeout : null,
26391     
26392     hoverState : null, //???
26393     
26394     placement : 'bottom', 
26395     
26396     alignment : false,
26397     
26398     getAutoCreate : function(){
26399     
26400         var cfg = {
26401            cls : 'tooltip',
26402            role : 'tooltip',
26403            cn : [
26404                 {
26405                     cls : 'tooltip-arrow'
26406                 },
26407                 {
26408                     cls : 'tooltip-inner'
26409                 }
26410            ]
26411         };
26412         
26413         return cfg;
26414     },
26415     bind : function(el)
26416     {
26417         this.bindEl = el;
26418     },
26419       
26420     
26421     enter : function () {
26422        
26423         if (this.timeout != null) {
26424             clearTimeout(this.timeout);
26425         }
26426         
26427         this.hoverState = 'in';
26428          //Roo.log("enter - show");
26429         if (!this.delay || !this.delay.show) {
26430             this.show();
26431             return;
26432         }
26433         var _t = this;
26434         this.timeout = setTimeout(function () {
26435             if (_t.hoverState == 'in') {
26436                 _t.show();
26437             }
26438         }, this.delay.show);
26439     },
26440     leave : function()
26441     {
26442         clearTimeout(this.timeout);
26443     
26444         this.hoverState = 'out';
26445          if (!this.delay || !this.delay.hide) {
26446             this.hide();
26447             return;
26448         }
26449        
26450         var _t = this;
26451         this.timeout = setTimeout(function () {
26452             //Roo.log("leave - timeout");
26453             
26454             if (_t.hoverState == 'out') {
26455                 _t.hide();
26456                 Roo.bootstrap.Tooltip.currentEl = false;
26457             }
26458         }, delay);
26459     },
26460     
26461     show : function (msg)
26462     {
26463         if (!this.el) {
26464             this.render(document.body);
26465         }
26466         // set content.
26467         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26468         
26469         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26470         
26471         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26472         
26473         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26474         
26475         var placement = typeof this.placement == 'function' ?
26476             this.placement.call(this, this.el, on_el) :
26477             this.placement;
26478             
26479         var autoToken = /\s?auto?\s?/i;
26480         var autoPlace = autoToken.test(placement);
26481         if (autoPlace) {
26482             placement = placement.replace(autoToken, '') || 'top';
26483         }
26484         
26485         //this.el.detach()
26486         //this.el.setXY([0,0]);
26487         this.el.show();
26488         //this.el.dom.style.display='block';
26489         
26490         //this.el.appendTo(on_el);
26491         
26492         var p = this.getPosition();
26493         var box = this.el.getBox();
26494         
26495         if (autoPlace) {
26496             // fixme..
26497         }
26498         
26499         var align = this.alignment[placement];
26500         
26501         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26502         
26503         if(placement == 'top' || placement == 'bottom'){
26504             if(xy[0] < 0){
26505                 placement = 'right';
26506             }
26507             
26508             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26509                 placement = 'left';
26510             }
26511             
26512             var scroll = Roo.select('body', true).first().getScroll();
26513             
26514             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26515                 placement = 'top';
26516             }
26517             
26518             align = this.alignment[placement];
26519         }
26520         
26521         this.el.alignTo(this.bindEl, align[0],align[1]);
26522         //var arrow = this.el.select('.arrow',true).first();
26523         //arrow.set(align[2], 
26524         
26525         this.el.addClass(placement);
26526         
26527         this.el.addClass('in fade');
26528         
26529         this.hoverState = null;
26530         
26531         if (this.el.hasClass('fade')) {
26532             // fade it?
26533         }
26534         
26535     },
26536     hide : function()
26537     {
26538          
26539         if (!this.el) {
26540             return;
26541         }
26542         //this.el.setXY([0,0]);
26543         this.el.removeClass('in');
26544         //this.el.hide();
26545         
26546     }
26547     
26548 });
26549  
26550
26551  /*
26552  * - LGPL
26553  *
26554  * Location Picker
26555  * 
26556  */
26557
26558 /**
26559  * @class Roo.bootstrap.LocationPicker
26560  * @extends Roo.bootstrap.Component
26561  * Bootstrap LocationPicker class
26562  * @cfg {Number} latitude Position when init default 0
26563  * @cfg {Number} longitude Position when init default 0
26564  * @cfg {Number} zoom default 15
26565  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26566  * @cfg {Boolean} mapTypeControl default false
26567  * @cfg {Boolean} disableDoubleClickZoom default false
26568  * @cfg {Boolean} scrollwheel default true
26569  * @cfg {Boolean} streetViewControl default false
26570  * @cfg {Number} radius default 0
26571  * @cfg {String} locationName
26572  * @cfg {Boolean} draggable default true
26573  * @cfg {Boolean} enableAutocomplete default false
26574  * @cfg {Boolean} enableReverseGeocode default true
26575  * @cfg {String} markerTitle
26576  * 
26577  * @constructor
26578  * Create a new LocationPicker
26579  * @param {Object} config The config object
26580  */
26581
26582
26583 Roo.bootstrap.LocationPicker = function(config){
26584     
26585     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26586     
26587     this.addEvents({
26588         /**
26589          * @event initial
26590          * Fires when the picker initialized.
26591          * @param {Roo.bootstrap.LocationPicker} this
26592          * @param {Google Location} location
26593          */
26594         initial : true,
26595         /**
26596          * @event positionchanged
26597          * Fires when the picker position changed.
26598          * @param {Roo.bootstrap.LocationPicker} this
26599          * @param {Google Location} location
26600          */
26601         positionchanged : true,
26602         /**
26603          * @event resize
26604          * Fires when the map resize.
26605          * @param {Roo.bootstrap.LocationPicker} this
26606          */
26607         resize : true,
26608         /**
26609          * @event show
26610          * Fires when the map show.
26611          * @param {Roo.bootstrap.LocationPicker} this
26612          */
26613         show : true,
26614         /**
26615          * @event hide
26616          * Fires when the map hide.
26617          * @param {Roo.bootstrap.LocationPicker} this
26618          */
26619         hide : true,
26620         /**
26621          * @event mapClick
26622          * Fires when click the map.
26623          * @param {Roo.bootstrap.LocationPicker} this
26624          * @param {Map event} e
26625          */
26626         mapClick : true,
26627         /**
26628          * @event mapRightClick
26629          * Fires when right click the map.
26630          * @param {Roo.bootstrap.LocationPicker} this
26631          * @param {Map event} e
26632          */
26633         mapRightClick : true,
26634         /**
26635          * @event markerClick
26636          * Fires when click the marker.
26637          * @param {Roo.bootstrap.LocationPicker} this
26638          * @param {Map event} e
26639          */
26640         markerClick : true,
26641         /**
26642          * @event markerRightClick
26643          * Fires when right click the marker.
26644          * @param {Roo.bootstrap.LocationPicker} this
26645          * @param {Map event} e
26646          */
26647         markerRightClick : true,
26648         /**
26649          * @event OverlayViewDraw
26650          * Fires when OverlayView Draw
26651          * @param {Roo.bootstrap.LocationPicker} this
26652          */
26653         OverlayViewDraw : true,
26654         /**
26655          * @event OverlayViewOnAdd
26656          * Fires when OverlayView Draw
26657          * @param {Roo.bootstrap.LocationPicker} this
26658          */
26659         OverlayViewOnAdd : true,
26660         /**
26661          * @event OverlayViewOnRemove
26662          * Fires when OverlayView Draw
26663          * @param {Roo.bootstrap.LocationPicker} this
26664          */
26665         OverlayViewOnRemove : true,
26666         /**
26667          * @event OverlayViewShow
26668          * Fires when OverlayView Draw
26669          * @param {Roo.bootstrap.LocationPicker} this
26670          * @param {Pixel} cpx
26671          */
26672         OverlayViewShow : true,
26673         /**
26674          * @event OverlayViewHide
26675          * Fires when OverlayView Draw
26676          * @param {Roo.bootstrap.LocationPicker} this
26677          */
26678         OverlayViewHide : true,
26679         /**
26680          * @event loadexception
26681          * Fires when load google lib failed.
26682          * @param {Roo.bootstrap.LocationPicker} this
26683          */
26684         loadexception : true
26685     });
26686         
26687 };
26688
26689 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26690     
26691     gMapContext: false,
26692     
26693     latitude: 0,
26694     longitude: 0,
26695     zoom: 15,
26696     mapTypeId: false,
26697     mapTypeControl: false,
26698     disableDoubleClickZoom: false,
26699     scrollwheel: true,
26700     streetViewControl: false,
26701     radius: 0,
26702     locationName: '',
26703     draggable: true,
26704     enableAutocomplete: false,
26705     enableReverseGeocode: true,
26706     markerTitle: '',
26707     
26708     getAutoCreate: function()
26709     {
26710
26711         var cfg = {
26712             tag: 'div',
26713             cls: 'roo-location-picker'
26714         };
26715         
26716         return cfg
26717     },
26718     
26719     initEvents: function(ct, position)
26720     {       
26721         if(!this.el.getWidth() || this.isApplied()){
26722             return;
26723         }
26724         
26725         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26726         
26727         this.initial();
26728     },
26729     
26730     initial: function()
26731     {
26732         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26733             this.fireEvent('loadexception', this);
26734             return;
26735         }
26736         
26737         if(!this.mapTypeId){
26738             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26739         }
26740         
26741         this.gMapContext = this.GMapContext();
26742         
26743         this.initOverlayView();
26744         
26745         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26746         
26747         var _this = this;
26748                 
26749         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26750             _this.setPosition(_this.gMapContext.marker.position);
26751         });
26752         
26753         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26754             _this.fireEvent('mapClick', this, event);
26755             
26756         });
26757
26758         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26759             _this.fireEvent('mapRightClick', this, event);
26760             
26761         });
26762         
26763         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26764             _this.fireEvent('markerClick', this, event);
26765             
26766         });
26767
26768         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26769             _this.fireEvent('markerRightClick', this, event);
26770             
26771         });
26772         
26773         this.setPosition(this.gMapContext.location);
26774         
26775         this.fireEvent('initial', this, this.gMapContext.location);
26776     },
26777     
26778     initOverlayView: function()
26779     {
26780         var _this = this;
26781         
26782         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26783             
26784             draw: function()
26785             {
26786                 _this.fireEvent('OverlayViewDraw', _this);
26787             },
26788             
26789             onAdd: function()
26790             {
26791                 _this.fireEvent('OverlayViewOnAdd', _this);
26792             },
26793             
26794             onRemove: function()
26795             {
26796                 _this.fireEvent('OverlayViewOnRemove', _this);
26797             },
26798             
26799             show: function(cpx)
26800             {
26801                 _this.fireEvent('OverlayViewShow', _this, cpx);
26802             },
26803             
26804             hide: function()
26805             {
26806                 _this.fireEvent('OverlayViewHide', _this);
26807             }
26808             
26809         });
26810     },
26811     
26812     fromLatLngToContainerPixel: function(event)
26813     {
26814         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26815     },
26816     
26817     isApplied: function() 
26818     {
26819         return this.getGmapContext() == false ? false : true;
26820     },
26821     
26822     getGmapContext: function() 
26823     {
26824         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26825     },
26826     
26827     GMapContext: function() 
26828     {
26829         var position = new google.maps.LatLng(this.latitude, this.longitude);
26830         
26831         var _map = new google.maps.Map(this.el.dom, {
26832             center: position,
26833             zoom: this.zoom,
26834             mapTypeId: this.mapTypeId,
26835             mapTypeControl: this.mapTypeControl,
26836             disableDoubleClickZoom: this.disableDoubleClickZoom,
26837             scrollwheel: this.scrollwheel,
26838             streetViewControl: this.streetViewControl,
26839             locationName: this.locationName,
26840             draggable: this.draggable,
26841             enableAutocomplete: this.enableAutocomplete,
26842             enableReverseGeocode: this.enableReverseGeocode
26843         });
26844         
26845         var _marker = new google.maps.Marker({
26846             position: position,
26847             map: _map,
26848             title: this.markerTitle,
26849             draggable: this.draggable
26850         });
26851         
26852         return {
26853             map: _map,
26854             marker: _marker,
26855             circle: null,
26856             location: position,
26857             radius: this.radius,
26858             locationName: this.locationName,
26859             addressComponents: {
26860                 formatted_address: null,
26861                 addressLine1: null,
26862                 addressLine2: null,
26863                 streetName: null,
26864                 streetNumber: null,
26865                 city: null,
26866                 district: null,
26867                 state: null,
26868                 stateOrProvince: null
26869             },
26870             settings: this,
26871             domContainer: this.el.dom,
26872             geodecoder: new google.maps.Geocoder()
26873         };
26874     },
26875     
26876     drawCircle: function(center, radius, options) 
26877     {
26878         if (this.gMapContext.circle != null) {
26879             this.gMapContext.circle.setMap(null);
26880         }
26881         if (radius > 0) {
26882             radius *= 1;
26883             options = Roo.apply({}, options, {
26884                 strokeColor: "#0000FF",
26885                 strokeOpacity: .35,
26886                 strokeWeight: 2,
26887                 fillColor: "#0000FF",
26888                 fillOpacity: .2
26889             });
26890             
26891             options.map = this.gMapContext.map;
26892             options.radius = radius;
26893             options.center = center;
26894             this.gMapContext.circle = new google.maps.Circle(options);
26895             return this.gMapContext.circle;
26896         }
26897         
26898         return null;
26899     },
26900     
26901     setPosition: function(location) 
26902     {
26903         this.gMapContext.location = location;
26904         this.gMapContext.marker.setPosition(location);
26905         this.gMapContext.map.panTo(location);
26906         this.drawCircle(location, this.gMapContext.radius, {});
26907         
26908         var _this = this;
26909         
26910         if (this.gMapContext.settings.enableReverseGeocode) {
26911             this.gMapContext.geodecoder.geocode({
26912                 latLng: this.gMapContext.location
26913             }, function(results, status) {
26914                 
26915                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26916                     _this.gMapContext.locationName = results[0].formatted_address;
26917                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26918                     
26919                     _this.fireEvent('positionchanged', this, location);
26920                 }
26921             });
26922             
26923             return;
26924         }
26925         
26926         this.fireEvent('positionchanged', this, location);
26927     },
26928     
26929     resize: function()
26930     {
26931         google.maps.event.trigger(this.gMapContext.map, "resize");
26932         
26933         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26934         
26935         this.fireEvent('resize', this);
26936     },
26937     
26938     setPositionByLatLng: function(latitude, longitude)
26939     {
26940         this.setPosition(new google.maps.LatLng(latitude, longitude));
26941     },
26942     
26943     getCurrentPosition: function() 
26944     {
26945         return {
26946             latitude: this.gMapContext.location.lat(),
26947             longitude: this.gMapContext.location.lng()
26948         };
26949     },
26950     
26951     getAddressName: function() 
26952     {
26953         return this.gMapContext.locationName;
26954     },
26955     
26956     getAddressComponents: function() 
26957     {
26958         return this.gMapContext.addressComponents;
26959     },
26960     
26961     address_component_from_google_geocode: function(address_components) 
26962     {
26963         var result = {};
26964         
26965         for (var i = 0; i < address_components.length; i++) {
26966             var component = address_components[i];
26967             if (component.types.indexOf("postal_code") >= 0) {
26968                 result.postalCode = component.short_name;
26969             } else if (component.types.indexOf("street_number") >= 0) {
26970                 result.streetNumber = component.short_name;
26971             } else if (component.types.indexOf("route") >= 0) {
26972                 result.streetName = component.short_name;
26973             } else if (component.types.indexOf("neighborhood") >= 0) {
26974                 result.city = component.short_name;
26975             } else if (component.types.indexOf("locality") >= 0) {
26976                 result.city = component.short_name;
26977             } else if (component.types.indexOf("sublocality") >= 0) {
26978                 result.district = component.short_name;
26979             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26980                 result.stateOrProvince = component.short_name;
26981             } else if (component.types.indexOf("country") >= 0) {
26982                 result.country = component.short_name;
26983             }
26984         }
26985         
26986         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26987         result.addressLine2 = "";
26988         return result;
26989     },
26990     
26991     setZoomLevel: function(zoom)
26992     {
26993         this.gMapContext.map.setZoom(zoom);
26994     },
26995     
26996     show: function()
26997     {
26998         if(!this.el){
26999             return;
27000         }
27001         
27002         this.el.show();
27003         
27004         this.resize();
27005         
27006         this.fireEvent('show', this);
27007     },
27008     
27009     hide: function()
27010     {
27011         if(!this.el){
27012             return;
27013         }
27014         
27015         this.el.hide();
27016         
27017         this.fireEvent('hide', this);
27018     }
27019     
27020 });
27021
27022 Roo.apply(Roo.bootstrap.LocationPicker, {
27023     
27024     OverlayView : function(map, options)
27025     {
27026         options = options || {};
27027         
27028         this.setMap(map);
27029     }
27030     
27031     
27032 });/*
27033  * - LGPL
27034  *
27035  * Alert
27036  * 
27037  */
27038
27039 /**
27040  * @class Roo.bootstrap.Alert
27041  * @extends Roo.bootstrap.Component
27042  * Bootstrap Alert class
27043  * @cfg {String} title The title of alert
27044  * @cfg {String} html The content of alert
27045  * @cfg {String} weight (  success | info | warning | danger )
27046  * @cfg {String} faicon font-awesomeicon
27047  * 
27048  * @constructor
27049  * Create a new alert
27050  * @param {Object} config The config object
27051  */
27052
27053
27054 Roo.bootstrap.Alert = function(config){
27055     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27056     
27057 };
27058
27059 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27060     
27061     title: '',
27062     html: '',
27063     weight: false,
27064     faicon: false,
27065     
27066     getAutoCreate : function()
27067     {
27068         
27069         var cfg = {
27070             tag : 'div',
27071             cls : 'alert',
27072             cn : [
27073                 {
27074                     tag : 'i',
27075                     cls : 'roo-alert-icon'
27076                     
27077                 },
27078                 {
27079                     tag : 'b',
27080                     cls : 'roo-alert-title',
27081                     html : this.title
27082                 },
27083                 {
27084                     tag : 'span',
27085                     cls : 'roo-alert-text',
27086                     html : this.html
27087                 }
27088             ]
27089         };
27090         
27091         if(this.faicon){
27092             cfg.cn[0].cls += ' fa ' + this.faicon;
27093         }
27094         
27095         if(this.weight){
27096             cfg.cls += ' alert-' + this.weight;
27097         }
27098         
27099         return cfg;
27100     },
27101     
27102     initEvents: function() 
27103     {
27104         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27105     },
27106     
27107     setTitle : function(str)
27108     {
27109         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27110     },
27111     
27112     setText : function(str)
27113     {
27114         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27115     },
27116     
27117     setWeight : function(weight)
27118     {
27119         if(this.weight){
27120             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27121         }
27122         
27123         this.weight = weight;
27124         
27125         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27126     },
27127     
27128     setIcon : function(icon)
27129     {
27130         if(this.faicon){
27131             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27132         }
27133         
27134         this.faicon = icon;
27135         
27136         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27137     },
27138     
27139     hide: function() 
27140     {
27141         this.el.hide();   
27142     },
27143     
27144     show: function() 
27145     {  
27146         this.el.show();   
27147     }
27148     
27149 });
27150
27151  
27152 /*
27153 * Licence: LGPL
27154 */
27155
27156 /**
27157  * @class Roo.bootstrap.UploadCropbox
27158  * @extends Roo.bootstrap.Component
27159  * Bootstrap UploadCropbox class
27160  * @cfg {String} emptyText show when image has been loaded
27161  * @cfg {String} rotateNotify show when image too small to rotate
27162  * @cfg {Number} errorTimeout default 3000
27163  * @cfg {Number} minWidth default 300
27164  * @cfg {Number} minHeight default 300
27165  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27166  * @cfg {Boolean} isDocument (true|false) default false
27167  * @cfg {String} url action url
27168  * @cfg {String} paramName default 'imageUpload'
27169  * @cfg {String} method default POST
27170  * @cfg {Boolean} loadMask (true|false) default true
27171  * @cfg {Boolean} loadingText default 'Loading...'
27172  * 
27173  * @constructor
27174  * Create a new UploadCropbox
27175  * @param {Object} config The config object
27176  */
27177
27178 Roo.bootstrap.UploadCropbox = function(config){
27179     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27180     
27181     this.addEvents({
27182         /**
27183          * @event beforeselectfile
27184          * Fire before select file
27185          * @param {Roo.bootstrap.UploadCropbox} this
27186          */
27187         "beforeselectfile" : true,
27188         /**
27189          * @event initial
27190          * Fire after initEvent
27191          * @param {Roo.bootstrap.UploadCropbox} this
27192          */
27193         "initial" : true,
27194         /**
27195          * @event crop
27196          * Fire after initEvent
27197          * @param {Roo.bootstrap.UploadCropbox} this
27198          * @param {String} data
27199          */
27200         "crop" : true,
27201         /**
27202          * @event prepare
27203          * Fire when preparing the file data
27204          * @param {Roo.bootstrap.UploadCropbox} this
27205          * @param {Object} file
27206          */
27207         "prepare" : true,
27208         /**
27209          * @event exception
27210          * Fire when get exception
27211          * @param {Roo.bootstrap.UploadCropbox} this
27212          * @param {XMLHttpRequest} xhr
27213          */
27214         "exception" : true,
27215         /**
27216          * @event beforeloadcanvas
27217          * Fire before load the canvas
27218          * @param {Roo.bootstrap.UploadCropbox} this
27219          * @param {String} src
27220          */
27221         "beforeloadcanvas" : true,
27222         /**
27223          * @event trash
27224          * Fire when trash image
27225          * @param {Roo.bootstrap.UploadCropbox} this
27226          */
27227         "trash" : true,
27228         /**
27229          * @event download
27230          * Fire when download the image
27231          * @param {Roo.bootstrap.UploadCropbox} this
27232          */
27233         "download" : true,
27234         /**
27235          * @event footerbuttonclick
27236          * Fire when footerbuttonclick
27237          * @param {Roo.bootstrap.UploadCropbox} this
27238          * @param {String} type
27239          */
27240         "footerbuttonclick" : true,
27241         /**
27242          * @event resize
27243          * Fire when resize
27244          * @param {Roo.bootstrap.UploadCropbox} this
27245          */
27246         "resize" : true,
27247         /**
27248          * @event rotate
27249          * Fire when rotate the image
27250          * @param {Roo.bootstrap.UploadCropbox} this
27251          * @param {String} pos
27252          */
27253         "rotate" : true,
27254         /**
27255          * @event inspect
27256          * Fire when inspect the file
27257          * @param {Roo.bootstrap.UploadCropbox} this
27258          * @param {Object} file
27259          */
27260         "inspect" : true,
27261         /**
27262          * @event upload
27263          * Fire when xhr upload the file
27264          * @param {Roo.bootstrap.UploadCropbox} this
27265          * @param {Object} data
27266          */
27267         "upload" : true,
27268         /**
27269          * @event arrange
27270          * Fire when arrange the file data
27271          * @param {Roo.bootstrap.UploadCropbox} this
27272          * @param {Object} formData
27273          */
27274         "arrange" : true
27275     });
27276     
27277     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27278 };
27279
27280 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27281     
27282     emptyText : 'Click to upload image',
27283     rotateNotify : 'Image is too small to rotate',
27284     errorTimeout : 3000,
27285     scale : 0,
27286     baseScale : 1,
27287     rotate : 0,
27288     dragable : false,
27289     pinching : false,
27290     mouseX : 0,
27291     mouseY : 0,
27292     cropData : false,
27293     minWidth : 300,
27294     minHeight : 300,
27295     file : false,
27296     exif : {},
27297     baseRotate : 1,
27298     cropType : 'image/jpeg',
27299     buttons : false,
27300     canvasLoaded : false,
27301     isDocument : false,
27302     method : 'POST',
27303     paramName : 'imageUpload',
27304     loadMask : true,
27305     loadingText : 'Loading...',
27306     maskEl : false,
27307     
27308     getAutoCreate : function()
27309     {
27310         var cfg = {
27311             tag : 'div',
27312             cls : 'roo-upload-cropbox',
27313             cn : [
27314                 {
27315                     tag : 'input',
27316                     cls : 'roo-upload-cropbox-selector',
27317                     type : 'file'
27318                 },
27319                 {
27320                     tag : 'div',
27321                     cls : 'roo-upload-cropbox-body',
27322                     style : 'cursor:pointer',
27323                     cn : [
27324                         {
27325                             tag : 'div',
27326                             cls : 'roo-upload-cropbox-preview'
27327                         },
27328                         {
27329                             tag : 'div',
27330                             cls : 'roo-upload-cropbox-thumb'
27331                         },
27332                         {
27333                             tag : 'div',
27334                             cls : 'roo-upload-cropbox-empty-notify',
27335                             html : this.emptyText
27336                         },
27337                         {
27338                             tag : 'div',
27339                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27340                             html : this.rotateNotify
27341                         }
27342                     ]
27343                 },
27344                 {
27345                     tag : 'div',
27346                     cls : 'roo-upload-cropbox-footer',
27347                     cn : {
27348                         tag : 'div',
27349                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27350                         cn : []
27351                     }
27352                 }
27353             ]
27354         };
27355         
27356         return cfg;
27357     },
27358     
27359     onRender : function(ct, position)
27360     {
27361         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27362         
27363         if (this.buttons.length) {
27364             
27365             Roo.each(this.buttons, function(bb) {
27366                 
27367                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27368                 
27369                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27370                 
27371             }, this);
27372         }
27373         
27374         if(this.loadMask){
27375             this.maskEl = this.el;
27376         }
27377     },
27378     
27379     initEvents : function()
27380     {
27381         this.urlAPI = (window.createObjectURL && window) || 
27382                                 (window.URL && URL.revokeObjectURL && URL) || 
27383                                 (window.webkitURL && webkitURL);
27384                         
27385         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27386         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27387         
27388         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27389         this.selectorEl.hide();
27390         
27391         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27392         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27393         
27394         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27395         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27396         this.thumbEl.hide();
27397         
27398         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27399         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27400         
27401         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27402         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27403         this.errorEl.hide();
27404         
27405         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27406         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27407         this.footerEl.hide();
27408         
27409         this.setThumbBoxSize();
27410         
27411         this.bind();
27412         
27413         this.resize();
27414         
27415         this.fireEvent('initial', this);
27416     },
27417
27418     bind : function()
27419     {
27420         var _this = this;
27421         
27422         window.addEventListener("resize", function() { _this.resize(); } );
27423         
27424         this.bodyEl.on('click', this.beforeSelectFile, this);
27425         
27426         if(Roo.isTouch){
27427             this.bodyEl.on('touchstart', this.onTouchStart, this);
27428             this.bodyEl.on('touchmove', this.onTouchMove, this);
27429             this.bodyEl.on('touchend', this.onTouchEnd, this);
27430         }
27431         
27432         if(!Roo.isTouch){
27433             this.bodyEl.on('mousedown', this.onMouseDown, this);
27434             this.bodyEl.on('mousemove', this.onMouseMove, this);
27435             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27436             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27437             Roo.get(document).on('mouseup', this.onMouseUp, this);
27438         }
27439         
27440         this.selectorEl.on('change', this.onFileSelected, this);
27441     },
27442     
27443     reset : function()
27444     {    
27445         this.scale = 0;
27446         this.baseScale = 1;
27447         this.rotate = 0;
27448         this.baseRotate = 1;
27449         this.dragable = false;
27450         this.pinching = false;
27451         this.mouseX = 0;
27452         this.mouseY = 0;
27453         this.cropData = false;
27454         this.notifyEl.dom.innerHTML = this.emptyText;
27455         
27456         this.selectorEl.dom.value = '';
27457         
27458     },
27459     
27460     resize : function()
27461     {
27462         if(this.fireEvent('resize', this) != false){
27463             this.setThumbBoxPosition();
27464             this.setCanvasPosition();
27465         }
27466     },
27467     
27468     onFooterButtonClick : function(e, el, o, type)
27469     {
27470         switch (type) {
27471             case 'rotate-left' :
27472                 this.onRotateLeft(e);
27473                 break;
27474             case 'rotate-right' :
27475                 this.onRotateRight(e);
27476                 break;
27477             case 'picture' :
27478                 this.beforeSelectFile(e);
27479                 break;
27480             case 'trash' :
27481                 this.trash(e);
27482                 break;
27483             case 'crop' :
27484                 this.crop(e);
27485                 break;
27486             case 'download' :
27487                 this.download(e);
27488                 break;
27489             default :
27490                 break;
27491         }
27492         
27493         this.fireEvent('footerbuttonclick', this, type);
27494     },
27495     
27496     beforeSelectFile : function(e)
27497     {
27498         e.preventDefault();
27499         
27500         if(this.fireEvent('beforeselectfile', this) != false){
27501             this.selectorEl.dom.click();
27502         }
27503     },
27504     
27505     onFileSelected : function(e)
27506     {
27507         e.preventDefault();
27508         
27509         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27510             return;
27511         }
27512         
27513         var file = this.selectorEl.dom.files[0];
27514         
27515         if(this.fireEvent('inspect', this, file) != false){
27516             this.prepare(file);
27517         }
27518         
27519     },
27520     
27521     trash : function(e)
27522     {
27523         this.fireEvent('trash', this);
27524     },
27525     
27526     download : function(e)
27527     {
27528         this.fireEvent('download', this);
27529     },
27530     
27531     loadCanvas : function(src)
27532     {   
27533         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27534             
27535             this.reset();
27536             
27537             this.imageEl = document.createElement('img');
27538             
27539             var _this = this;
27540             
27541             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27542             
27543             this.imageEl.src = src;
27544         }
27545     },
27546     
27547     onLoadCanvas : function()
27548     {   
27549         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27550         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27551         
27552         this.bodyEl.un('click', this.beforeSelectFile, this);
27553         
27554         this.notifyEl.hide();
27555         this.thumbEl.show();
27556         this.footerEl.show();
27557         
27558         this.baseRotateLevel();
27559         
27560         if(this.isDocument){
27561             this.setThumbBoxSize();
27562         }
27563         
27564         this.setThumbBoxPosition();
27565         
27566         this.baseScaleLevel();
27567         
27568         this.draw();
27569         
27570         this.resize();
27571         
27572         this.canvasLoaded = true;
27573         
27574         if(this.loadMask){
27575             this.maskEl.unmask();
27576         }
27577         
27578     },
27579     
27580     setCanvasPosition : function()
27581     {   
27582         if(!this.canvasEl){
27583             return;
27584         }
27585         
27586         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27587         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27588         
27589         this.previewEl.setLeft(pw);
27590         this.previewEl.setTop(ph);
27591         
27592     },
27593     
27594     onMouseDown : function(e)
27595     {   
27596         e.stopEvent();
27597         
27598         this.dragable = true;
27599         this.pinching = false;
27600         
27601         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27602             this.dragable = false;
27603             return;
27604         }
27605         
27606         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27607         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27608         
27609     },
27610     
27611     onMouseMove : function(e)
27612     {   
27613         e.stopEvent();
27614         
27615         if(!this.canvasLoaded){
27616             return;
27617         }
27618         
27619         if (!this.dragable){
27620             return;
27621         }
27622         
27623         var minX = Math.ceil(this.thumbEl.getLeft(true));
27624         var minY = Math.ceil(this.thumbEl.getTop(true));
27625         
27626         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27627         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27628         
27629         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27630         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27631         
27632         x = x - this.mouseX;
27633         y = y - this.mouseY;
27634         
27635         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27636         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27637         
27638         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27639         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27640         
27641         this.previewEl.setLeft(bgX);
27642         this.previewEl.setTop(bgY);
27643         
27644         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27645         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27646     },
27647     
27648     onMouseUp : function(e)
27649     {   
27650         e.stopEvent();
27651         
27652         this.dragable = false;
27653     },
27654     
27655     onMouseWheel : function(e)
27656     {   
27657         e.stopEvent();
27658         
27659         this.startScale = this.scale;
27660         
27661         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27662         
27663         if(!this.zoomable()){
27664             this.scale = this.startScale;
27665             return;
27666         }
27667         
27668         this.draw();
27669         
27670         return;
27671     },
27672     
27673     zoomable : function()
27674     {
27675         var minScale = this.thumbEl.getWidth() / this.minWidth;
27676         
27677         if(this.minWidth < this.minHeight){
27678             minScale = this.thumbEl.getHeight() / this.minHeight;
27679         }
27680         
27681         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27682         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27683         
27684         if(
27685                 this.isDocument &&
27686                 (this.rotate == 0 || this.rotate == 180) && 
27687                 (
27688                     width > this.imageEl.OriginWidth || 
27689                     height > this.imageEl.OriginHeight ||
27690                     (width < this.minWidth && height < this.minHeight)
27691                 )
27692         ){
27693             return false;
27694         }
27695         
27696         if(
27697                 this.isDocument &&
27698                 (this.rotate == 90 || this.rotate == 270) && 
27699                 (
27700                     width > this.imageEl.OriginWidth || 
27701                     height > this.imageEl.OriginHeight ||
27702                     (width < this.minHeight && height < this.minWidth)
27703                 )
27704         ){
27705             return false;
27706         }
27707         
27708         if(
27709                 !this.isDocument &&
27710                 (this.rotate == 0 || this.rotate == 180) && 
27711                 (
27712                     width < this.minWidth || 
27713                     width > this.imageEl.OriginWidth || 
27714                     height < this.minHeight || 
27715                     height > this.imageEl.OriginHeight
27716                 )
27717         ){
27718             return false;
27719         }
27720         
27721         if(
27722                 !this.isDocument &&
27723                 (this.rotate == 90 || this.rotate == 270) && 
27724                 (
27725                     width < this.minHeight || 
27726                     width > this.imageEl.OriginWidth || 
27727                     height < this.minWidth || 
27728                     height > this.imageEl.OriginHeight
27729                 )
27730         ){
27731             return false;
27732         }
27733         
27734         return true;
27735         
27736     },
27737     
27738     onRotateLeft : function(e)
27739     {   
27740         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27741             
27742             var minScale = this.thumbEl.getWidth() / this.minWidth;
27743             
27744             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27745             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27746             
27747             this.startScale = this.scale;
27748             
27749             while (this.getScaleLevel() < minScale){
27750             
27751                 this.scale = this.scale + 1;
27752                 
27753                 if(!this.zoomable()){
27754                     break;
27755                 }
27756                 
27757                 if(
27758                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27759                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27760                 ){
27761                     continue;
27762                 }
27763                 
27764                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27765
27766                 this.draw();
27767                 
27768                 return;
27769             }
27770             
27771             this.scale = this.startScale;
27772             
27773             this.onRotateFail();
27774             
27775             return false;
27776         }
27777         
27778         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27779
27780         if(this.isDocument){
27781             this.setThumbBoxSize();
27782             this.setThumbBoxPosition();
27783             this.setCanvasPosition();
27784         }
27785         
27786         this.draw();
27787         
27788         this.fireEvent('rotate', this, 'left');
27789         
27790     },
27791     
27792     onRotateRight : function(e)
27793     {
27794         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27795             
27796             var minScale = this.thumbEl.getWidth() / this.minWidth;
27797         
27798             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27799             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27800             
27801             this.startScale = this.scale;
27802             
27803             while (this.getScaleLevel() < minScale){
27804             
27805                 this.scale = this.scale + 1;
27806                 
27807                 if(!this.zoomable()){
27808                     break;
27809                 }
27810                 
27811                 if(
27812                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27813                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27814                 ){
27815                     continue;
27816                 }
27817                 
27818                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27819
27820                 this.draw();
27821                 
27822                 return;
27823             }
27824             
27825             this.scale = this.startScale;
27826             
27827             this.onRotateFail();
27828             
27829             return false;
27830         }
27831         
27832         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27833
27834         if(this.isDocument){
27835             this.setThumbBoxSize();
27836             this.setThumbBoxPosition();
27837             this.setCanvasPosition();
27838         }
27839         
27840         this.draw();
27841         
27842         this.fireEvent('rotate', this, 'right');
27843     },
27844     
27845     onRotateFail : function()
27846     {
27847         this.errorEl.show(true);
27848         
27849         var _this = this;
27850         
27851         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27852     },
27853     
27854     draw : function()
27855     {
27856         this.previewEl.dom.innerHTML = '';
27857         
27858         var canvasEl = document.createElement("canvas");
27859         
27860         var contextEl = canvasEl.getContext("2d");
27861         
27862         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27863         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27864         var center = this.imageEl.OriginWidth / 2;
27865         
27866         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27867             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27868             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27869             center = this.imageEl.OriginHeight / 2;
27870         }
27871         
27872         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27873         
27874         contextEl.translate(center, center);
27875         contextEl.rotate(this.rotate * Math.PI / 180);
27876
27877         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27878         
27879         this.canvasEl = document.createElement("canvas");
27880         
27881         this.contextEl = this.canvasEl.getContext("2d");
27882         
27883         switch (this.rotate) {
27884             case 0 :
27885                 
27886                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27887                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27888                 
27889                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27890                 
27891                 break;
27892             case 90 : 
27893                 
27894                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27895                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27896                 
27897                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27898                     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);
27899                     break;
27900                 }
27901                 
27902                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27903                 
27904                 break;
27905             case 180 :
27906                 
27907                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27908                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27909                 
27910                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27911                     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);
27912                     break;
27913                 }
27914                 
27915                 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);
27916                 
27917                 break;
27918             case 270 :
27919                 
27920                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27921                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27922         
27923                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27924                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27925                     break;
27926                 }
27927                 
27928                 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);
27929                 
27930                 break;
27931             default : 
27932                 break;
27933         }
27934         
27935         this.previewEl.appendChild(this.canvasEl);
27936         
27937         this.setCanvasPosition();
27938     },
27939     
27940     crop : function()
27941     {
27942         if(!this.canvasLoaded){
27943             return;
27944         }
27945         
27946         var imageCanvas = document.createElement("canvas");
27947         
27948         var imageContext = imageCanvas.getContext("2d");
27949         
27950         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27951         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27952         
27953         var center = imageCanvas.width / 2;
27954         
27955         imageContext.translate(center, center);
27956         
27957         imageContext.rotate(this.rotate * Math.PI / 180);
27958         
27959         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27960         
27961         var canvas = document.createElement("canvas");
27962         
27963         var context = canvas.getContext("2d");
27964                 
27965         canvas.width = this.minWidth;
27966         canvas.height = this.minHeight;
27967
27968         switch (this.rotate) {
27969             case 0 :
27970                 
27971                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27972                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27973                 
27974                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27975                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27976                 
27977                 var targetWidth = this.minWidth - 2 * x;
27978                 var targetHeight = this.minHeight - 2 * y;
27979                 
27980                 var scale = 1;
27981                 
27982                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27983                     scale = targetWidth / width;
27984                 }
27985                 
27986                 if(x > 0 && y == 0){
27987                     scale = targetHeight / height;
27988                 }
27989                 
27990                 if(x > 0 && y > 0){
27991                     scale = targetWidth / width;
27992                     
27993                     if(width < height){
27994                         scale = targetHeight / height;
27995                     }
27996                 }
27997                 
27998                 context.scale(scale, scale);
27999                 
28000                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28001                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28002
28003                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28004                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28005
28006                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28007                 
28008                 break;
28009             case 90 : 
28010                 
28011                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28012                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28013                 
28014                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28015                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28016                 
28017                 var targetWidth = this.minWidth - 2 * x;
28018                 var targetHeight = this.minHeight - 2 * y;
28019                 
28020                 var scale = 1;
28021                 
28022                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28023                     scale = targetWidth / width;
28024                 }
28025                 
28026                 if(x > 0 && y == 0){
28027                     scale = targetHeight / height;
28028                 }
28029                 
28030                 if(x > 0 && y > 0){
28031                     scale = targetWidth / width;
28032                     
28033                     if(width < height){
28034                         scale = targetHeight / height;
28035                     }
28036                 }
28037                 
28038                 context.scale(scale, scale);
28039                 
28040                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28041                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28042
28043                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28044                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28045                 
28046                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28047                 
28048                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28049                 
28050                 break;
28051             case 180 :
28052                 
28053                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28054                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28055                 
28056                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28057                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28058                 
28059                 var targetWidth = this.minWidth - 2 * x;
28060                 var targetHeight = this.minHeight - 2 * y;
28061                 
28062                 var scale = 1;
28063                 
28064                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28065                     scale = targetWidth / width;
28066                 }
28067                 
28068                 if(x > 0 && y == 0){
28069                     scale = targetHeight / height;
28070                 }
28071                 
28072                 if(x > 0 && y > 0){
28073                     scale = targetWidth / width;
28074                     
28075                     if(width < height){
28076                         scale = targetHeight / height;
28077                     }
28078                 }
28079                 
28080                 context.scale(scale, scale);
28081                 
28082                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28083                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28084
28085                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28086                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28087
28088                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28089                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28090                 
28091                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28092                 
28093                 break;
28094             case 270 :
28095                 
28096                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28097                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28098                 
28099                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28100                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28101                 
28102                 var targetWidth = this.minWidth - 2 * x;
28103                 var targetHeight = this.minHeight - 2 * y;
28104                 
28105                 var scale = 1;
28106                 
28107                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28108                     scale = targetWidth / width;
28109                 }
28110                 
28111                 if(x > 0 && y == 0){
28112                     scale = targetHeight / height;
28113                 }
28114                 
28115                 if(x > 0 && y > 0){
28116                     scale = targetWidth / width;
28117                     
28118                     if(width < height){
28119                         scale = targetHeight / height;
28120                     }
28121                 }
28122                 
28123                 context.scale(scale, scale);
28124                 
28125                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28126                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28127
28128                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28129                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28130                 
28131                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28132                 
28133                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28134                 
28135                 break;
28136             default : 
28137                 break;
28138         }
28139         
28140         this.cropData = canvas.toDataURL(this.cropType);
28141         
28142         if(this.fireEvent('crop', this, this.cropData) !== false){
28143             this.process(this.file, this.cropData);
28144         }
28145         
28146         return;
28147         
28148     },
28149     
28150     setThumbBoxSize : function()
28151     {
28152         var width, height;
28153         
28154         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28155             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28156             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28157             
28158             this.minWidth = width;
28159             this.minHeight = height;
28160             
28161             if(this.rotate == 90 || this.rotate == 270){
28162                 this.minWidth = height;
28163                 this.minHeight = width;
28164             }
28165         }
28166         
28167         height = 300;
28168         width = Math.ceil(this.minWidth * height / this.minHeight);
28169         
28170         if(this.minWidth > this.minHeight){
28171             width = 300;
28172             height = Math.ceil(this.minHeight * width / this.minWidth);
28173         }
28174         
28175         this.thumbEl.setStyle({
28176             width : width + 'px',
28177             height : height + 'px'
28178         });
28179
28180         return;
28181             
28182     },
28183     
28184     setThumbBoxPosition : function()
28185     {
28186         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28187         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28188         
28189         this.thumbEl.setLeft(x);
28190         this.thumbEl.setTop(y);
28191         
28192     },
28193     
28194     baseRotateLevel : function()
28195     {
28196         this.baseRotate = 1;
28197         
28198         if(
28199                 typeof(this.exif) != 'undefined' &&
28200                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28201                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28202         ){
28203             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28204         }
28205         
28206         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28207         
28208     },
28209     
28210     baseScaleLevel : function()
28211     {
28212         var width, height;
28213         
28214         if(this.isDocument){
28215             
28216             if(this.baseRotate == 6 || this.baseRotate == 8){
28217             
28218                 height = this.thumbEl.getHeight();
28219                 this.baseScale = height / this.imageEl.OriginWidth;
28220
28221                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28222                     width = this.thumbEl.getWidth();
28223                     this.baseScale = width / this.imageEl.OriginHeight;
28224                 }
28225
28226                 return;
28227             }
28228
28229             height = this.thumbEl.getHeight();
28230             this.baseScale = height / this.imageEl.OriginHeight;
28231
28232             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28233                 width = this.thumbEl.getWidth();
28234                 this.baseScale = width / this.imageEl.OriginWidth;
28235             }
28236
28237             return;
28238         }
28239         
28240         if(this.baseRotate == 6 || this.baseRotate == 8){
28241             
28242             width = this.thumbEl.getHeight();
28243             this.baseScale = width / this.imageEl.OriginHeight;
28244             
28245             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28246                 height = this.thumbEl.getWidth();
28247                 this.baseScale = height / this.imageEl.OriginHeight;
28248             }
28249             
28250             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28251                 height = this.thumbEl.getWidth();
28252                 this.baseScale = height / this.imageEl.OriginHeight;
28253                 
28254                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28255                     width = this.thumbEl.getHeight();
28256                     this.baseScale = width / this.imageEl.OriginWidth;
28257                 }
28258             }
28259             
28260             return;
28261         }
28262         
28263         width = this.thumbEl.getWidth();
28264         this.baseScale = width / this.imageEl.OriginWidth;
28265         
28266         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28267             height = this.thumbEl.getHeight();
28268             this.baseScale = height / this.imageEl.OriginHeight;
28269         }
28270         
28271         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28272             
28273             height = this.thumbEl.getHeight();
28274             this.baseScale = height / this.imageEl.OriginHeight;
28275             
28276             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28277                 width = this.thumbEl.getWidth();
28278                 this.baseScale = width / this.imageEl.OriginWidth;
28279             }
28280             
28281         }
28282         
28283         return;
28284     },
28285     
28286     getScaleLevel : function()
28287     {
28288         return this.baseScale * Math.pow(1.1, this.scale);
28289     },
28290     
28291     onTouchStart : function(e)
28292     {
28293         if(!this.canvasLoaded){
28294             this.beforeSelectFile(e);
28295             return;
28296         }
28297         
28298         var touches = e.browserEvent.touches;
28299         
28300         if(!touches){
28301             return;
28302         }
28303         
28304         if(touches.length == 1){
28305             this.onMouseDown(e);
28306             return;
28307         }
28308         
28309         if(touches.length != 2){
28310             return;
28311         }
28312         
28313         var coords = [];
28314         
28315         for(var i = 0, finger; finger = touches[i]; i++){
28316             coords.push(finger.pageX, finger.pageY);
28317         }
28318         
28319         var x = Math.pow(coords[0] - coords[2], 2);
28320         var y = Math.pow(coords[1] - coords[3], 2);
28321         
28322         this.startDistance = Math.sqrt(x + y);
28323         
28324         this.startScale = this.scale;
28325         
28326         this.pinching = true;
28327         this.dragable = false;
28328         
28329     },
28330     
28331     onTouchMove : function(e)
28332     {
28333         if(!this.pinching && !this.dragable){
28334             return;
28335         }
28336         
28337         var touches = e.browserEvent.touches;
28338         
28339         if(!touches){
28340             return;
28341         }
28342         
28343         if(this.dragable){
28344             this.onMouseMove(e);
28345             return;
28346         }
28347         
28348         var coords = [];
28349         
28350         for(var i = 0, finger; finger = touches[i]; i++){
28351             coords.push(finger.pageX, finger.pageY);
28352         }
28353         
28354         var x = Math.pow(coords[0] - coords[2], 2);
28355         var y = Math.pow(coords[1] - coords[3], 2);
28356         
28357         this.endDistance = Math.sqrt(x + y);
28358         
28359         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28360         
28361         if(!this.zoomable()){
28362             this.scale = this.startScale;
28363             return;
28364         }
28365         
28366         this.draw();
28367         
28368     },
28369     
28370     onTouchEnd : function(e)
28371     {
28372         this.pinching = false;
28373         this.dragable = false;
28374         
28375     },
28376     
28377     process : function(file, crop)
28378     {
28379         if(this.loadMask){
28380             this.maskEl.mask(this.loadingText);
28381         }
28382         
28383         this.xhr = new XMLHttpRequest();
28384         
28385         file.xhr = this.xhr;
28386
28387         this.xhr.open(this.method, this.url, true);
28388         
28389         var headers = {
28390             "Accept": "application/json",
28391             "Cache-Control": "no-cache",
28392             "X-Requested-With": "XMLHttpRequest"
28393         };
28394         
28395         for (var headerName in headers) {
28396             var headerValue = headers[headerName];
28397             if (headerValue) {
28398                 this.xhr.setRequestHeader(headerName, headerValue);
28399             }
28400         }
28401         
28402         var _this = this;
28403         
28404         this.xhr.onload = function()
28405         {
28406             _this.xhrOnLoad(_this.xhr);
28407         }
28408         
28409         this.xhr.onerror = function()
28410         {
28411             _this.xhrOnError(_this.xhr);
28412         }
28413         
28414         var formData = new FormData();
28415
28416         formData.append('returnHTML', 'NO');
28417         
28418         if(crop){
28419             formData.append('crop', crop);
28420         }
28421         
28422         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28423             formData.append(this.paramName, file, file.name);
28424         }
28425         
28426         if(typeof(file.filename) != 'undefined'){
28427             formData.append('filename', file.filename);
28428         }
28429         
28430         if(typeof(file.mimetype) != 'undefined'){
28431             formData.append('mimetype', file.mimetype);
28432         }
28433         
28434         if(this.fireEvent('arrange', this, formData) != false){
28435             this.xhr.send(formData);
28436         };
28437     },
28438     
28439     xhrOnLoad : function(xhr)
28440     {
28441         if(this.loadMask){
28442             this.maskEl.unmask();
28443         }
28444         
28445         if (xhr.readyState !== 4) {
28446             this.fireEvent('exception', this, xhr);
28447             return;
28448         }
28449
28450         var response = Roo.decode(xhr.responseText);
28451         
28452         if(!response.success){
28453             this.fireEvent('exception', this, xhr);
28454             return;
28455         }
28456         
28457         var response = Roo.decode(xhr.responseText);
28458         
28459         this.fireEvent('upload', this, response);
28460         
28461     },
28462     
28463     xhrOnError : function()
28464     {
28465         if(this.loadMask){
28466             this.maskEl.unmask();
28467         }
28468         
28469         Roo.log('xhr on error');
28470         
28471         var response = Roo.decode(xhr.responseText);
28472           
28473         Roo.log(response);
28474         
28475     },
28476     
28477     prepare : function(file)
28478     {   
28479         if(this.loadMask){
28480             this.maskEl.mask(this.loadingText);
28481         }
28482         
28483         this.file = false;
28484         this.exif = {};
28485         
28486         if(typeof(file) === 'string'){
28487             this.loadCanvas(file);
28488             return;
28489         }
28490         
28491         if(!file || !this.urlAPI){
28492             return;
28493         }
28494         
28495         this.file = file;
28496         this.cropType = file.type;
28497         
28498         var _this = this;
28499         
28500         if(this.fireEvent('prepare', this, this.file) != false){
28501             
28502             var reader = new FileReader();
28503             
28504             reader.onload = function (e) {
28505                 if (e.target.error) {
28506                     Roo.log(e.target.error);
28507                     return;
28508                 }
28509                 
28510                 var buffer = e.target.result,
28511                     dataView = new DataView(buffer),
28512                     offset = 2,
28513                     maxOffset = dataView.byteLength - 4,
28514                     markerBytes,
28515                     markerLength;
28516                 
28517                 if (dataView.getUint16(0) === 0xffd8) {
28518                     while (offset < maxOffset) {
28519                         markerBytes = dataView.getUint16(offset);
28520                         
28521                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28522                             markerLength = dataView.getUint16(offset + 2) + 2;
28523                             if (offset + markerLength > dataView.byteLength) {
28524                                 Roo.log('Invalid meta data: Invalid segment size.');
28525                                 break;
28526                             }
28527                             
28528                             if(markerBytes == 0xffe1){
28529                                 _this.parseExifData(
28530                                     dataView,
28531                                     offset,
28532                                     markerLength
28533                                 );
28534                             }
28535                             
28536                             offset += markerLength;
28537                             
28538                             continue;
28539                         }
28540                         
28541                         break;
28542                     }
28543                     
28544                 }
28545                 
28546                 var url = _this.urlAPI.createObjectURL(_this.file);
28547                 
28548                 _this.loadCanvas(url);
28549                 
28550                 return;
28551             }
28552             
28553             reader.readAsArrayBuffer(this.file);
28554             
28555         }
28556         
28557     },
28558     
28559     parseExifData : function(dataView, offset, length)
28560     {
28561         var tiffOffset = offset + 10,
28562             littleEndian,
28563             dirOffset;
28564     
28565         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28566             // No Exif data, might be XMP data instead
28567             return;
28568         }
28569         
28570         // Check for the ASCII code for "Exif" (0x45786966):
28571         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28572             // No Exif data, might be XMP data instead
28573             return;
28574         }
28575         if (tiffOffset + 8 > dataView.byteLength) {
28576             Roo.log('Invalid Exif data: Invalid segment size.');
28577             return;
28578         }
28579         // Check for the two null bytes:
28580         if (dataView.getUint16(offset + 8) !== 0x0000) {
28581             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28582             return;
28583         }
28584         // Check the byte alignment:
28585         switch (dataView.getUint16(tiffOffset)) {
28586         case 0x4949:
28587             littleEndian = true;
28588             break;
28589         case 0x4D4D:
28590             littleEndian = false;
28591             break;
28592         default:
28593             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28594             return;
28595         }
28596         // Check for the TIFF tag marker (0x002A):
28597         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28598             Roo.log('Invalid Exif data: Missing TIFF marker.');
28599             return;
28600         }
28601         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28602         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28603         
28604         this.parseExifTags(
28605             dataView,
28606             tiffOffset,
28607             tiffOffset + dirOffset,
28608             littleEndian
28609         );
28610     },
28611     
28612     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28613     {
28614         var tagsNumber,
28615             dirEndOffset,
28616             i;
28617         if (dirOffset + 6 > dataView.byteLength) {
28618             Roo.log('Invalid Exif data: Invalid directory offset.');
28619             return;
28620         }
28621         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28622         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28623         if (dirEndOffset + 4 > dataView.byteLength) {
28624             Roo.log('Invalid Exif data: Invalid directory size.');
28625             return;
28626         }
28627         for (i = 0; i < tagsNumber; i += 1) {
28628             this.parseExifTag(
28629                 dataView,
28630                 tiffOffset,
28631                 dirOffset + 2 + 12 * i, // tag offset
28632                 littleEndian
28633             );
28634         }
28635         // Return the offset to the next directory:
28636         return dataView.getUint32(dirEndOffset, littleEndian);
28637     },
28638     
28639     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28640     {
28641         var tag = dataView.getUint16(offset, littleEndian);
28642         
28643         this.exif[tag] = this.getExifValue(
28644             dataView,
28645             tiffOffset,
28646             offset,
28647             dataView.getUint16(offset + 2, littleEndian), // tag type
28648             dataView.getUint32(offset + 4, littleEndian), // tag length
28649             littleEndian
28650         );
28651     },
28652     
28653     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28654     {
28655         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28656             tagSize,
28657             dataOffset,
28658             values,
28659             i,
28660             str,
28661             c;
28662     
28663         if (!tagType) {
28664             Roo.log('Invalid Exif data: Invalid tag type.');
28665             return;
28666         }
28667         
28668         tagSize = tagType.size * length;
28669         // Determine if the value is contained in the dataOffset bytes,
28670         // or if the value at the dataOffset is a pointer to the actual data:
28671         dataOffset = tagSize > 4 ?
28672                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28673         if (dataOffset + tagSize > dataView.byteLength) {
28674             Roo.log('Invalid Exif data: Invalid data offset.');
28675             return;
28676         }
28677         if (length === 1) {
28678             return tagType.getValue(dataView, dataOffset, littleEndian);
28679         }
28680         values = [];
28681         for (i = 0; i < length; i += 1) {
28682             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28683         }
28684         
28685         if (tagType.ascii) {
28686             str = '';
28687             // Concatenate the chars:
28688             for (i = 0; i < values.length; i += 1) {
28689                 c = values[i];
28690                 // Ignore the terminating NULL byte(s):
28691                 if (c === '\u0000') {
28692                     break;
28693                 }
28694                 str += c;
28695             }
28696             return str;
28697         }
28698         return values;
28699     }
28700     
28701 });
28702
28703 Roo.apply(Roo.bootstrap.UploadCropbox, {
28704     tags : {
28705         'Orientation': 0x0112
28706     },
28707     
28708     Orientation: {
28709             1: 0, //'top-left',
28710 //            2: 'top-right',
28711             3: 180, //'bottom-right',
28712 //            4: 'bottom-left',
28713 //            5: 'left-top',
28714             6: 90, //'right-top',
28715 //            7: 'right-bottom',
28716             8: 270 //'left-bottom'
28717     },
28718     
28719     exifTagTypes : {
28720         // byte, 8-bit unsigned int:
28721         1: {
28722             getValue: function (dataView, dataOffset) {
28723                 return dataView.getUint8(dataOffset);
28724             },
28725             size: 1
28726         },
28727         // ascii, 8-bit byte:
28728         2: {
28729             getValue: function (dataView, dataOffset) {
28730                 return String.fromCharCode(dataView.getUint8(dataOffset));
28731             },
28732             size: 1,
28733             ascii: true
28734         },
28735         // short, 16 bit int:
28736         3: {
28737             getValue: function (dataView, dataOffset, littleEndian) {
28738                 return dataView.getUint16(dataOffset, littleEndian);
28739             },
28740             size: 2
28741         },
28742         // long, 32 bit int:
28743         4: {
28744             getValue: function (dataView, dataOffset, littleEndian) {
28745                 return dataView.getUint32(dataOffset, littleEndian);
28746             },
28747             size: 4
28748         },
28749         // rational = two long values, first is numerator, second is denominator:
28750         5: {
28751             getValue: function (dataView, dataOffset, littleEndian) {
28752                 return dataView.getUint32(dataOffset, littleEndian) /
28753                     dataView.getUint32(dataOffset + 4, littleEndian);
28754             },
28755             size: 8
28756         },
28757         // slong, 32 bit signed int:
28758         9: {
28759             getValue: function (dataView, dataOffset, littleEndian) {
28760                 return dataView.getInt32(dataOffset, littleEndian);
28761             },
28762             size: 4
28763         },
28764         // srational, two slongs, first is numerator, second is denominator:
28765         10: {
28766             getValue: function (dataView, dataOffset, littleEndian) {
28767                 return dataView.getInt32(dataOffset, littleEndian) /
28768                     dataView.getInt32(dataOffset + 4, littleEndian);
28769             },
28770             size: 8
28771         }
28772     },
28773     
28774     footer : {
28775         STANDARD : [
28776             {
28777                 tag : 'div',
28778                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28779                 action : 'rotate-left',
28780                 cn : [
28781                     {
28782                         tag : 'button',
28783                         cls : 'btn btn-default',
28784                         html : '<i class="fa fa-undo"></i>'
28785                     }
28786                 ]
28787             },
28788             {
28789                 tag : 'div',
28790                 cls : 'btn-group roo-upload-cropbox-picture',
28791                 action : 'picture',
28792                 cn : [
28793                     {
28794                         tag : 'button',
28795                         cls : 'btn btn-default',
28796                         html : '<i class="fa fa-picture-o"></i>'
28797                     }
28798                 ]
28799             },
28800             {
28801                 tag : 'div',
28802                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28803                 action : 'rotate-right',
28804                 cn : [
28805                     {
28806                         tag : 'button',
28807                         cls : 'btn btn-default',
28808                         html : '<i class="fa fa-repeat"></i>'
28809                     }
28810                 ]
28811             }
28812         ],
28813         DOCUMENT : [
28814             {
28815                 tag : 'div',
28816                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28817                 action : 'rotate-left',
28818                 cn : [
28819                     {
28820                         tag : 'button',
28821                         cls : 'btn btn-default',
28822                         html : '<i class="fa fa-undo"></i>'
28823                     }
28824                 ]
28825             },
28826             {
28827                 tag : 'div',
28828                 cls : 'btn-group roo-upload-cropbox-download',
28829                 action : 'download',
28830                 cn : [
28831                     {
28832                         tag : 'button',
28833                         cls : 'btn btn-default',
28834                         html : '<i class="fa fa-download"></i>'
28835                     }
28836                 ]
28837             },
28838             {
28839                 tag : 'div',
28840                 cls : 'btn-group roo-upload-cropbox-crop',
28841                 action : 'crop',
28842                 cn : [
28843                     {
28844                         tag : 'button',
28845                         cls : 'btn btn-default',
28846                         html : '<i class="fa fa-crop"></i>'
28847                     }
28848                 ]
28849             },
28850             {
28851                 tag : 'div',
28852                 cls : 'btn-group roo-upload-cropbox-trash',
28853                 action : 'trash',
28854                 cn : [
28855                     {
28856                         tag : 'button',
28857                         cls : 'btn btn-default',
28858                         html : '<i class="fa fa-trash"></i>'
28859                     }
28860                 ]
28861             },
28862             {
28863                 tag : 'div',
28864                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28865                 action : 'rotate-right',
28866                 cn : [
28867                     {
28868                         tag : 'button',
28869                         cls : 'btn btn-default',
28870                         html : '<i class="fa fa-repeat"></i>'
28871                     }
28872                 ]
28873             }
28874         ],
28875         ROTATOR : [
28876             {
28877                 tag : 'div',
28878                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28879                 action : 'rotate-left',
28880                 cn : [
28881                     {
28882                         tag : 'button',
28883                         cls : 'btn btn-default',
28884                         html : '<i class="fa fa-undo"></i>'
28885                     }
28886                 ]
28887             },
28888             {
28889                 tag : 'div',
28890                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28891                 action : 'rotate-right',
28892                 cn : [
28893                     {
28894                         tag : 'button',
28895                         cls : 'btn btn-default',
28896                         html : '<i class="fa fa-repeat"></i>'
28897                     }
28898                 ]
28899             }
28900         ]
28901     }
28902 });
28903
28904 /*
28905 * Licence: LGPL
28906 */
28907
28908 /**
28909  * @class Roo.bootstrap.DocumentManager
28910  * @extends Roo.bootstrap.Component
28911  * Bootstrap DocumentManager class
28912  * @cfg {String} paramName default 'imageUpload'
28913  * @cfg {String} toolTipName default 'filename'
28914  * @cfg {String} method default POST
28915  * @cfg {String} url action url
28916  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28917  * @cfg {Boolean} multiple multiple upload default true
28918  * @cfg {Number} thumbSize default 300
28919  * @cfg {String} fieldLabel
28920  * @cfg {Number} labelWidth default 4
28921  * @cfg {String} labelAlign (left|top) default left
28922  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28923 * @cfg {Number} labellg set the width of label (1-12)
28924  * @cfg {Number} labelmd set the width of label (1-12)
28925  * @cfg {Number} labelsm set the width of label (1-12)
28926  * @cfg {Number} labelxs set the width of label (1-12)
28927  * 
28928  * @constructor
28929  * Create a new DocumentManager
28930  * @param {Object} config The config object
28931  */
28932
28933 Roo.bootstrap.DocumentManager = function(config){
28934     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28935     
28936     this.files = [];
28937     this.delegates = [];
28938     
28939     this.addEvents({
28940         /**
28941          * @event initial
28942          * Fire when initial the DocumentManager
28943          * @param {Roo.bootstrap.DocumentManager} this
28944          */
28945         "initial" : true,
28946         /**
28947          * @event inspect
28948          * inspect selected file
28949          * @param {Roo.bootstrap.DocumentManager} this
28950          * @param {File} file
28951          */
28952         "inspect" : true,
28953         /**
28954          * @event exception
28955          * Fire when xhr load exception
28956          * @param {Roo.bootstrap.DocumentManager} this
28957          * @param {XMLHttpRequest} xhr
28958          */
28959         "exception" : true,
28960         /**
28961          * @event afterupload
28962          * Fire when xhr load exception
28963          * @param {Roo.bootstrap.DocumentManager} this
28964          * @param {XMLHttpRequest} xhr
28965          */
28966         "afterupload" : true,
28967         /**
28968          * @event prepare
28969          * prepare the form data
28970          * @param {Roo.bootstrap.DocumentManager} this
28971          * @param {Object} formData
28972          */
28973         "prepare" : true,
28974         /**
28975          * @event remove
28976          * Fire when remove the file
28977          * @param {Roo.bootstrap.DocumentManager} this
28978          * @param {Object} file
28979          */
28980         "remove" : true,
28981         /**
28982          * @event refresh
28983          * Fire after refresh the file
28984          * @param {Roo.bootstrap.DocumentManager} this
28985          */
28986         "refresh" : true,
28987         /**
28988          * @event click
28989          * Fire after click the image
28990          * @param {Roo.bootstrap.DocumentManager} this
28991          * @param {Object} file
28992          */
28993         "click" : true,
28994         /**
28995          * @event edit
28996          * Fire when upload a image and editable set to true
28997          * @param {Roo.bootstrap.DocumentManager} this
28998          * @param {Object} file
28999          */
29000         "edit" : true,
29001         /**
29002          * @event beforeselectfile
29003          * Fire before select file
29004          * @param {Roo.bootstrap.DocumentManager} this
29005          */
29006         "beforeselectfile" : true,
29007         /**
29008          * @event process
29009          * Fire before process file
29010          * @param {Roo.bootstrap.DocumentManager} this
29011          * @param {Object} file
29012          */
29013         "process" : true,
29014         /**
29015          * @event previewrendered
29016          * Fire when preview rendered
29017          * @param {Roo.bootstrap.DocumentManager} this
29018          * @param {Object} file
29019          */
29020         "previewrendered" : true,
29021         /**
29022          */
29023         "previewResize" : true
29024         
29025     });
29026 };
29027
29028 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29029     
29030     boxes : 0,
29031     inputName : '',
29032     thumbSize : 300,
29033     multiple : true,
29034     files : false,
29035     method : 'POST',
29036     url : '',
29037     paramName : 'imageUpload',
29038     toolTipName : 'filename',
29039     fieldLabel : '',
29040     labelWidth : 4,
29041     labelAlign : 'left',
29042     editable : true,
29043     delegates : false,
29044     xhr : false, 
29045     
29046     labellg : 0,
29047     labelmd : 0,
29048     labelsm : 0,
29049     labelxs : 0,
29050     
29051     getAutoCreate : function()
29052     {   
29053         var managerWidget = {
29054             tag : 'div',
29055             cls : 'roo-document-manager',
29056             cn : [
29057                 {
29058                     tag : 'input',
29059                     cls : 'roo-document-manager-selector',
29060                     type : 'file'
29061                 },
29062                 {
29063                     tag : 'div',
29064                     cls : 'roo-document-manager-uploader',
29065                     cn : [
29066                         {
29067                             tag : 'div',
29068                             cls : 'roo-document-manager-upload-btn',
29069                             html : '<i class="fa fa-plus"></i>'
29070                         }
29071                     ]
29072                     
29073                 }
29074             ]
29075         };
29076         
29077         var content = [
29078             {
29079                 tag : 'div',
29080                 cls : 'column col-md-12',
29081                 cn : managerWidget
29082             }
29083         ];
29084         
29085         if(this.fieldLabel.length){
29086             
29087             content = [
29088                 {
29089                     tag : 'div',
29090                     cls : 'column col-md-12',
29091                     html : this.fieldLabel
29092                 },
29093                 {
29094                     tag : 'div',
29095                     cls : 'column col-md-12',
29096                     cn : managerWidget
29097                 }
29098             ];
29099
29100             if(this.labelAlign == 'left'){
29101                 content = [
29102                     {
29103                         tag : 'div',
29104                         cls : 'column',
29105                         html : this.fieldLabel
29106                     },
29107                     {
29108                         tag : 'div',
29109                         cls : 'column',
29110                         cn : managerWidget
29111                     }
29112                 ];
29113                 
29114                 if(this.labelWidth > 12){
29115                     content[0].style = "width: " + this.labelWidth + 'px';
29116                 }
29117
29118                 if(this.labelWidth < 13 && this.labelmd == 0){
29119                     this.labelmd = this.labelWidth;
29120                 }
29121
29122                 if(this.labellg > 0){
29123                     content[0].cls += ' col-lg-' + this.labellg;
29124                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29125                 }
29126
29127                 if(this.labelmd > 0){
29128                     content[0].cls += ' col-md-' + this.labelmd;
29129                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29130                 }
29131
29132                 if(this.labelsm > 0){
29133                     content[0].cls += ' col-sm-' + this.labelsm;
29134                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29135                 }
29136
29137                 if(this.labelxs > 0){
29138                     content[0].cls += ' col-xs-' + this.labelxs;
29139                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29140                 }
29141                 
29142             }
29143         }
29144         
29145         var cfg = {
29146             tag : 'div',
29147             cls : 'row clearfix',
29148             cn : content
29149         };
29150         
29151         return cfg;
29152         
29153     },
29154     
29155     initEvents : function()
29156     {
29157         this.managerEl = this.el.select('.roo-document-manager', true).first();
29158         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29159         
29160         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29161         this.selectorEl.hide();
29162         
29163         if(this.multiple){
29164             this.selectorEl.attr('multiple', 'multiple');
29165         }
29166         
29167         this.selectorEl.on('change', this.onFileSelected, this);
29168         
29169         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29170         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29171         
29172         this.uploader.on('click', this.onUploaderClick, this);
29173         
29174         this.renderProgressDialog();
29175         
29176         var _this = this;
29177         
29178         window.addEventListener("resize", function() { _this.refresh(); } );
29179         
29180         this.fireEvent('initial', this);
29181     },
29182     
29183     renderProgressDialog : function()
29184     {
29185         var _this = this;
29186         
29187         this.progressDialog = new Roo.bootstrap.Modal({
29188             cls : 'roo-document-manager-progress-dialog',
29189             allow_close : false,
29190             animate : false,
29191             title : '',
29192             buttons : [
29193                 {
29194                     name  :'cancel',
29195                     weight : 'danger',
29196                     html : 'Cancel'
29197                 }
29198             ], 
29199             listeners : { 
29200                 btnclick : function() {
29201                     _this.uploadCancel();
29202                     this.hide();
29203                 }
29204             }
29205         });
29206          
29207         this.progressDialog.render(Roo.get(document.body));
29208          
29209         this.progress = new Roo.bootstrap.Progress({
29210             cls : 'roo-document-manager-progress',
29211             active : true,
29212             striped : true
29213         });
29214         
29215         this.progress.render(this.progressDialog.getChildContainer());
29216         
29217         this.progressBar = new Roo.bootstrap.ProgressBar({
29218             cls : 'roo-document-manager-progress-bar',
29219             aria_valuenow : 0,
29220             aria_valuemin : 0,
29221             aria_valuemax : 12,
29222             panel : 'success'
29223         });
29224         
29225         this.progressBar.render(this.progress.getChildContainer());
29226     },
29227     
29228     onUploaderClick : function(e)
29229     {
29230         e.preventDefault();
29231      
29232         if(this.fireEvent('beforeselectfile', this) != false){
29233             this.selectorEl.dom.click();
29234         }
29235         
29236     },
29237     
29238     onFileSelected : function(e)
29239     {
29240         e.preventDefault();
29241         
29242         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29243             return;
29244         }
29245         
29246         Roo.each(this.selectorEl.dom.files, function(file){
29247             if(this.fireEvent('inspect', this, file) != false){
29248                 this.files.push(file);
29249             }
29250         }, this);
29251         
29252         this.queue();
29253         
29254     },
29255     
29256     queue : function()
29257     {
29258         this.selectorEl.dom.value = '';
29259         
29260         if(!this.files || !this.files.length){
29261             return;
29262         }
29263         
29264         if(this.boxes > 0 && this.files.length > this.boxes){
29265             this.files = this.files.slice(0, this.boxes);
29266         }
29267         
29268         this.uploader.show();
29269         
29270         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29271             this.uploader.hide();
29272         }
29273         
29274         var _this = this;
29275         
29276         var files = [];
29277         
29278         var docs = [];
29279         
29280         Roo.each(this.files, function(file){
29281             
29282             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29283                 var f = this.renderPreview(file);
29284                 files.push(f);
29285                 return;
29286             }
29287             
29288             if(file.type.indexOf('image') != -1){
29289                 this.delegates.push(
29290                     (function(){
29291                         _this.process(file);
29292                     }).createDelegate(this)
29293                 );
29294         
29295                 return;
29296             }
29297             
29298             docs.push(
29299                 (function(){
29300                     _this.process(file);
29301                 }).createDelegate(this)
29302             );
29303             
29304         }, this);
29305         
29306         this.files = files;
29307         
29308         this.delegates = this.delegates.concat(docs);
29309         
29310         if(!this.delegates.length){
29311             this.refresh();
29312             return;
29313         }
29314         
29315         this.progressBar.aria_valuemax = this.delegates.length;
29316         
29317         this.arrange();
29318         
29319         return;
29320     },
29321     
29322     arrange : function()
29323     {
29324         if(!this.delegates.length){
29325             this.progressDialog.hide();
29326             this.refresh();
29327             return;
29328         }
29329         
29330         var delegate = this.delegates.shift();
29331         
29332         this.progressDialog.show();
29333         
29334         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29335         
29336         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29337         
29338         delegate();
29339     },
29340     
29341     refresh : function()
29342     {
29343         this.uploader.show();
29344         
29345         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29346             this.uploader.hide();
29347         }
29348         
29349         Roo.isTouch ? this.closable(false) : this.closable(true);
29350         
29351         this.fireEvent('refresh', this);
29352     },
29353     
29354     onRemove : function(e, el, o)
29355     {
29356         e.preventDefault();
29357         
29358         this.fireEvent('remove', this, o);
29359         
29360     },
29361     
29362     remove : function(o)
29363     {
29364         var files = [];
29365         
29366         Roo.each(this.files, function(file){
29367             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29368                 files.push(file);
29369                 return;
29370             }
29371
29372             o.target.remove();
29373
29374         }, this);
29375         
29376         this.files = files;
29377         
29378         this.refresh();
29379     },
29380     
29381     clear : function()
29382     {
29383         Roo.each(this.files, function(file){
29384             if(!file.target){
29385                 return;
29386             }
29387             
29388             file.target.remove();
29389
29390         }, this);
29391         
29392         this.files = [];
29393         
29394         this.refresh();
29395     },
29396     
29397     onClick : function(e, el, o)
29398     {
29399         e.preventDefault();
29400         
29401         this.fireEvent('click', this, o);
29402         
29403     },
29404     
29405     closable : function(closable)
29406     {
29407         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29408             
29409             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29410             
29411             if(closable){
29412                 el.show();
29413                 return;
29414             }
29415             
29416             el.hide();
29417             
29418         }, this);
29419     },
29420     
29421     xhrOnLoad : function(xhr)
29422     {
29423         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29424             el.remove();
29425         }, this);
29426         
29427         if (xhr.readyState !== 4) {
29428             this.arrange();
29429             this.fireEvent('exception', this, xhr);
29430             return;
29431         }
29432
29433         var response = Roo.decode(xhr.responseText);
29434         
29435         if(!response.success){
29436             this.arrange();
29437             this.fireEvent('exception', this, xhr);
29438             return;
29439         }
29440         
29441         var file = this.renderPreview(response.data);
29442         
29443         this.files.push(file);
29444         
29445         this.arrange();
29446         
29447         this.fireEvent('afterupload', this, xhr);
29448         
29449     },
29450     
29451     xhrOnError : function(xhr)
29452     {
29453         Roo.log('xhr on error');
29454         
29455         var response = Roo.decode(xhr.responseText);
29456           
29457         Roo.log(response);
29458         
29459         this.arrange();
29460     },
29461     
29462     process : function(file)
29463     {
29464         if(this.fireEvent('process', this, file) !== false){
29465             if(this.editable && file.type.indexOf('image') != -1){
29466                 this.fireEvent('edit', this, file);
29467                 return;
29468             }
29469
29470             this.uploadStart(file, false);
29471
29472             return;
29473         }
29474         
29475     },
29476     
29477     uploadStart : function(file, crop)
29478     {
29479         this.xhr = new XMLHttpRequest();
29480         
29481         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29482             this.arrange();
29483             return;
29484         }
29485         
29486         file.xhr = this.xhr;
29487             
29488         this.managerEl.createChild({
29489             tag : 'div',
29490             cls : 'roo-document-manager-loading',
29491             cn : [
29492                 {
29493                     tag : 'div',
29494                     tooltip : file.name,
29495                     cls : 'roo-document-manager-thumb',
29496                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29497                 }
29498             ]
29499
29500         });
29501
29502         this.xhr.open(this.method, this.url, true);
29503         
29504         var headers = {
29505             "Accept": "application/json",
29506             "Cache-Control": "no-cache",
29507             "X-Requested-With": "XMLHttpRequest"
29508         };
29509         
29510         for (var headerName in headers) {
29511             var headerValue = headers[headerName];
29512             if (headerValue) {
29513                 this.xhr.setRequestHeader(headerName, headerValue);
29514             }
29515         }
29516         
29517         var _this = this;
29518         
29519         this.xhr.onload = function()
29520         {
29521             _this.xhrOnLoad(_this.xhr);
29522         }
29523         
29524         this.xhr.onerror = function()
29525         {
29526             _this.xhrOnError(_this.xhr);
29527         }
29528         
29529         var formData = new FormData();
29530
29531         formData.append('returnHTML', 'NO');
29532         
29533         if(crop){
29534             formData.append('crop', crop);
29535         }
29536         
29537         formData.append(this.paramName, file, file.name);
29538         
29539         var options = {
29540             file : file, 
29541             manually : false
29542         };
29543         
29544         if(this.fireEvent('prepare', this, formData, options) != false){
29545             
29546             if(options.manually){
29547                 return;
29548             }
29549             
29550             this.xhr.send(formData);
29551             return;
29552         };
29553         
29554         this.uploadCancel();
29555     },
29556     
29557     uploadCancel : function()
29558     {
29559         if (this.xhr) {
29560             this.xhr.abort();
29561         }
29562         
29563         this.delegates = [];
29564         
29565         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29566             el.remove();
29567         }, this);
29568         
29569         this.arrange();
29570     },
29571     
29572     renderPreview : function(file)
29573     {
29574         if(typeof(file.target) != 'undefined' && file.target){
29575             return file;
29576         }
29577         
29578         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29579         
29580         var previewEl = this.managerEl.createChild({
29581             tag : 'div',
29582             cls : 'roo-document-manager-preview',
29583             cn : [
29584                 {
29585                     tag : 'div',
29586                     tooltip : file[this.toolTipName],
29587                     cls : 'roo-document-manager-thumb',
29588                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29589                 },
29590                 {
29591                     tag : 'button',
29592                     cls : 'close',
29593                     html : '<i class="fa fa-times-circle"></i>'
29594                 }
29595             ]
29596         });
29597
29598         var close = previewEl.select('button.close', true).first();
29599
29600         close.on('click', this.onRemove, this, file);
29601
29602         file.target = previewEl;
29603
29604         var image = previewEl.select('img', true).first();
29605         
29606         var _this = this;
29607         
29608         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29609         
29610         image.on('click', this.onClick, this, file);
29611         
29612         this.fireEvent('previewrendered', this, file);
29613         
29614         return file;
29615         
29616     },
29617     
29618     onPreviewLoad : function(file, image)
29619     {
29620         if(typeof(file.target) == 'undefined' || !file.target){
29621             return;
29622         }
29623         
29624         var width = image.dom.naturalWidth || image.dom.width;
29625         var height = image.dom.naturalHeight || image.dom.height;
29626         
29627         if(!this.previewResize) {
29628             return;
29629         }
29630         
29631         if(width > height){
29632             file.target.addClass('wide');
29633             return;
29634         }
29635         
29636         file.target.addClass('tall');
29637         return;
29638         
29639     },
29640     
29641     uploadFromSource : function(file, crop)
29642     {
29643         this.xhr = new XMLHttpRequest();
29644         
29645         this.managerEl.createChild({
29646             tag : 'div',
29647             cls : 'roo-document-manager-loading',
29648             cn : [
29649                 {
29650                     tag : 'div',
29651                     tooltip : file.name,
29652                     cls : 'roo-document-manager-thumb',
29653                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29654                 }
29655             ]
29656
29657         });
29658
29659         this.xhr.open(this.method, this.url, true);
29660         
29661         var headers = {
29662             "Accept": "application/json",
29663             "Cache-Control": "no-cache",
29664             "X-Requested-With": "XMLHttpRequest"
29665         };
29666         
29667         for (var headerName in headers) {
29668             var headerValue = headers[headerName];
29669             if (headerValue) {
29670                 this.xhr.setRequestHeader(headerName, headerValue);
29671             }
29672         }
29673         
29674         var _this = this;
29675         
29676         this.xhr.onload = function()
29677         {
29678             _this.xhrOnLoad(_this.xhr);
29679         }
29680         
29681         this.xhr.onerror = function()
29682         {
29683             _this.xhrOnError(_this.xhr);
29684         }
29685         
29686         var formData = new FormData();
29687
29688         formData.append('returnHTML', 'NO');
29689         
29690         formData.append('crop', crop);
29691         
29692         if(typeof(file.filename) != 'undefined'){
29693             formData.append('filename', file.filename);
29694         }
29695         
29696         if(typeof(file.mimetype) != 'undefined'){
29697             formData.append('mimetype', file.mimetype);
29698         }
29699         
29700         Roo.log(formData);
29701         
29702         if(this.fireEvent('prepare', this, formData) != false){
29703             this.xhr.send(formData);
29704         };
29705     }
29706 });
29707
29708 /*
29709 * Licence: LGPL
29710 */
29711
29712 /**
29713  * @class Roo.bootstrap.DocumentViewer
29714  * @extends Roo.bootstrap.Component
29715  * Bootstrap DocumentViewer class
29716  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29717  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29718  * 
29719  * @constructor
29720  * Create a new DocumentViewer
29721  * @param {Object} config The config object
29722  */
29723
29724 Roo.bootstrap.DocumentViewer = function(config){
29725     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29726     
29727     this.addEvents({
29728         /**
29729          * @event initial
29730          * Fire after initEvent
29731          * @param {Roo.bootstrap.DocumentViewer} this
29732          */
29733         "initial" : true,
29734         /**
29735          * @event click
29736          * Fire after click
29737          * @param {Roo.bootstrap.DocumentViewer} this
29738          */
29739         "click" : true,
29740         /**
29741          * @event download
29742          * Fire after download button
29743          * @param {Roo.bootstrap.DocumentViewer} this
29744          */
29745         "download" : true,
29746         /**
29747          * @event trash
29748          * Fire after trash button
29749          * @param {Roo.bootstrap.DocumentViewer} this
29750          */
29751         "trash" : true
29752         
29753     });
29754 };
29755
29756 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29757     
29758     showDownload : true,
29759     
29760     showTrash : true,
29761     
29762     getAutoCreate : function()
29763     {
29764         var cfg = {
29765             tag : 'div',
29766             cls : 'roo-document-viewer',
29767             cn : [
29768                 {
29769                     tag : 'div',
29770                     cls : 'roo-document-viewer-body',
29771                     cn : [
29772                         {
29773                             tag : 'div',
29774                             cls : 'roo-document-viewer-thumb',
29775                             cn : [
29776                                 {
29777                                     tag : 'img',
29778                                     cls : 'roo-document-viewer-image'
29779                                 }
29780                             ]
29781                         }
29782                     ]
29783                 },
29784                 {
29785                     tag : 'div',
29786                     cls : 'roo-document-viewer-footer',
29787                     cn : {
29788                         tag : 'div',
29789                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29790                         cn : [
29791                             {
29792                                 tag : 'div',
29793                                 cls : 'btn-group roo-document-viewer-download',
29794                                 cn : [
29795                                     {
29796                                         tag : 'button',
29797                                         cls : 'btn btn-default',
29798                                         html : '<i class="fa fa-download"></i>'
29799                                     }
29800                                 ]
29801                             },
29802                             {
29803                                 tag : 'div',
29804                                 cls : 'btn-group roo-document-viewer-trash',
29805                                 cn : [
29806                                     {
29807                                         tag : 'button',
29808                                         cls : 'btn btn-default',
29809                                         html : '<i class="fa fa-trash"></i>'
29810                                     }
29811                                 ]
29812                             }
29813                         ]
29814                     }
29815                 }
29816             ]
29817         };
29818         
29819         return cfg;
29820     },
29821     
29822     initEvents : function()
29823     {
29824         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29825         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29826         
29827         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29828         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29829         
29830         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29831         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29832         
29833         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29834         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29835         
29836         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29837         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29838         
29839         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29840         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29841         
29842         this.bodyEl.on('click', this.onClick, this);
29843         this.downloadBtn.on('click', this.onDownload, this);
29844         this.trashBtn.on('click', this.onTrash, this);
29845         
29846         this.downloadBtn.hide();
29847         this.trashBtn.hide();
29848         
29849         if(this.showDownload){
29850             this.downloadBtn.show();
29851         }
29852         
29853         if(this.showTrash){
29854             this.trashBtn.show();
29855         }
29856         
29857         if(!this.showDownload && !this.showTrash) {
29858             this.footerEl.hide();
29859         }
29860         
29861     },
29862     
29863     initial : function()
29864     {
29865         this.fireEvent('initial', this);
29866         
29867     },
29868     
29869     onClick : function(e)
29870     {
29871         e.preventDefault();
29872         
29873         this.fireEvent('click', this);
29874     },
29875     
29876     onDownload : function(e)
29877     {
29878         e.preventDefault();
29879         
29880         this.fireEvent('download', this);
29881     },
29882     
29883     onTrash : function(e)
29884     {
29885         e.preventDefault();
29886         
29887         this.fireEvent('trash', this);
29888     }
29889     
29890 });
29891 /*
29892  * - LGPL
29893  *
29894  * nav progress bar
29895  * 
29896  */
29897
29898 /**
29899  * @class Roo.bootstrap.NavProgressBar
29900  * @extends Roo.bootstrap.Component
29901  * Bootstrap NavProgressBar class
29902  * 
29903  * @constructor
29904  * Create a new nav progress bar
29905  * @param {Object} config The config object
29906  */
29907
29908 Roo.bootstrap.NavProgressBar = function(config){
29909     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29910
29911     this.bullets = this.bullets || [];
29912    
29913 //    Roo.bootstrap.NavProgressBar.register(this);
29914      this.addEvents({
29915         /**
29916              * @event changed
29917              * Fires when the active item changes
29918              * @param {Roo.bootstrap.NavProgressBar} this
29919              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29920              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29921          */
29922         'changed': true
29923      });
29924     
29925 };
29926
29927 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29928     
29929     bullets : [],
29930     barItems : [],
29931     
29932     getAutoCreate : function()
29933     {
29934         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29935         
29936         cfg = {
29937             tag : 'div',
29938             cls : 'roo-navigation-bar-group',
29939             cn : [
29940                 {
29941                     tag : 'div',
29942                     cls : 'roo-navigation-top-bar'
29943                 },
29944                 {
29945                     tag : 'div',
29946                     cls : 'roo-navigation-bullets-bar',
29947                     cn : [
29948                         {
29949                             tag : 'ul',
29950                             cls : 'roo-navigation-bar'
29951                         }
29952                     ]
29953                 },
29954                 
29955                 {
29956                     tag : 'div',
29957                     cls : 'roo-navigation-bottom-bar'
29958                 }
29959             ]
29960             
29961         };
29962         
29963         return cfg;
29964         
29965     },
29966     
29967     initEvents: function() 
29968     {
29969         
29970     },
29971     
29972     onRender : function(ct, position) 
29973     {
29974         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29975         
29976         if(this.bullets.length){
29977             Roo.each(this.bullets, function(b){
29978                this.addItem(b);
29979             }, this);
29980         }
29981         
29982         this.format();
29983         
29984     },
29985     
29986     addItem : function(cfg)
29987     {
29988         var item = new Roo.bootstrap.NavProgressItem(cfg);
29989         
29990         item.parentId = this.id;
29991         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29992         
29993         if(cfg.html){
29994             var top = new Roo.bootstrap.Element({
29995                 tag : 'div',
29996                 cls : 'roo-navigation-bar-text'
29997             });
29998             
29999             var bottom = new Roo.bootstrap.Element({
30000                 tag : 'div',
30001                 cls : 'roo-navigation-bar-text'
30002             });
30003             
30004             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30005             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30006             
30007             var topText = new Roo.bootstrap.Element({
30008                 tag : 'span',
30009                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30010             });
30011             
30012             var bottomText = new Roo.bootstrap.Element({
30013                 tag : 'span',
30014                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30015             });
30016             
30017             topText.onRender(top.el, null);
30018             bottomText.onRender(bottom.el, null);
30019             
30020             item.topEl = top;
30021             item.bottomEl = bottom;
30022         }
30023         
30024         this.barItems.push(item);
30025         
30026         return item;
30027     },
30028     
30029     getActive : function()
30030     {
30031         var active = false;
30032         
30033         Roo.each(this.barItems, function(v){
30034             
30035             if (!v.isActive()) {
30036                 return;
30037             }
30038             
30039             active = v;
30040             return false;
30041             
30042         });
30043         
30044         return active;
30045     },
30046     
30047     setActiveItem : function(item)
30048     {
30049         var prev = false;
30050         
30051         Roo.each(this.barItems, function(v){
30052             if (v.rid == item.rid) {
30053                 return ;
30054             }
30055             
30056             if (v.isActive()) {
30057                 v.setActive(false);
30058                 prev = v;
30059             }
30060         });
30061
30062         item.setActive(true);
30063         
30064         this.fireEvent('changed', this, item, prev);
30065     },
30066     
30067     getBarItem: function(rid)
30068     {
30069         var ret = false;
30070         
30071         Roo.each(this.barItems, function(e) {
30072             if (e.rid != rid) {
30073                 return;
30074             }
30075             
30076             ret =  e;
30077             return false;
30078         });
30079         
30080         return ret;
30081     },
30082     
30083     indexOfItem : function(item)
30084     {
30085         var index = false;
30086         
30087         Roo.each(this.barItems, function(v, i){
30088             
30089             if (v.rid != item.rid) {
30090                 return;
30091             }
30092             
30093             index = i;
30094             return false
30095         });
30096         
30097         return index;
30098     },
30099     
30100     setActiveNext : function()
30101     {
30102         var i = this.indexOfItem(this.getActive());
30103         
30104         if (i > this.barItems.length) {
30105             return;
30106         }
30107         
30108         this.setActiveItem(this.barItems[i+1]);
30109     },
30110     
30111     setActivePrev : function()
30112     {
30113         var i = this.indexOfItem(this.getActive());
30114         
30115         if (i  < 1) {
30116             return;
30117         }
30118         
30119         this.setActiveItem(this.barItems[i-1]);
30120     },
30121     
30122     format : function()
30123     {
30124         if(!this.barItems.length){
30125             return;
30126         }
30127      
30128         var width = 100 / this.barItems.length;
30129         
30130         Roo.each(this.barItems, function(i){
30131             i.el.setStyle('width', width + '%');
30132             i.topEl.el.setStyle('width', width + '%');
30133             i.bottomEl.el.setStyle('width', width + '%');
30134         }, this);
30135         
30136     }
30137     
30138 });
30139 /*
30140  * - LGPL
30141  *
30142  * Nav Progress Item
30143  * 
30144  */
30145
30146 /**
30147  * @class Roo.bootstrap.NavProgressItem
30148  * @extends Roo.bootstrap.Component
30149  * Bootstrap NavProgressItem class
30150  * @cfg {String} rid the reference id
30151  * @cfg {Boolean} active (true|false) Is item active default false
30152  * @cfg {Boolean} disabled (true|false) Is item active default false
30153  * @cfg {String} html
30154  * @cfg {String} position (top|bottom) text position default bottom
30155  * @cfg {String} icon show icon instead of number
30156  * 
30157  * @constructor
30158  * Create a new NavProgressItem
30159  * @param {Object} config The config object
30160  */
30161 Roo.bootstrap.NavProgressItem = function(config){
30162     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30163     this.addEvents({
30164         // raw events
30165         /**
30166          * @event click
30167          * The raw click event for the entire grid.
30168          * @param {Roo.bootstrap.NavProgressItem} this
30169          * @param {Roo.EventObject} e
30170          */
30171         "click" : true
30172     });
30173    
30174 };
30175
30176 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30177     
30178     rid : '',
30179     active : false,
30180     disabled : false,
30181     html : '',
30182     position : 'bottom',
30183     icon : false,
30184     
30185     getAutoCreate : function()
30186     {
30187         var iconCls = 'roo-navigation-bar-item-icon';
30188         
30189         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30190         
30191         var cfg = {
30192             tag: 'li',
30193             cls: 'roo-navigation-bar-item',
30194             cn : [
30195                 {
30196                     tag : 'i',
30197                     cls : iconCls
30198                 }
30199             ]
30200         };
30201         
30202         if(this.active){
30203             cfg.cls += ' active';
30204         }
30205         if(this.disabled){
30206             cfg.cls += ' disabled';
30207         }
30208         
30209         return cfg;
30210     },
30211     
30212     disable : function()
30213     {
30214         this.setDisabled(true);
30215     },
30216     
30217     enable : function()
30218     {
30219         this.setDisabled(false);
30220     },
30221     
30222     initEvents: function() 
30223     {
30224         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30225         
30226         this.iconEl.on('click', this.onClick, this);
30227     },
30228     
30229     onClick : function(e)
30230     {
30231         e.preventDefault();
30232         
30233         if(this.disabled){
30234             return;
30235         }
30236         
30237         if(this.fireEvent('click', this, e) === false){
30238             return;
30239         };
30240         
30241         this.parent().setActiveItem(this);
30242     },
30243     
30244     isActive: function () 
30245     {
30246         return this.active;
30247     },
30248     
30249     setActive : function(state)
30250     {
30251         if(this.active == state){
30252             return;
30253         }
30254         
30255         this.active = state;
30256         
30257         if (state) {
30258             this.el.addClass('active');
30259             return;
30260         }
30261         
30262         this.el.removeClass('active');
30263         
30264         return;
30265     },
30266     
30267     setDisabled : function(state)
30268     {
30269         if(this.disabled == state){
30270             return;
30271         }
30272         
30273         this.disabled = state;
30274         
30275         if (state) {
30276             this.el.addClass('disabled');
30277             return;
30278         }
30279         
30280         this.el.removeClass('disabled');
30281     },
30282     
30283     tooltipEl : function()
30284     {
30285         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30286     }
30287 });
30288  
30289
30290  /*
30291  * - LGPL
30292  *
30293  * FieldLabel
30294  * 
30295  */
30296
30297 /**
30298  * @class Roo.bootstrap.FieldLabel
30299  * @extends Roo.bootstrap.Component
30300  * Bootstrap FieldLabel class
30301  * @cfg {String} html contents of the element
30302  * @cfg {String} tag tag of the element default label
30303  * @cfg {String} cls class of the element
30304  * @cfg {String} target label target 
30305  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30306  * @cfg {String} invalidClass default "text-warning"
30307  * @cfg {String} validClass default "text-success"
30308  * @cfg {String} iconTooltip default "This field is required"
30309  * @cfg {String} indicatorpos (left|right) default left
30310  * 
30311  * @constructor
30312  * Create a new FieldLabel
30313  * @param {Object} config The config object
30314  */
30315
30316 Roo.bootstrap.FieldLabel = function(config){
30317     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30318     
30319     this.addEvents({
30320             /**
30321              * @event invalid
30322              * Fires after the field has been marked as invalid.
30323              * @param {Roo.form.FieldLabel} this
30324              * @param {String} msg The validation message
30325              */
30326             invalid : true,
30327             /**
30328              * @event valid
30329              * Fires after the field has been validated with no errors.
30330              * @param {Roo.form.FieldLabel} this
30331              */
30332             valid : true
30333         });
30334 };
30335
30336 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30337     
30338     tag: 'label',
30339     cls: '',
30340     html: '',
30341     target: '',
30342     allowBlank : true,
30343     invalidClass : 'has-warning',
30344     validClass : 'has-success',
30345     iconTooltip : 'This field is required',
30346     indicatorpos : 'left',
30347     
30348     getAutoCreate : function(){
30349         
30350         var cls = "";
30351         if (!this.allowBlank) {
30352             cls  = "visible";
30353         }
30354         
30355         var cfg = {
30356             tag : this.tag,
30357             cls : 'roo-bootstrap-field-label ' + this.cls,
30358             for : this.target,
30359             cn : [
30360                 {
30361                     tag : 'i',
30362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30363                     tooltip : this.iconTooltip
30364                 },
30365                 {
30366                     tag : 'span',
30367                     html : this.html
30368                 }
30369             ] 
30370         };
30371         
30372         if(this.indicatorpos == 'right'){
30373             var cfg = {
30374                 tag : this.tag,
30375                 cls : 'roo-bootstrap-field-label ' + this.cls,
30376                 for : this.target,
30377                 cn : [
30378                     {
30379                         tag : 'span',
30380                         html : this.html
30381                     },
30382                     {
30383                         tag : 'i',
30384                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30385                         tooltip : this.iconTooltip
30386                     }
30387                 ] 
30388             };
30389         }
30390         
30391         return cfg;
30392     },
30393     
30394     initEvents: function() 
30395     {
30396         Roo.bootstrap.Element.superclass.initEvents.call(this);
30397         
30398         this.indicator = this.indicatorEl();
30399         
30400         if(this.indicator){
30401             this.indicator.removeClass('visible');
30402             this.indicator.addClass('invisible');
30403         }
30404         
30405         Roo.bootstrap.FieldLabel.register(this);
30406     },
30407     
30408     indicatorEl : function()
30409     {
30410         var indicator = this.el.select('i.roo-required-indicator',true).first();
30411         
30412         if(!indicator){
30413             return false;
30414         }
30415         
30416         return indicator;
30417         
30418     },
30419     
30420     /**
30421      * Mark this field as valid
30422      */
30423     markValid : function()
30424     {
30425         if(this.indicator){
30426             this.indicator.removeClass('visible');
30427             this.indicator.addClass('invisible');
30428         }
30429         
30430         this.el.removeClass(this.invalidClass);
30431         
30432         this.el.addClass(this.validClass);
30433         
30434         this.fireEvent('valid', this);
30435     },
30436     
30437     /**
30438      * Mark this field as invalid
30439      * @param {String} msg The validation message
30440      */
30441     markInvalid : function(msg)
30442     {
30443         if(this.indicator){
30444             this.indicator.removeClass('invisible');
30445             this.indicator.addClass('visible');
30446         }
30447         
30448         this.el.removeClass(this.validClass);
30449         
30450         this.el.addClass(this.invalidClass);
30451         
30452         this.fireEvent('invalid', this, msg);
30453     }
30454     
30455    
30456 });
30457
30458 Roo.apply(Roo.bootstrap.FieldLabel, {
30459     
30460     groups: {},
30461     
30462      /**
30463     * register a FieldLabel Group
30464     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30465     */
30466     register : function(label)
30467     {
30468         if(this.groups.hasOwnProperty(label.target)){
30469             return;
30470         }
30471      
30472         this.groups[label.target] = label;
30473         
30474     },
30475     /**
30476     * fetch a FieldLabel Group based on the target
30477     * @param {string} target
30478     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30479     */
30480     get: function(target) {
30481         if (typeof(this.groups[target]) == 'undefined') {
30482             return false;
30483         }
30484         
30485         return this.groups[target] ;
30486     }
30487 });
30488
30489  
30490
30491  /*
30492  * - LGPL
30493  *
30494  * page DateSplitField.
30495  * 
30496  */
30497
30498
30499 /**
30500  * @class Roo.bootstrap.DateSplitField
30501  * @extends Roo.bootstrap.Component
30502  * Bootstrap DateSplitField class
30503  * @cfg {string} fieldLabel - the label associated
30504  * @cfg {Number} labelWidth set the width of label (0-12)
30505  * @cfg {String} labelAlign (top|left)
30506  * @cfg {Boolean} dayAllowBlank (true|false) default false
30507  * @cfg {Boolean} monthAllowBlank (true|false) default false
30508  * @cfg {Boolean} yearAllowBlank (true|false) default false
30509  * @cfg {string} dayPlaceholder 
30510  * @cfg {string} monthPlaceholder
30511  * @cfg {string} yearPlaceholder
30512  * @cfg {string} dayFormat default 'd'
30513  * @cfg {string} monthFormat default 'm'
30514  * @cfg {string} yearFormat default 'Y'
30515  * @cfg {Number} labellg set the width of label (1-12)
30516  * @cfg {Number} labelmd set the width of label (1-12)
30517  * @cfg {Number} labelsm set the width of label (1-12)
30518  * @cfg {Number} labelxs set the width of label (1-12)
30519
30520  *     
30521  * @constructor
30522  * Create a new DateSplitField
30523  * @param {Object} config The config object
30524  */
30525
30526 Roo.bootstrap.DateSplitField = function(config){
30527     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30528     
30529     this.addEvents({
30530         // raw events
30531          /**
30532          * @event years
30533          * getting the data of years
30534          * @param {Roo.bootstrap.DateSplitField} this
30535          * @param {Object} years
30536          */
30537         "years" : true,
30538         /**
30539          * @event days
30540          * getting the data of days
30541          * @param {Roo.bootstrap.DateSplitField} this
30542          * @param {Object} days
30543          */
30544         "days" : true,
30545         /**
30546          * @event invalid
30547          * Fires after the field has been marked as invalid.
30548          * @param {Roo.form.Field} this
30549          * @param {String} msg The validation message
30550          */
30551         invalid : true,
30552        /**
30553          * @event valid
30554          * Fires after the field has been validated with no errors.
30555          * @param {Roo.form.Field} this
30556          */
30557         valid : true
30558     });
30559 };
30560
30561 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30562     
30563     fieldLabel : '',
30564     labelAlign : 'top',
30565     labelWidth : 3,
30566     dayAllowBlank : false,
30567     monthAllowBlank : false,
30568     yearAllowBlank : false,
30569     dayPlaceholder : '',
30570     monthPlaceholder : '',
30571     yearPlaceholder : '',
30572     dayFormat : 'd',
30573     monthFormat : 'm',
30574     yearFormat : 'Y',
30575     isFormField : true,
30576     labellg : 0,
30577     labelmd : 0,
30578     labelsm : 0,
30579     labelxs : 0,
30580     
30581     getAutoCreate : function()
30582     {
30583         var cfg = {
30584             tag : 'div',
30585             cls : 'row roo-date-split-field-group',
30586             cn : [
30587                 {
30588                     tag : 'input',
30589                     type : 'hidden',
30590                     cls : 'form-hidden-field roo-date-split-field-group-value',
30591                     name : this.name
30592                 }
30593             ]
30594         };
30595         
30596         var labelCls = 'col-md-12';
30597         var contentCls = 'col-md-4';
30598         
30599         if(this.fieldLabel){
30600             
30601             var label = {
30602                 tag : 'div',
30603                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30604                 cn : [
30605                     {
30606                         tag : 'label',
30607                         html : this.fieldLabel
30608                     }
30609                 ]
30610             };
30611             
30612             if(this.labelAlign == 'left'){
30613             
30614                 if(this.labelWidth > 12){
30615                     label.style = "width: " + this.labelWidth + 'px';
30616                 }
30617
30618                 if(this.labelWidth < 13 && this.labelmd == 0){
30619                     this.labelmd = this.labelWidth;
30620                 }
30621
30622                 if(this.labellg > 0){
30623                     labelCls = ' col-lg-' + this.labellg;
30624                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30625                 }
30626
30627                 if(this.labelmd > 0){
30628                     labelCls = ' col-md-' + this.labelmd;
30629                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30630                 }
30631
30632                 if(this.labelsm > 0){
30633                     labelCls = ' col-sm-' + this.labelsm;
30634                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30635                 }
30636
30637                 if(this.labelxs > 0){
30638                     labelCls = ' col-xs-' + this.labelxs;
30639                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30640                 }
30641             }
30642             
30643             label.cls += ' ' + labelCls;
30644             
30645             cfg.cn.push(label);
30646         }
30647         
30648         Roo.each(['day', 'month', 'year'], function(t){
30649             cfg.cn.push({
30650                 tag : 'div',
30651                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30652             });
30653         }, this);
30654         
30655         return cfg;
30656     },
30657     
30658     inputEl: function ()
30659     {
30660         return this.el.select('.roo-date-split-field-group-value', true).first();
30661     },
30662     
30663     onRender : function(ct, position) 
30664     {
30665         var _this = this;
30666         
30667         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30668         
30669         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30670         
30671         this.dayField = new Roo.bootstrap.ComboBox({
30672             allowBlank : this.dayAllowBlank,
30673             alwaysQuery : true,
30674             displayField : 'value',
30675             editable : false,
30676             fieldLabel : '',
30677             forceSelection : true,
30678             mode : 'local',
30679             placeholder : this.dayPlaceholder,
30680             selectOnFocus : true,
30681             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30682             triggerAction : 'all',
30683             typeAhead : true,
30684             valueField : 'value',
30685             store : new Roo.data.SimpleStore({
30686                 data : (function() {    
30687                     var days = [];
30688                     _this.fireEvent('days', _this, days);
30689                     return days;
30690                 })(),
30691                 fields : [ 'value' ]
30692             }),
30693             listeners : {
30694                 select : function (_self, record, index)
30695                 {
30696                     _this.setValue(_this.getValue());
30697                 }
30698             }
30699         });
30700
30701         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30702         
30703         this.monthField = new Roo.bootstrap.MonthField({
30704             after : '<i class=\"fa fa-calendar\"></i>',
30705             allowBlank : this.monthAllowBlank,
30706             placeholder : this.monthPlaceholder,
30707             readOnly : true,
30708             listeners : {
30709                 render : function (_self)
30710                 {
30711                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30712                         e.preventDefault();
30713                         _self.focus();
30714                     });
30715                 },
30716                 select : function (_self, oldvalue, newvalue)
30717                 {
30718                     _this.setValue(_this.getValue());
30719                 }
30720             }
30721         });
30722         
30723         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30724         
30725         this.yearField = new Roo.bootstrap.ComboBox({
30726             allowBlank : this.yearAllowBlank,
30727             alwaysQuery : true,
30728             displayField : 'value',
30729             editable : false,
30730             fieldLabel : '',
30731             forceSelection : true,
30732             mode : 'local',
30733             placeholder : this.yearPlaceholder,
30734             selectOnFocus : true,
30735             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30736             triggerAction : 'all',
30737             typeAhead : true,
30738             valueField : 'value',
30739             store : new Roo.data.SimpleStore({
30740                 data : (function() {
30741                     var years = [];
30742                     _this.fireEvent('years', _this, years);
30743                     return years;
30744                 })(),
30745                 fields : [ 'value' ]
30746             }),
30747             listeners : {
30748                 select : function (_self, record, index)
30749                 {
30750                     _this.setValue(_this.getValue());
30751                 }
30752             }
30753         });
30754
30755         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30756     },
30757     
30758     setValue : function(v, format)
30759     {
30760         this.inputEl.dom.value = v;
30761         
30762         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30763         
30764         var d = Date.parseDate(v, f);
30765         
30766         if(!d){
30767             this.validate();
30768             return;
30769         }
30770         
30771         this.setDay(d.format(this.dayFormat));
30772         this.setMonth(d.format(this.monthFormat));
30773         this.setYear(d.format(this.yearFormat));
30774         
30775         this.validate();
30776         
30777         return;
30778     },
30779     
30780     setDay : function(v)
30781     {
30782         this.dayField.setValue(v);
30783         this.inputEl.dom.value = this.getValue();
30784         this.validate();
30785         return;
30786     },
30787     
30788     setMonth : function(v)
30789     {
30790         this.monthField.setValue(v, true);
30791         this.inputEl.dom.value = this.getValue();
30792         this.validate();
30793         return;
30794     },
30795     
30796     setYear : function(v)
30797     {
30798         this.yearField.setValue(v);
30799         this.inputEl.dom.value = this.getValue();
30800         this.validate();
30801         return;
30802     },
30803     
30804     getDay : function()
30805     {
30806         return this.dayField.getValue();
30807     },
30808     
30809     getMonth : function()
30810     {
30811         return this.monthField.getValue();
30812     },
30813     
30814     getYear : function()
30815     {
30816         return this.yearField.getValue();
30817     },
30818     
30819     getValue : function()
30820     {
30821         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30822         
30823         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30824         
30825         return date;
30826     },
30827     
30828     reset : function()
30829     {
30830         this.setDay('');
30831         this.setMonth('');
30832         this.setYear('');
30833         this.inputEl.dom.value = '';
30834         this.validate();
30835         return;
30836     },
30837     
30838     validate : function()
30839     {
30840         var d = this.dayField.validate();
30841         var m = this.monthField.validate();
30842         var y = this.yearField.validate();
30843         
30844         var valid = true;
30845         
30846         if(
30847                 (!this.dayAllowBlank && !d) ||
30848                 (!this.monthAllowBlank && !m) ||
30849                 (!this.yearAllowBlank && !y)
30850         ){
30851             valid = false;
30852         }
30853         
30854         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30855             return valid;
30856         }
30857         
30858         if(valid){
30859             this.markValid();
30860             return valid;
30861         }
30862         
30863         this.markInvalid();
30864         
30865         return valid;
30866     },
30867     
30868     markValid : function()
30869     {
30870         
30871         var label = this.el.select('label', true).first();
30872         var icon = this.el.select('i.fa-star', true).first();
30873
30874         if(label && icon){
30875             icon.remove();
30876         }
30877         
30878         this.fireEvent('valid', this);
30879     },
30880     
30881      /**
30882      * Mark this field as invalid
30883      * @param {String} msg The validation message
30884      */
30885     markInvalid : function(msg)
30886     {
30887         
30888         var label = this.el.select('label', true).first();
30889         var icon = this.el.select('i.fa-star', true).first();
30890
30891         if(label && !icon){
30892             this.el.select('.roo-date-split-field-label', true).createChild({
30893                 tag : 'i',
30894                 cls : 'text-danger fa fa-lg fa-star',
30895                 tooltip : 'This field is required',
30896                 style : 'margin-right:5px;'
30897             }, label, true);
30898         }
30899         
30900         this.fireEvent('invalid', this, msg);
30901     },
30902     
30903     clearInvalid : function()
30904     {
30905         var label = this.el.select('label', true).first();
30906         var icon = this.el.select('i.fa-star', true).first();
30907
30908         if(label && icon){
30909             icon.remove();
30910         }
30911         
30912         this.fireEvent('valid', this);
30913     },
30914     
30915     getName: function()
30916     {
30917         return this.name;
30918     }
30919     
30920 });
30921
30922  /**
30923  *
30924  * This is based on 
30925  * http://masonry.desandro.com
30926  *
30927  * The idea is to render all the bricks based on vertical width...
30928  *
30929  * The original code extends 'outlayer' - we might need to use that....
30930  * 
30931  */
30932
30933
30934 /**
30935  * @class Roo.bootstrap.LayoutMasonry
30936  * @extends Roo.bootstrap.Component
30937  * Bootstrap Layout Masonry class
30938  * 
30939  * @constructor
30940  * Create a new Element
30941  * @param {Object} config The config object
30942  */
30943
30944 Roo.bootstrap.LayoutMasonry = function(config){
30945     
30946     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30947     
30948     this.bricks = [];
30949     
30950     Roo.bootstrap.LayoutMasonry.register(this);
30951     
30952     this.addEvents({
30953         // raw events
30954         /**
30955          * @event layout
30956          * Fire after layout the items
30957          * @param {Roo.bootstrap.LayoutMasonry} this
30958          * @param {Roo.EventObject} e
30959          */
30960         "layout" : true
30961     });
30962     
30963 };
30964
30965 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30966     
30967     /**
30968      * @cfg {Boolean} isLayoutInstant = no animation?
30969      */   
30970     isLayoutInstant : false, // needed?
30971    
30972     /**
30973      * @cfg {Number} boxWidth  width of the columns
30974      */   
30975     boxWidth : 450,
30976     
30977       /**
30978      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30979      */   
30980     boxHeight : 0,
30981     
30982     /**
30983      * @cfg {Number} padWidth padding below box..
30984      */   
30985     padWidth : 10, 
30986     
30987     /**
30988      * @cfg {Number} gutter gutter width..
30989      */   
30990     gutter : 10,
30991     
30992      /**
30993      * @cfg {Number} maxCols maximum number of columns
30994      */   
30995     
30996     maxCols: 0,
30997     
30998     /**
30999      * @cfg {Boolean} isAutoInitial defalut true
31000      */   
31001     isAutoInitial : true, 
31002     
31003     containerWidth: 0,
31004     
31005     /**
31006      * @cfg {Boolean} isHorizontal defalut false
31007      */   
31008     isHorizontal : false, 
31009
31010     currentSize : null,
31011     
31012     tag: 'div',
31013     
31014     cls: '',
31015     
31016     bricks: null, //CompositeElement
31017     
31018     cols : 1,
31019     
31020     _isLayoutInited : false,
31021     
31022 //    isAlternative : false, // only use for vertical layout...
31023     
31024     /**
31025      * @cfg {Number} alternativePadWidth padding below box..
31026      */   
31027     alternativePadWidth : 50,
31028     
31029     selectedBrick : [],
31030     
31031     getAutoCreate : function(){
31032         
31033         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31034         
31035         var cfg = {
31036             tag: this.tag,
31037             cls: 'blog-masonary-wrapper ' + this.cls,
31038             cn : {
31039                 cls : 'mas-boxes masonary'
31040             }
31041         };
31042         
31043         return cfg;
31044     },
31045     
31046     getChildContainer: function( )
31047     {
31048         if (this.boxesEl) {
31049             return this.boxesEl;
31050         }
31051         
31052         this.boxesEl = this.el.select('.mas-boxes').first();
31053         
31054         return this.boxesEl;
31055     },
31056     
31057     
31058     initEvents : function()
31059     {
31060         var _this = this;
31061         
31062         if(this.isAutoInitial){
31063             Roo.log('hook children rendered');
31064             this.on('childrenrendered', function() {
31065                 Roo.log('children rendered');
31066                 _this.initial();
31067             } ,this);
31068         }
31069     },
31070     
31071     initial : function()
31072     {
31073         this.selectedBrick = [];
31074         
31075         this.currentSize = this.el.getBox(true);
31076         
31077         Roo.EventManager.onWindowResize(this.resize, this); 
31078
31079         if(!this.isAutoInitial){
31080             this.layout();
31081             return;
31082         }
31083         
31084         this.layout();
31085         
31086         return;
31087         //this.layout.defer(500,this);
31088         
31089     },
31090     
31091     resize : function()
31092     {
31093         var cs = this.el.getBox(true);
31094         
31095         if (
31096                 this.currentSize.width == cs.width && 
31097                 this.currentSize.x == cs.x && 
31098                 this.currentSize.height == cs.height && 
31099                 this.currentSize.y == cs.y 
31100         ) {
31101             Roo.log("no change in with or X or Y");
31102             return;
31103         }
31104         
31105         this.currentSize = cs;
31106         
31107         this.layout();
31108         
31109     },
31110     
31111     layout : function()
31112     {   
31113         this._resetLayout();
31114         
31115         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31116         
31117         this.layoutItems( isInstant );
31118       
31119         this._isLayoutInited = true;
31120         
31121         this.fireEvent('layout', this);
31122         
31123     },
31124     
31125     _resetLayout : function()
31126     {
31127         if(this.isHorizontal){
31128             this.horizontalMeasureColumns();
31129             return;
31130         }
31131         
31132         this.verticalMeasureColumns();
31133         
31134     },
31135     
31136     verticalMeasureColumns : function()
31137     {
31138         this.getContainerWidth();
31139         
31140 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31141 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31142 //            return;
31143 //        }
31144         
31145         var boxWidth = this.boxWidth + this.padWidth;
31146         
31147         if(this.containerWidth < this.boxWidth){
31148             boxWidth = this.containerWidth
31149         }
31150         
31151         var containerWidth = this.containerWidth;
31152         
31153         var cols = Math.floor(containerWidth / boxWidth);
31154         
31155         this.cols = Math.max( cols, 1 );
31156         
31157         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31158         
31159         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31160         
31161         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31162         
31163         this.colWidth = boxWidth + avail - this.padWidth;
31164         
31165         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31166         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31167     },
31168     
31169     horizontalMeasureColumns : function()
31170     {
31171         this.getContainerWidth();
31172         
31173         var boxWidth = this.boxWidth;
31174         
31175         if(this.containerWidth < boxWidth){
31176             boxWidth = this.containerWidth;
31177         }
31178         
31179         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31180         
31181         this.el.setHeight(boxWidth);
31182         
31183     },
31184     
31185     getContainerWidth : function()
31186     {
31187         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31188     },
31189     
31190     layoutItems : function( isInstant )
31191     {
31192         Roo.log(this.bricks);
31193         
31194         var items = Roo.apply([], this.bricks);
31195         
31196         if(this.isHorizontal){
31197             this._horizontalLayoutItems( items , isInstant );
31198             return;
31199         }
31200         
31201 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31202 //            this._verticalAlternativeLayoutItems( items , isInstant );
31203 //            return;
31204 //        }
31205         
31206         this._verticalLayoutItems( items , isInstant );
31207         
31208     },
31209     
31210     _verticalLayoutItems : function ( items , isInstant)
31211     {
31212         if ( !items || !items.length ) {
31213             return;
31214         }
31215         
31216         var standard = [
31217             ['xs', 'xs', 'xs', 'tall'],
31218             ['xs', 'xs', 'tall'],
31219             ['xs', 'xs', 'sm'],
31220             ['xs', 'xs', 'xs'],
31221             ['xs', 'tall'],
31222             ['xs', 'sm'],
31223             ['xs', 'xs'],
31224             ['xs'],
31225             
31226             ['sm', 'xs', 'xs'],
31227             ['sm', 'xs'],
31228             ['sm'],
31229             
31230             ['tall', 'xs', 'xs', 'xs'],
31231             ['tall', 'xs', 'xs'],
31232             ['tall', 'xs'],
31233             ['tall']
31234             
31235         ];
31236         
31237         var queue = [];
31238         
31239         var boxes = [];
31240         
31241         var box = [];
31242         
31243         Roo.each(items, function(item, k){
31244             
31245             switch (item.size) {
31246                 // these layouts take up a full box,
31247                 case 'md' :
31248                 case 'md-left' :
31249                 case 'md-right' :
31250                 case 'wide' :
31251                     
31252                     if(box.length){
31253                         boxes.push(box);
31254                         box = [];
31255                     }
31256                     
31257                     boxes.push([item]);
31258                     
31259                     break;
31260                     
31261                 case 'xs' :
31262                 case 'sm' :
31263                 case 'tall' :
31264                     
31265                     box.push(item);
31266                     
31267                     break;
31268                 default :
31269                     break;
31270                     
31271             }
31272             
31273         }, this);
31274         
31275         if(box.length){
31276             boxes.push(box);
31277             box = [];
31278         }
31279         
31280         var filterPattern = function(box, length)
31281         {
31282             if(!box.length){
31283                 return;
31284             }
31285             
31286             var match = false;
31287             
31288             var pattern = box.slice(0, length);
31289             
31290             var format = [];
31291             
31292             Roo.each(pattern, function(i){
31293                 format.push(i.size);
31294             }, this);
31295             
31296             Roo.each(standard, function(s){
31297                 
31298                 if(String(s) != String(format)){
31299                     return;
31300                 }
31301                 
31302                 match = true;
31303                 return false;
31304                 
31305             }, this);
31306             
31307             if(!match && length == 1){
31308                 return;
31309             }
31310             
31311             if(!match){
31312                 filterPattern(box, length - 1);
31313                 return;
31314             }
31315                 
31316             queue.push(pattern);
31317
31318             box = box.slice(length, box.length);
31319
31320             filterPattern(box, 4);
31321
31322             return;
31323             
31324         }
31325         
31326         Roo.each(boxes, function(box, k){
31327             
31328             if(!box.length){
31329                 return;
31330             }
31331             
31332             if(box.length == 1){
31333                 queue.push(box);
31334                 return;
31335             }
31336             
31337             filterPattern(box, 4);
31338             
31339         }, this);
31340         
31341         this._processVerticalLayoutQueue( queue, isInstant );
31342         
31343     },
31344     
31345 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31346 //    {
31347 //        if ( !items || !items.length ) {
31348 //            return;
31349 //        }
31350 //
31351 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31352 //        
31353 //    },
31354     
31355     _horizontalLayoutItems : function ( items , isInstant)
31356     {
31357         if ( !items || !items.length || items.length < 3) {
31358             return;
31359         }
31360         
31361         items.reverse();
31362         
31363         var eItems = items.slice(0, 3);
31364         
31365         items = items.slice(3, items.length);
31366         
31367         var standard = [
31368             ['xs', 'xs', 'xs', 'wide'],
31369             ['xs', 'xs', 'wide'],
31370             ['xs', 'xs', 'sm'],
31371             ['xs', 'xs', 'xs'],
31372             ['xs', 'wide'],
31373             ['xs', 'sm'],
31374             ['xs', 'xs'],
31375             ['xs'],
31376             
31377             ['sm', 'xs', 'xs'],
31378             ['sm', 'xs'],
31379             ['sm'],
31380             
31381             ['wide', 'xs', 'xs', 'xs'],
31382             ['wide', 'xs', 'xs'],
31383             ['wide', 'xs'],
31384             ['wide'],
31385             
31386             ['wide-thin']
31387         ];
31388         
31389         var queue = [];
31390         
31391         var boxes = [];
31392         
31393         var box = [];
31394         
31395         Roo.each(items, function(item, k){
31396             
31397             switch (item.size) {
31398                 case 'md' :
31399                 case 'md-left' :
31400                 case 'md-right' :
31401                 case 'tall' :
31402                     
31403                     if(box.length){
31404                         boxes.push(box);
31405                         box = [];
31406                     }
31407                     
31408                     boxes.push([item]);
31409                     
31410                     break;
31411                     
31412                 case 'xs' :
31413                 case 'sm' :
31414                 case 'wide' :
31415                 case 'wide-thin' :
31416                     
31417                     box.push(item);
31418                     
31419                     break;
31420                 default :
31421                     break;
31422                     
31423             }
31424             
31425         }, this);
31426         
31427         if(box.length){
31428             boxes.push(box);
31429             box = [];
31430         }
31431         
31432         var filterPattern = function(box, length)
31433         {
31434             if(!box.length){
31435                 return;
31436             }
31437             
31438             var match = false;
31439             
31440             var pattern = box.slice(0, length);
31441             
31442             var format = [];
31443             
31444             Roo.each(pattern, function(i){
31445                 format.push(i.size);
31446             }, this);
31447             
31448             Roo.each(standard, function(s){
31449                 
31450                 if(String(s) != String(format)){
31451                     return;
31452                 }
31453                 
31454                 match = true;
31455                 return false;
31456                 
31457             }, this);
31458             
31459             if(!match && length == 1){
31460                 return;
31461             }
31462             
31463             if(!match){
31464                 filterPattern(box, length - 1);
31465                 return;
31466             }
31467                 
31468             queue.push(pattern);
31469
31470             box = box.slice(length, box.length);
31471
31472             filterPattern(box, 4);
31473
31474             return;
31475             
31476         }
31477         
31478         Roo.each(boxes, function(box, k){
31479             
31480             if(!box.length){
31481                 return;
31482             }
31483             
31484             if(box.length == 1){
31485                 queue.push(box);
31486                 return;
31487             }
31488             
31489             filterPattern(box, 4);
31490             
31491         }, this);
31492         
31493         
31494         var prune = [];
31495         
31496         var pos = this.el.getBox(true);
31497         
31498         var minX = pos.x;
31499         
31500         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31501         
31502         var hit_end = false;
31503         
31504         Roo.each(queue, function(box){
31505             
31506             if(hit_end){
31507                 
31508                 Roo.each(box, function(b){
31509                 
31510                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31511                     b.el.hide();
31512
31513                 }, this);
31514
31515                 return;
31516             }
31517             
31518             var mx = 0;
31519             
31520             Roo.each(box, function(b){
31521                 
31522                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31523                 b.el.show();
31524
31525                 mx = Math.max(mx, b.x);
31526                 
31527             }, this);
31528             
31529             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31530             
31531             if(maxX < minX){
31532                 
31533                 Roo.each(box, function(b){
31534                 
31535                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31536                     b.el.hide();
31537                     
31538                 }, this);
31539                 
31540                 hit_end = true;
31541                 
31542                 return;
31543             }
31544             
31545             prune.push(box);
31546             
31547         }, this);
31548         
31549         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31550     },
31551     
31552     /** Sets position of item in DOM
31553     * @param {Element} item
31554     * @param {Number} x - horizontal position
31555     * @param {Number} y - vertical position
31556     * @param {Boolean} isInstant - disables transitions
31557     */
31558     _processVerticalLayoutQueue : function( queue, isInstant )
31559     {
31560         var pos = this.el.getBox(true);
31561         var x = pos.x;
31562         var y = pos.y;
31563         var maxY = [];
31564         
31565         for (var i = 0; i < this.cols; i++){
31566             maxY[i] = pos.y;
31567         }
31568         
31569         Roo.each(queue, function(box, k){
31570             
31571             var col = k % this.cols;
31572             
31573             Roo.each(box, function(b,kk){
31574                 
31575                 b.el.position('absolute');
31576                 
31577                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31578                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31579                 
31580                 if(b.size == 'md-left' || b.size == 'md-right'){
31581                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31582                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31583                 }
31584                 
31585                 b.el.setWidth(width);
31586                 b.el.setHeight(height);
31587                 // iframe?
31588                 b.el.select('iframe',true).setSize(width,height);
31589                 
31590             }, this);
31591             
31592             for (var i = 0; i < this.cols; i++){
31593                 
31594                 if(maxY[i] < maxY[col]){
31595                     col = i;
31596                     continue;
31597                 }
31598                 
31599                 col = Math.min(col, i);
31600                 
31601             }
31602             
31603             x = pos.x + col * (this.colWidth + this.padWidth);
31604             
31605             y = maxY[col];
31606             
31607             var positions = [];
31608             
31609             switch (box.length){
31610                 case 1 :
31611                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31612                     break;
31613                 case 2 :
31614                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31615                     break;
31616                 case 3 :
31617                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31618                     break;
31619                 case 4 :
31620                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31621                     break;
31622                 default :
31623                     break;
31624             }
31625             
31626             Roo.each(box, function(b,kk){
31627                 
31628                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31629                 
31630                 var sz = b.el.getSize();
31631                 
31632                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31633                 
31634             }, this);
31635             
31636         }, this);
31637         
31638         var mY = 0;
31639         
31640         for (var i = 0; i < this.cols; i++){
31641             mY = Math.max(mY, maxY[i]);
31642         }
31643         
31644         this.el.setHeight(mY - pos.y);
31645         
31646     },
31647     
31648 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31649 //    {
31650 //        var pos = this.el.getBox(true);
31651 //        var x = pos.x;
31652 //        var y = pos.y;
31653 //        var maxX = pos.right;
31654 //        
31655 //        var maxHeight = 0;
31656 //        
31657 //        Roo.each(items, function(item, k){
31658 //            
31659 //            var c = k % 2;
31660 //            
31661 //            item.el.position('absolute');
31662 //                
31663 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31664 //
31665 //            item.el.setWidth(width);
31666 //
31667 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31668 //
31669 //            item.el.setHeight(height);
31670 //            
31671 //            if(c == 0){
31672 //                item.el.setXY([x, y], isInstant ? false : true);
31673 //            } else {
31674 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31675 //            }
31676 //            
31677 //            y = y + height + this.alternativePadWidth;
31678 //            
31679 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31680 //            
31681 //        }, this);
31682 //        
31683 //        this.el.setHeight(maxHeight);
31684 //        
31685 //    },
31686     
31687     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31688     {
31689         var pos = this.el.getBox(true);
31690         
31691         var minX = pos.x;
31692         var minY = pos.y;
31693         
31694         var maxX = pos.right;
31695         
31696         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31697         
31698         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31699         
31700         Roo.each(queue, function(box, k){
31701             
31702             Roo.each(box, function(b, kk){
31703                 
31704                 b.el.position('absolute');
31705                 
31706                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31707                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31708                 
31709                 if(b.size == 'md-left' || b.size == 'md-right'){
31710                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31711                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31712                 }
31713                 
31714                 b.el.setWidth(width);
31715                 b.el.setHeight(height);
31716                 
31717             }, this);
31718             
31719             if(!box.length){
31720                 return;
31721             }
31722             
31723             var positions = [];
31724             
31725             switch (box.length){
31726                 case 1 :
31727                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31728                     break;
31729                 case 2 :
31730                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31731                     break;
31732                 case 3 :
31733                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31734                     break;
31735                 case 4 :
31736                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31737                     break;
31738                 default :
31739                     break;
31740             }
31741             
31742             Roo.each(box, function(b,kk){
31743                 
31744                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31745                 
31746                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31747                 
31748             }, this);
31749             
31750         }, this);
31751         
31752     },
31753     
31754     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31755     {
31756         Roo.each(eItems, function(b,k){
31757             
31758             b.size = (k == 0) ? 'sm' : 'xs';
31759             b.x = (k == 0) ? 2 : 1;
31760             b.y = (k == 0) ? 2 : 1;
31761             
31762             b.el.position('absolute');
31763             
31764             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31765                 
31766             b.el.setWidth(width);
31767             
31768             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31769             
31770             b.el.setHeight(height);
31771             
31772         }, this);
31773
31774         var positions = [];
31775         
31776         positions.push({
31777             x : maxX - this.unitWidth * 2 - this.gutter,
31778             y : minY
31779         });
31780         
31781         positions.push({
31782             x : maxX - this.unitWidth,
31783             y : minY + (this.unitWidth + this.gutter) * 2
31784         });
31785         
31786         positions.push({
31787             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31788             y : minY
31789         });
31790         
31791         Roo.each(eItems, function(b,k){
31792             
31793             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31794
31795         }, this);
31796         
31797     },
31798     
31799     getVerticalOneBoxColPositions : function(x, y, box)
31800     {
31801         var pos = [];
31802         
31803         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31804         
31805         if(box[0].size == 'md-left'){
31806             rand = 0;
31807         }
31808         
31809         if(box[0].size == 'md-right'){
31810             rand = 1;
31811         }
31812         
31813         pos.push({
31814             x : x + (this.unitWidth + this.gutter) * rand,
31815             y : y
31816         });
31817         
31818         return pos;
31819     },
31820     
31821     getVerticalTwoBoxColPositions : function(x, y, box)
31822     {
31823         var pos = [];
31824         
31825         if(box[0].size == 'xs'){
31826             
31827             pos.push({
31828                 x : x,
31829                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31830             });
31831
31832             pos.push({
31833                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31834                 y : y
31835             });
31836             
31837             return pos;
31838             
31839         }
31840         
31841         pos.push({
31842             x : x,
31843             y : y
31844         });
31845
31846         pos.push({
31847             x : x + (this.unitWidth + this.gutter) * 2,
31848             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31849         });
31850         
31851         return pos;
31852         
31853     },
31854     
31855     getVerticalThreeBoxColPositions : function(x, y, box)
31856     {
31857         var pos = [];
31858         
31859         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31860             
31861             pos.push({
31862                 x : x,
31863                 y : y
31864             });
31865
31866             pos.push({
31867                 x : x + (this.unitWidth + this.gutter) * 1,
31868                 y : y
31869             });
31870             
31871             pos.push({
31872                 x : x + (this.unitWidth + this.gutter) * 2,
31873                 y : y
31874             });
31875             
31876             return pos;
31877             
31878         }
31879         
31880         if(box[0].size == 'xs' && box[1].size == 'xs'){
31881             
31882             pos.push({
31883                 x : x,
31884                 y : y
31885             });
31886
31887             pos.push({
31888                 x : x,
31889                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31890             });
31891             
31892             pos.push({
31893                 x : x + (this.unitWidth + this.gutter) * 1,
31894                 y : y
31895             });
31896             
31897             return pos;
31898             
31899         }
31900         
31901         pos.push({
31902             x : x,
31903             y : y
31904         });
31905
31906         pos.push({
31907             x : x + (this.unitWidth + this.gutter) * 2,
31908             y : y
31909         });
31910
31911         pos.push({
31912             x : x + (this.unitWidth + this.gutter) * 2,
31913             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31914         });
31915             
31916         return pos;
31917         
31918     },
31919     
31920     getVerticalFourBoxColPositions : function(x, y, box)
31921     {
31922         var pos = [];
31923         
31924         if(box[0].size == 'xs'){
31925             
31926             pos.push({
31927                 x : x,
31928                 y : y
31929             });
31930
31931             pos.push({
31932                 x : x,
31933                 y : y + (this.unitHeight + this.gutter) * 1
31934             });
31935             
31936             pos.push({
31937                 x : x,
31938                 y : y + (this.unitHeight + this.gutter) * 2
31939             });
31940             
31941             pos.push({
31942                 x : x + (this.unitWidth + this.gutter) * 1,
31943                 y : y
31944             });
31945             
31946             return pos;
31947             
31948         }
31949         
31950         pos.push({
31951             x : x,
31952             y : y
31953         });
31954
31955         pos.push({
31956             x : x + (this.unitWidth + this.gutter) * 2,
31957             y : y
31958         });
31959
31960         pos.push({
31961             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31962             y : y + (this.unitHeight + this.gutter) * 1
31963         });
31964
31965         pos.push({
31966             x : x + (this.unitWidth + this.gutter) * 2,
31967             y : y + (this.unitWidth + this.gutter) * 2
31968         });
31969
31970         return pos;
31971         
31972     },
31973     
31974     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31975     {
31976         var pos = [];
31977         
31978         if(box[0].size == 'md-left'){
31979             pos.push({
31980                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31981                 y : minY
31982             });
31983             
31984             return pos;
31985         }
31986         
31987         if(box[0].size == 'md-right'){
31988             pos.push({
31989                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31990                 y : minY + (this.unitWidth + this.gutter) * 1
31991             });
31992             
31993             return pos;
31994         }
31995         
31996         var rand = Math.floor(Math.random() * (4 - box[0].y));
31997         
31998         pos.push({
31999             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32000             y : minY + (this.unitWidth + this.gutter) * rand
32001         });
32002         
32003         return pos;
32004         
32005     },
32006     
32007     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32008     {
32009         var pos = [];
32010         
32011         if(box[0].size == 'xs'){
32012             
32013             pos.push({
32014                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32015                 y : minY
32016             });
32017
32018             pos.push({
32019                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32020                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32021             });
32022             
32023             return pos;
32024             
32025         }
32026         
32027         pos.push({
32028             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32029             y : minY
32030         });
32031
32032         pos.push({
32033             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32034             y : minY + (this.unitWidth + this.gutter) * 2
32035         });
32036         
32037         return pos;
32038         
32039     },
32040     
32041     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32042     {
32043         var pos = [];
32044         
32045         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32046             
32047             pos.push({
32048                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32049                 y : minY
32050             });
32051
32052             pos.push({
32053                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32054                 y : minY + (this.unitWidth + this.gutter) * 1
32055             });
32056             
32057             pos.push({
32058                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32059                 y : minY + (this.unitWidth + this.gutter) * 2
32060             });
32061             
32062             return pos;
32063             
32064         }
32065         
32066         if(box[0].size == 'xs' && box[1].size == 'xs'){
32067             
32068             pos.push({
32069                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32070                 y : minY
32071             });
32072
32073             pos.push({
32074                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32075                 y : minY
32076             });
32077             
32078             pos.push({
32079                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32080                 y : minY + (this.unitWidth + this.gutter) * 1
32081             });
32082             
32083             return pos;
32084             
32085         }
32086         
32087         pos.push({
32088             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32089             y : minY
32090         });
32091
32092         pos.push({
32093             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32094             y : minY + (this.unitWidth + this.gutter) * 2
32095         });
32096
32097         pos.push({
32098             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32099             y : minY + (this.unitWidth + this.gutter) * 2
32100         });
32101             
32102         return pos;
32103         
32104     },
32105     
32106     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32107     {
32108         var pos = [];
32109         
32110         if(box[0].size == 'xs'){
32111             
32112             pos.push({
32113                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32114                 y : minY
32115             });
32116
32117             pos.push({
32118                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32119                 y : minY
32120             });
32121             
32122             pos.push({
32123                 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),
32124                 y : minY
32125             });
32126             
32127             pos.push({
32128                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32129                 y : minY + (this.unitWidth + this.gutter) * 1
32130             });
32131             
32132             return pos;
32133             
32134         }
32135         
32136         pos.push({
32137             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32138             y : minY
32139         });
32140         
32141         pos.push({
32142             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32143             y : minY + (this.unitWidth + this.gutter) * 2
32144         });
32145         
32146         pos.push({
32147             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32148             y : minY + (this.unitWidth + this.gutter) * 2
32149         });
32150         
32151         pos.push({
32152             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),
32153             y : minY + (this.unitWidth + this.gutter) * 2
32154         });
32155
32156         return pos;
32157         
32158     },
32159     
32160     /**
32161     * remove a Masonry Brick
32162     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32163     */
32164     removeBrick : function(brick_id)
32165     {
32166         if (!brick_id) {
32167             return;
32168         }
32169         
32170         for (var i = 0; i<this.bricks.length; i++) {
32171             if (this.bricks[i].id == brick_id) {
32172                 this.bricks.splice(i,1);
32173                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32174                 this.initial();
32175             }
32176         }
32177     },
32178     
32179     /**
32180     * adds a Masonry Brick
32181     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32182     */
32183     addBrick : function(cfg)
32184     {
32185         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32186         //this.register(cn);
32187         cn.parentId = this.id;
32188         cn.render(this.el);
32189         return cn;
32190     },
32191     
32192     /**
32193     * register a Masonry Brick
32194     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32195     */
32196     
32197     register : function(brick)
32198     {
32199         this.bricks.push(brick);
32200         brick.masonryId = this.id;
32201     },
32202     
32203     /**
32204     * clear all the Masonry Brick
32205     */
32206     clearAll : function()
32207     {
32208         this.bricks = [];
32209         //this.getChildContainer().dom.innerHTML = "";
32210         this.el.dom.innerHTML = '';
32211     },
32212     
32213     getSelected : function()
32214     {
32215         if (!this.selectedBrick) {
32216             return false;
32217         }
32218         
32219         return this.selectedBrick;
32220     }
32221 });
32222
32223 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32224     
32225     groups: {},
32226      /**
32227     * register a Masonry Layout
32228     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32229     */
32230     
32231     register : function(layout)
32232     {
32233         this.groups[layout.id] = layout;
32234     },
32235     /**
32236     * fetch a  Masonry Layout based on the masonry layout ID
32237     * @param {string} the masonry layout to add
32238     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32239     */
32240     
32241     get: function(layout_id) {
32242         if (typeof(this.groups[layout_id]) == 'undefined') {
32243             return false;
32244         }
32245         return this.groups[layout_id] ;
32246     }
32247     
32248     
32249     
32250 });
32251
32252  
32253
32254  /**
32255  *
32256  * This is based on 
32257  * http://masonry.desandro.com
32258  *
32259  * The idea is to render all the bricks based on vertical width...
32260  *
32261  * The original code extends 'outlayer' - we might need to use that....
32262  * 
32263  */
32264
32265
32266 /**
32267  * @class Roo.bootstrap.LayoutMasonryAuto
32268  * @extends Roo.bootstrap.Component
32269  * Bootstrap Layout Masonry class
32270  * 
32271  * @constructor
32272  * Create a new Element
32273  * @param {Object} config The config object
32274  */
32275
32276 Roo.bootstrap.LayoutMasonryAuto = function(config){
32277     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32278 };
32279
32280 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32281     
32282       /**
32283      * @cfg {Boolean} isFitWidth  - resize the width..
32284      */   
32285     isFitWidth : false,  // options..
32286     /**
32287      * @cfg {Boolean} isOriginLeft = left align?
32288      */   
32289     isOriginLeft : true,
32290     /**
32291      * @cfg {Boolean} isOriginTop = top align?
32292      */   
32293     isOriginTop : false,
32294     /**
32295      * @cfg {Boolean} isLayoutInstant = no animation?
32296      */   
32297     isLayoutInstant : false, // needed?
32298     /**
32299      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32300      */   
32301     isResizingContainer : true,
32302     /**
32303      * @cfg {Number} columnWidth  width of the columns 
32304      */   
32305     
32306     columnWidth : 0,
32307     
32308     /**
32309      * @cfg {Number} maxCols maximum number of columns
32310      */   
32311     
32312     maxCols: 0,
32313     /**
32314      * @cfg {Number} padHeight padding below box..
32315      */   
32316     
32317     padHeight : 10, 
32318     
32319     /**
32320      * @cfg {Boolean} isAutoInitial defalut true
32321      */   
32322     
32323     isAutoInitial : true, 
32324     
32325     // private?
32326     gutter : 0,
32327     
32328     containerWidth: 0,
32329     initialColumnWidth : 0,
32330     currentSize : null,
32331     
32332     colYs : null, // array.
32333     maxY : 0,
32334     padWidth: 10,
32335     
32336     
32337     tag: 'div',
32338     cls: '',
32339     bricks: null, //CompositeElement
32340     cols : 0, // array?
32341     // element : null, // wrapped now this.el
32342     _isLayoutInited : null, 
32343     
32344     
32345     getAutoCreate : function(){
32346         
32347         var cfg = {
32348             tag: this.tag,
32349             cls: 'blog-masonary-wrapper ' + this.cls,
32350             cn : {
32351                 cls : 'mas-boxes masonary'
32352             }
32353         };
32354         
32355         return cfg;
32356     },
32357     
32358     getChildContainer: function( )
32359     {
32360         if (this.boxesEl) {
32361             return this.boxesEl;
32362         }
32363         
32364         this.boxesEl = this.el.select('.mas-boxes').first();
32365         
32366         return this.boxesEl;
32367     },
32368     
32369     
32370     initEvents : function()
32371     {
32372         var _this = this;
32373         
32374         if(this.isAutoInitial){
32375             Roo.log('hook children rendered');
32376             this.on('childrenrendered', function() {
32377                 Roo.log('children rendered');
32378                 _this.initial();
32379             } ,this);
32380         }
32381         
32382     },
32383     
32384     initial : function()
32385     {
32386         this.reloadItems();
32387
32388         this.currentSize = this.el.getBox(true);
32389
32390         /// was window resize... - let's see if this works..
32391         Roo.EventManager.onWindowResize(this.resize, this); 
32392
32393         if(!this.isAutoInitial){
32394             this.layout();
32395             return;
32396         }
32397         
32398         this.layout.defer(500,this);
32399     },
32400     
32401     reloadItems: function()
32402     {
32403         this.bricks = this.el.select('.masonry-brick', true);
32404         
32405         this.bricks.each(function(b) {
32406             //Roo.log(b.getSize());
32407             if (!b.attr('originalwidth')) {
32408                 b.attr('originalwidth',  b.getSize().width);
32409             }
32410             
32411         });
32412         
32413         Roo.log(this.bricks.elements.length);
32414     },
32415     
32416     resize : function()
32417     {
32418         Roo.log('resize');
32419         var cs = this.el.getBox(true);
32420         
32421         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32422             Roo.log("no change in with or X");
32423             return;
32424         }
32425         this.currentSize = cs;
32426         this.layout();
32427     },
32428     
32429     layout : function()
32430     {
32431          Roo.log('layout');
32432         this._resetLayout();
32433         //this._manageStamps();
32434       
32435         // don't animate first layout
32436         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32437         this.layoutItems( isInstant );
32438       
32439         // flag for initalized
32440         this._isLayoutInited = true;
32441     },
32442     
32443     layoutItems : function( isInstant )
32444     {
32445         //var items = this._getItemsForLayout( this.items );
32446         // original code supports filtering layout items.. we just ignore it..
32447         
32448         this._layoutItems( this.bricks , isInstant );
32449       
32450         this._postLayout();
32451     },
32452     _layoutItems : function ( items , isInstant)
32453     {
32454        //this.fireEvent( 'layout', this, items );
32455     
32456
32457         if ( !items || !items.elements.length ) {
32458           // no items, emit event with empty array
32459             return;
32460         }
32461
32462         var queue = [];
32463         items.each(function(item) {
32464             Roo.log("layout item");
32465             Roo.log(item);
32466             // get x/y object from method
32467             var position = this._getItemLayoutPosition( item );
32468             // enqueue
32469             position.item = item;
32470             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32471             queue.push( position );
32472         }, this);
32473       
32474         this._processLayoutQueue( queue );
32475     },
32476     /** Sets position of item in DOM
32477     * @param {Element} item
32478     * @param {Number} x - horizontal position
32479     * @param {Number} y - vertical position
32480     * @param {Boolean} isInstant - disables transitions
32481     */
32482     _processLayoutQueue : function( queue )
32483     {
32484         for ( var i=0, len = queue.length; i < len; i++ ) {
32485             var obj = queue[i];
32486             obj.item.position('absolute');
32487             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32488         }
32489     },
32490       
32491     
32492     /**
32493     * Any logic you want to do after each layout,
32494     * i.e. size the container
32495     */
32496     _postLayout : function()
32497     {
32498         this.resizeContainer();
32499     },
32500     
32501     resizeContainer : function()
32502     {
32503         if ( !this.isResizingContainer ) {
32504             return;
32505         }
32506         var size = this._getContainerSize();
32507         if ( size ) {
32508             this.el.setSize(size.width,size.height);
32509             this.boxesEl.setSize(size.width,size.height);
32510         }
32511     },
32512     
32513     
32514     
32515     _resetLayout : function()
32516     {
32517         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32518         this.colWidth = this.el.getWidth();
32519         //this.gutter = this.el.getWidth(); 
32520         
32521         this.measureColumns();
32522
32523         // reset column Y
32524         var i = this.cols;
32525         this.colYs = [];
32526         while (i--) {
32527             this.colYs.push( 0 );
32528         }
32529     
32530         this.maxY = 0;
32531     },
32532
32533     measureColumns : function()
32534     {
32535         this.getContainerWidth();
32536       // if columnWidth is 0, default to outerWidth of first item
32537         if ( !this.columnWidth ) {
32538             var firstItem = this.bricks.first();
32539             Roo.log(firstItem);
32540             this.columnWidth  = this.containerWidth;
32541             if (firstItem && firstItem.attr('originalwidth') ) {
32542                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32543             }
32544             // columnWidth fall back to item of first element
32545             Roo.log("set column width?");
32546                         this.initialColumnWidth = this.columnWidth  ;
32547
32548             // if first elem has no width, default to size of container
32549             
32550         }
32551         
32552         
32553         if (this.initialColumnWidth) {
32554             this.columnWidth = this.initialColumnWidth;
32555         }
32556         
32557         
32558             
32559         // column width is fixed at the top - however if container width get's smaller we should
32560         // reduce it...
32561         
32562         // this bit calcs how man columns..
32563             
32564         var columnWidth = this.columnWidth += this.gutter;
32565       
32566         // calculate columns
32567         var containerWidth = this.containerWidth + this.gutter;
32568         
32569         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32570         // fix rounding errors, typically with gutters
32571         var excess = columnWidth - containerWidth % columnWidth;
32572         
32573         
32574         // if overshoot is less than a pixel, round up, otherwise floor it
32575         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32576         cols = Math[ mathMethod ]( cols );
32577         this.cols = Math.max( cols, 1 );
32578         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32579         
32580          // padding positioning..
32581         var totalColWidth = this.cols * this.columnWidth;
32582         var padavail = this.containerWidth - totalColWidth;
32583         // so for 2 columns - we need 3 'pads'
32584         
32585         var padNeeded = (1+this.cols) * this.padWidth;
32586         
32587         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32588         
32589         this.columnWidth += padExtra
32590         //this.padWidth = Math.floor(padavail /  ( this.cols));
32591         
32592         // adjust colum width so that padding is fixed??
32593         
32594         // we have 3 columns ... total = width * 3
32595         // we have X left over... that should be used by 
32596         
32597         //if (this.expandC) {
32598             
32599         //}
32600         
32601         
32602         
32603     },
32604     
32605     getContainerWidth : function()
32606     {
32607        /* // container is parent if fit width
32608         var container = this.isFitWidth ? this.element.parentNode : this.element;
32609         // check that this.size and size are there
32610         // IE8 triggers resize on body size change, so they might not be
32611         
32612         var size = getSize( container );  //FIXME
32613         this.containerWidth = size && size.innerWidth; //FIXME
32614         */
32615          
32616         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32617         
32618     },
32619     
32620     _getItemLayoutPosition : function( item )  // what is item?
32621     {
32622         // we resize the item to our columnWidth..
32623       
32624         item.setWidth(this.columnWidth);
32625         item.autoBoxAdjust  = false;
32626         
32627         var sz = item.getSize();
32628  
32629         // how many columns does this brick span
32630         var remainder = this.containerWidth % this.columnWidth;
32631         
32632         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32633         // round if off by 1 pixel, otherwise use ceil
32634         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32635         colSpan = Math.min( colSpan, this.cols );
32636         
32637         // normally this should be '1' as we dont' currently allow multi width columns..
32638         
32639         var colGroup = this._getColGroup( colSpan );
32640         // get the minimum Y value from the columns
32641         var minimumY = Math.min.apply( Math, colGroup );
32642         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32643         
32644         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32645          
32646         // position the brick
32647         var position = {
32648             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32649             y: this.currentSize.y + minimumY + this.padHeight
32650         };
32651         
32652         Roo.log(position);
32653         // apply setHeight to necessary columns
32654         var setHeight = minimumY + sz.height + this.padHeight;
32655         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32656         
32657         var setSpan = this.cols + 1 - colGroup.length;
32658         for ( var i = 0; i < setSpan; i++ ) {
32659           this.colYs[ shortColIndex + i ] = setHeight ;
32660         }
32661       
32662         return position;
32663     },
32664     
32665     /**
32666      * @param {Number} colSpan - number of columns the element spans
32667      * @returns {Array} colGroup
32668      */
32669     _getColGroup : function( colSpan )
32670     {
32671         if ( colSpan < 2 ) {
32672           // if brick spans only one column, use all the column Ys
32673           return this.colYs;
32674         }
32675       
32676         var colGroup = [];
32677         // how many different places could this brick fit horizontally
32678         var groupCount = this.cols + 1 - colSpan;
32679         // for each group potential horizontal position
32680         for ( var i = 0; i < groupCount; i++ ) {
32681           // make an array of colY values for that one group
32682           var groupColYs = this.colYs.slice( i, i + colSpan );
32683           // and get the max value of the array
32684           colGroup[i] = Math.max.apply( Math, groupColYs );
32685         }
32686         return colGroup;
32687     },
32688     /*
32689     _manageStamp : function( stamp )
32690     {
32691         var stampSize =  stamp.getSize();
32692         var offset = stamp.getBox();
32693         // get the columns that this stamp affects
32694         var firstX = this.isOriginLeft ? offset.x : offset.right;
32695         var lastX = firstX + stampSize.width;
32696         var firstCol = Math.floor( firstX / this.columnWidth );
32697         firstCol = Math.max( 0, firstCol );
32698         
32699         var lastCol = Math.floor( lastX / this.columnWidth );
32700         // lastCol should not go over if multiple of columnWidth #425
32701         lastCol -= lastX % this.columnWidth ? 0 : 1;
32702         lastCol = Math.min( this.cols - 1, lastCol );
32703         
32704         // set colYs to bottom of the stamp
32705         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32706             stampSize.height;
32707             
32708         for ( var i = firstCol; i <= lastCol; i++ ) {
32709           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32710         }
32711     },
32712     */
32713     
32714     _getContainerSize : function()
32715     {
32716         this.maxY = Math.max.apply( Math, this.colYs );
32717         var size = {
32718             height: this.maxY
32719         };
32720       
32721         if ( this.isFitWidth ) {
32722             size.width = this._getContainerFitWidth();
32723         }
32724       
32725         return size;
32726     },
32727     
32728     _getContainerFitWidth : function()
32729     {
32730         var unusedCols = 0;
32731         // count unused columns
32732         var i = this.cols;
32733         while ( --i ) {
32734           if ( this.colYs[i] !== 0 ) {
32735             break;
32736           }
32737           unusedCols++;
32738         }
32739         // fit container to columns that have been used
32740         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32741     },
32742     
32743     needsResizeLayout : function()
32744     {
32745         var previousWidth = this.containerWidth;
32746         this.getContainerWidth();
32747         return previousWidth !== this.containerWidth;
32748     }
32749  
32750 });
32751
32752  
32753
32754  /*
32755  * - LGPL
32756  *
32757  * element
32758  * 
32759  */
32760
32761 /**
32762  * @class Roo.bootstrap.MasonryBrick
32763  * @extends Roo.bootstrap.Component
32764  * Bootstrap MasonryBrick class
32765  * 
32766  * @constructor
32767  * Create a new MasonryBrick
32768  * @param {Object} config The config object
32769  */
32770
32771 Roo.bootstrap.MasonryBrick = function(config){
32772     
32773     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32774     
32775     Roo.bootstrap.MasonryBrick.register(this);
32776     
32777     this.addEvents({
32778         // raw events
32779         /**
32780          * @event click
32781          * When a MasonryBrick is clcik
32782          * @param {Roo.bootstrap.MasonryBrick} this
32783          * @param {Roo.EventObject} e
32784          */
32785         "click" : true
32786     });
32787 };
32788
32789 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32790     
32791     /**
32792      * @cfg {String} title
32793      */   
32794     title : '',
32795     /**
32796      * @cfg {String} html
32797      */   
32798     html : '',
32799     /**
32800      * @cfg {String} bgimage
32801      */   
32802     bgimage : '',
32803     /**
32804      * @cfg {String} videourl
32805      */   
32806     videourl : '',
32807     /**
32808      * @cfg {String} cls
32809      */   
32810     cls : '',
32811     /**
32812      * @cfg {String} href
32813      */   
32814     href : '',
32815     /**
32816      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32817      */   
32818     size : 'xs',
32819     
32820     /**
32821      * @cfg {String} placetitle (center|bottom)
32822      */   
32823     placetitle : '',
32824     
32825     /**
32826      * @cfg {Boolean} isFitContainer defalut true
32827      */   
32828     isFitContainer : true, 
32829     
32830     /**
32831      * @cfg {Boolean} preventDefault defalut false
32832      */   
32833     preventDefault : false, 
32834     
32835     /**
32836      * @cfg {Boolean} inverse defalut false
32837      */   
32838     maskInverse : false, 
32839     
32840     getAutoCreate : function()
32841     {
32842         if(!this.isFitContainer){
32843             return this.getSplitAutoCreate();
32844         }
32845         
32846         var cls = 'masonry-brick masonry-brick-full';
32847         
32848         if(this.href.length){
32849             cls += ' masonry-brick-link';
32850         }
32851         
32852         if(this.bgimage.length){
32853             cls += ' masonry-brick-image';
32854         }
32855         
32856         if(this.maskInverse){
32857             cls += ' mask-inverse';
32858         }
32859         
32860         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32861             cls += ' enable-mask';
32862         }
32863         
32864         if(this.size){
32865             cls += ' masonry-' + this.size + '-brick';
32866         }
32867         
32868         if(this.placetitle.length){
32869             
32870             switch (this.placetitle) {
32871                 case 'center' :
32872                     cls += ' masonry-center-title';
32873                     break;
32874                 case 'bottom' :
32875                     cls += ' masonry-bottom-title';
32876                     break;
32877                 default:
32878                     break;
32879             }
32880             
32881         } else {
32882             if(!this.html.length && !this.bgimage.length){
32883                 cls += ' masonry-center-title';
32884             }
32885
32886             if(!this.html.length && this.bgimage.length){
32887                 cls += ' masonry-bottom-title';
32888             }
32889         }
32890         
32891         if(this.cls){
32892             cls += ' ' + this.cls;
32893         }
32894         
32895         var cfg = {
32896             tag: (this.href.length) ? 'a' : 'div',
32897             cls: cls,
32898             cn: [
32899                 {
32900                     tag: 'div',
32901                     cls: 'masonry-brick-mask'
32902                 },
32903                 {
32904                     tag: 'div',
32905                     cls: 'masonry-brick-paragraph',
32906                     cn: []
32907                 }
32908             ]
32909         };
32910         
32911         if(this.href.length){
32912             cfg.href = this.href;
32913         }
32914         
32915         var cn = cfg.cn[1].cn;
32916         
32917         if(this.title.length){
32918             cn.push({
32919                 tag: 'h4',
32920                 cls: 'masonry-brick-title',
32921                 html: this.title
32922             });
32923         }
32924         
32925         if(this.html.length){
32926             cn.push({
32927                 tag: 'p',
32928                 cls: 'masonry-brick-text',
32929                 html: this.html
32930             });
32931         }
32932         
32933         if (!this.title.length && !this.html.length) {
32934             cfg.cn[1].cls += ' hide';
32935         }
32936         
32937         if(this.bgimage.length){
32938             cfg.cn.push({
32939                 tag: 'img',
32940                 cls: 'masonry-brick-image-view',
32941                 src: this.bgimage
32942             });
32943         }
32944         
32945         if(this.videourl.length){
32946             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32947             // youtube support only?
32948             cfg.cn.push({
32949                 tag: 'iframe',
32950                 cls: 'masonry-brick-image-view',
32951                 src: vurl,
32952                 frameborder : 0,
32953                 allowfullscreen : true
32954             });
32955         }
32956         
32957         return cfg;
32958         
32959     },
32960     
32961     getSplitAutoCreate : function()
32962     {
32963         var cls = 'masonry-brick masonry-brick-split';
32964         
32965         if(this.href.length){
32966             cls += ' masonry-brick-link';
32967         }
32968         
32969         if(this.bgimage.length){
32970             cls += ' masonry-brick-image';
32971         }
32972         
32973         if(this.size){
32974             cls += ' masonry-' + this.size + '-brick';
32975         }
32976         
32977         switch (this.placetitle) {
32978             case 'center' :
32979                 cls += ' masonry-center-title';
32980                 break;
32981             case 'bottom' :
32982                 cls += ' masonry-bottom-title';
32983                 break;
32984             default:
32985                 if(!this.bgimage.length){
32986                     cls += ' masonry-center-title';
32987                 }
32988
32989                 if(this.bgimage.length){
32990                     cls += ' masonry-bottom-title';
32991                 }
32992                 break;
32993         }
32994         
32995         if(this.cls){
32996             cls += ' ' + this.cls;
32997         }
32998         
32999         var cfg = {
33000             tag: (this.href.length) ? 'a' : 'div',
33001             cls: cls,
33002             cn: [
33003                 {
33004                     tag: 'div',
33005                     cls: 'masonry-brick-split-head',
33006                     cn: [
33007                         {
33008                             tag: 'div',
33009                             cls: 'masonry-brick-paragraph',
33010                             cn: []
33011                         }
33012                     ]
33013                 },
33014                 {
33015                     tag: 'div',
33016                     cls: 'masonry-brick-split-body',
33017                     cn: []
33018                 }
33019             ]
33020         };
33021         
33022         if(this.href.length){
33023             cfg.href = this.href;
33024         }
33025         
33026         if(this.title.length){
33027             cfg.cn[0].cn[0].cn.push({
33028                 tag: 'h4',
33029                 cls: 'masonry-brick-title',
33030                 html: this.title
33031             });
33032         }
33033         
33034         if(this.html.length){
33035             cfg.cn[1].cn.push({
33036                 tag: 'p',
33037                 cls: 'masonry-brick-text',
33038                 html: this.html
33039             });
33040         }
33041
33042         if(this.bgimage.length){
33043             cfg.cn[0].cn.push({
33044                 tag: 'img',
33045                 cls: 'masonry-brick-image-view',
33046                 src: this.bgimage
33047             });
33048         }
33049         
33050         if(this.videourl.length){
33051             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33052             // youtube support only?
33053             cfg.cn[0].cn.cn.push({
33054                 tag: 'iframe',
33055                 cls: 'masonry-brick-image-view',
33056                 src: vurl,
33057                 frameborder : 0,
33058                 allowfullscreen : true
33059             });
33060         }
33061         
33062         return cfg;
33063     },
33064     
33065     initEvents: function() 
33066     {
33067         switch (this.size) {
33068             case 'xs' :
33069                 this.x = 1;
33070                 this.y = 1;
33071                 break;
33072             case 'sm' :
33073                 this.x = 2;
33074                 this.y = 2;
33075                 break;
33076             case 'md' :
33077             case 'md-left' :
33078             case 'md-right' :
33079                 this.x = 3;
33080                 this.y = 3;
33081                 break;
33082             case 'tall' :
33083                 this.x = 2;
33084                 this.y = 3;
33085                 break;
33086             case 'wide' :
33087                 this.x = 3;
33088                 this.y = 2;
33089                 break;
33090             case 'wide-thin' :
33091                 this.x = 3;
33092                 this.y = 1;
33093                 break;
33094                         
33095             default :
33096                 break;
33097         }
33098         
33099         if(Roo.isTouch){
33100             this.el.on('touchstart', this.onTouchStart, this);
33101             this.el.on('touchmove', this.onTouchMove, this);
33102             this.el.on('touchend', this.onTouchEnd, this);
33103             this.el.on('contextmenu', this.onContextMenu, this);
33104         } else {
33105             this.el.on('mouseenter'  ,this.enter, this);
33106             this.el.on('mouseleave', this.leave, this);
33107             this.el.on('click', this.onClick, this);
33108         }
33109         
33110         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33111             this.parent().bricks.push(this);   
33112         }
33113         
33114     },
33115     
33116     onClick: function(e, el)
33117     {
33118         var time = this.endTimer - this.startTimer;
33119         // Roo.log(e.preventDefault());
33120         if(Roo.isTouch){
33121             if(time > 1000){
33122                 e.preventDefault();
33123                 return;
33124             }
33125         }
33126         
33127         if(!this.preventDefault){
33128             return;
33129         }
33130         
33131         e.preventDefault();
33132         
33133         if (this.activeClass != '') {
33134             this.selectBrick();
33135         }
33136         
33137         this.fireEvent('click', this, e);
33138     },
33139     
33140     enter: function(e, el)
33141     {
33142         e.preventDefault();
33143         
33144         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33145             return;
33146         }
33147         
33148         if(this.bgimage.length && this.html.length){
33149             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33150         }
33151     },
33152     
33153     leave: function(e, el)
33154     {
33155         e.preventDefault();
33156         
33157         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33158             return;
33159         }
33160         
33161         if(this.bgimage.length && this.html.length){
33162             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33163         }
33164     },
33165     
33166     onTouchStart: function(e, el)
33167     {
33168 //        e.preventDefault();
33169         
33170         this.touchmoved = false;
33171         
33172         if(!this.isFitContainer){
33173             return;
33174         }
33175         
33176         if(!this.bgimage.length || !this.html.length){
33177             return;
33178         }
33179         
33180         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33181         
33182         this.timer = new Date().getTime();
33183         
33184     },
33185     
33186     onTouchMove: function(e, el)
33187     {
33188         this.touchmoved = true;
33189     },
33190     
33191     onContextMenu : function(e,el)
33192     {
33193         e.preventDefault();
33194         e.stopPropagation();
33195         return false;
33196     },
33197     
33198     onTouchEnd: function(e, el)
33199     {
33200 //        e.preventDefault();
33201         
33202         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33203         
33204             this.leave(e,el);
33205             
33206             return;
33207         }
33208         
33209         if(!this.bgimage.length || !this.html.length){
33210             
33211             if(this.href.length){
33212                 window.location.href = this.href;
33213             }
33214             
33215             return;
33216         }
33217         
33218         if(!this.isFitContainer){
33219             return;
33220         }
33221         
33222         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33223         
33224         window.location.href = this.href;
33225     },
33226     
33227     //selection on single brick only
33228     selectBrick : function() {
33229         
33230         if (!this.parentId) {
33231             return;
33232         }
33233         
33234         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33235         var index = m.selectedBrick.indexOf(this.id);
33236         
33237         if ( index > -1) {
33238             m.selectedBrick.splice(index,1);
33239             this.el.removeClass(this.activeClass);
33240             return;
33241         }
33242         
33243         for(var i = 0; i < m.selectedBrick.length; i++) {
33244             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33245             b.el.removeClass(b.activeClass);
33246         }
33247         
33248         m.selectedBrick = [];
33249         
33250         m.selectedBrick.push(this.id);
33251         this.el.addClass(this.activeClass);
33252         return;
33253     },
33254     
33255     isSelected : function(){
33256         return this.el.hasClass(this.activeClass);
33257         
33258     }
33259 });
33260
33261 Roo.apply(Roo.bootstrap.MasonryBrick, {
33262     
33263     //groups: {},
33264     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33265      /**
33266     * register a Masonry Brick
33267     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33268     */
33269     
33270     register : function(brick)
33271     {
33272         //this.groups[brick.id] = brick;
33273         this.groups.add(brick.id, brick);
33274     },
33275     /**
33276     * fetch a  masonry brick based on the masonry brick ID
33277     * @param {string} the masonry brick to add
33278     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33279     */
33280     
33281     get: function(brick_id) 
33282     {
33283         // if (typeof(this.groups[brick_id]) == 'undefined') {
33284         //     return false;
33285         // }
33286         // return this.groups[brick_id] ;
33287         
33288         if(this.groups.key(brick_id)) {
33289             return this.groups.key(brick_id);
33290         }
33291         
33292         return false;
33293     }
33294     
33295     
33296     
33297 });
33298
33299  /*
33300  * - LGPL
33301  *
33302  * element
33303  * 
33304  */
33305
33306 /**
33307  * @class Roo.bootstrap.Brick
33308  * @extends Roo.bootstrap.Component
33309  * Bootstrap Brick class
33310  * 
33311  * @constructor
33312  * Create a new Brick
33313  * @param {Object} config The config object
33314  */
33315
33316 Roo.bootstrap.Brick = function(config){
33317     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33318     
33319     this.addEvents({
33320         // raw events
33321         /**
33322          * @event click
33323          * When a Brick is click
33324          * @param {Roo.bootstrap.Brick} this
33325          * @param {Roo.EventObject} e
33326          */
33327         "click" : true
33328     });
33329 };
33330
33331 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33332     
33333     /**
33334      * @cfg {String} title
33335      */   
33336     title : '',
33337     /**
33338      * @cfg {String} html
33339      */   
33340     html : '',
33341     /**
33342      * @cfg {String} bgimage
33343      */   
33344     bgimage : '',
33345     /**
33346      * @cfg {String} cls
33347      */   
33348     cls : '',
33349     /**
33350      * @cfg {String} href
33351      */   
33352     href : '',
33353     /**
33354      * @cfg {String} video
33355      */   
33356     video : '',
33357     /**
33358      * @cfg {Boolean} square
33359      */   
33360     square : true,
33361     
33362     getAutoCreate : function()
33363     {
33364         var cls = 'roo-brick';
33365         
33366         if(this.href.length){
33367             cls += ' roo-brick-link';
33368         }
33369         
33370         if(this.bgimage.length){
33371             cls += ' roo-brick-image';
33372         }
33373         
33374         if(!this.html.length && !this.bgimage.length){
33375             cls += ' roo-brick-center-title';
33376         }
33377         
33378         if(!this.html.length && this.bgimage.length){
33379             cls += ' roo-brick-bottom-title';
33380         }
33381         
33382         if(this.cls){
33383             cls += ' ' + this.cls;
33384         }
33385         
33386         var cfg = {
33387             tag: (this.href.length) ? 'a' : 'div',
33388             cls: cls,
33389             cn: [
33390                 {
33391                     tag: 'div',
33392                     cls: 'roo-brick-paragraph',
33393                     cn: []
33394                 }
33395             ]
33396         };
33397         
33398         if(this.href.length){
33399             cfg.href = this.href;
33400         }
33401         
33402         var cn = cfg.cn[0].cn;
33403         
33404         if(this.title.length){
33405             cn.push({
33406                 tag: 'h4',
33407                 cls: 'roo-brick-title',
33408                 html: this.title
33409             });
33410         }
33411         
33412         if(this.html.length){
33413             cn.push({
33414                 tag: 'p',
33415                 cls: 'roo-brick-text',
33416                 html: this.html
33417             });
33418         } else {
33419             cn.cls += ' hide';
33420         }
33421         
33422         if(this.bgimage.length){
33423             cfg.cn.push({
33424                 tag: 'img',
33425                 cls: 'roo-brick-image-view',
33426                 src: this.bgimage
33427             });
33428         }
33429         
33430         return cfg;
33431     },
33432     
33433     initEvents: function() 
33434     {
33435         if(this.title.length || this.html.length){
33436             this.el.on('mouseenter'  ,this.enter, this);
33437             this.el.on('mouseleave', this.leave, this);
33438         }
33439         
33440         Roo.EventManager.onWindowResize(this.resize, this); 
33441         
33442         if(this.bgimage.length){
33443             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33444             this.imageEl.on('load', this.onImageLoad, this);
33445             return;
33446         }
33447         
33448         this.resize();
33449     },
33450     
33451     onImageLoad : function()
33452     {
33453         this.resize();
33454     },
33455     
33456     resize : function()
33457     {
33458         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33459         
33460         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33461         
33462         if(this.bgimage.length){
33463             var image = this.el.select('.roo-brick-image-view', true).first();
33464             
33465             image.setWidth(paragraph.getWidth());
33466             
33467             if(this.square){
33468                 image.setHeight(paragraph.getWidth());
33469             }
33470             
33471             this.el.setHeight(image.getHeight());
33472             paragraph.setHeight(image.getHeight());
33473             
33474         }
33475         
33476     },
33477     
33478     enter: function(e, el)
33479     {
33480         e.preventDefault();
33481         
33482         if(this.bgimage.length){
33483             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33484             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33485         }
33486     },
33487     
33488     leave: function(e, el)
33489     {
33490         e.preventDefault();
33491         
33492         if(this.bgimage.length){
33493             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33494             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33495         }
33496     }
33497     
33498 });
33499
33500  
33501
33502  /*
33503  * - LGPL
33504  *
33505  * Number field 
33506  */
33507
33508 /**
33509  * @class Roo.bootstrap.NumberField
33510  * @extends Roo.bootstrap.Input
33511  * Bootstrap NumberField class
33512  * 
33513  * 
33514  * 
33515  * 
33516  * @constructor
33517  * Create a new NumberField
33518  * @param {Object} config The config object
33519  */
33520
33521 Roo.bootstrap.NumberField = function(config){
33522     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33523 };
33524
33525 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33526     
33527     /**
33528      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33529      */
33530     allowDecimals : true,
33531     /**
33532      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33533      */
33534     decimalSeparator : ".",
33535     /**
33536      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33537      */
33538     decimalPrecision : 2,
33539     /**
33540      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33541      */
33542     allowNegative : true,
33543     
33544     /**
33545      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33546      */
33547     allowZero: true,
33548     /**
33549      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33550      */
33551     minValue : Number.NEGATIVE_INFINITY,
33552     /**
33553      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33554      */
33555     maxValue : Number.MAX_VALUE,
33556     /**
33557      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33558      */
33559     minText : "The minimum value for this field is {0}",
33560     /**
33561      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33562      */
33563     maxText : "The maximum value for this field is {0}",
33564     /**
33565      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33566      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33567      */
33568     nanText : "{0} is not a valid number",
33569     /**
33570      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33571      */
33572     thousandsDelimiter : false,
33573     /**
33574      * @cfg {String} valueAlign alignment of value
33575      */
33576     valueAlign : "left",
33577
33578     getAutoCreate : function()
33579     {
33580         var hiddenInput = {
33581             tag: 'input',
33582             type: 'hidden',
33583             id: Roo.id(),
33584             cls: 'hidden-number-input'
33585         };
33586         
33587         if (this.name) {
33588             hiddenInput.name = this.name;
33589         }
33590         
33591         this.name = '';
33592         
33593         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33594         
33595         this.name = hiddenInput.name;
33596         
33597         if(cfg.cn.length > 0) {
33598             cfg.cn.push(hiddenInput);
33599         }
33600         
33601         return cfg;
33602     },
33603
33604     // private
33605     initEvents : function()
33606     {   
33607         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33608         
33609         var allowed = "0123456789";
33610         
33611         if(this.allowDecimals){
33612             allowed += this.decimalSeparator;
33613         }
33614         
33615         if(this.allowNegative){
33616             allowed += "-";
33617         }
33618         
33619         if(this.thousandsDelimiter) {
33620             allowed += ",";
33621         }
33622         
33623         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33624         
33625         var keyPress = function(e){
33626             
33627             var k = e.getKey();
33628             
33629             var c = e.getCharCode();
33630             
33631             if(
33632                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33633                     allowed.indexOf(String.fromCharCode(c)) === -1
33634             ){
33635                 e.stopEvent();
33636                 return;
33637             }
33638             
33639             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33640                 return;
33641             }
33642             
33643             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33644                 e.stopEvent();
33645             }
33646         };
33647         
33648         this.el.on("keypress", keyPress, this);
33649     },
33650     
33651     validateValue : function(value)
33652     {
33653         
33654         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33655             return false;
33656         }
33657         
33658         var num = this.parseValue(value);
33659         
33660         if(isNaN(num)){
33661             this.markInvalid(String.format(this.nanText, value));
33662             return false;
33663         }
33664         
33665         if(num < this.minValue){
33666             this.markInvalid(String.format(this.minText, this.minValue));
33667             return false;
33668         }
33669         
33670         if(num > this.maxValue){
33671             this.markInvalid(String.format(this.maxText, this.maxValue));
33672             return false;
33673         }
33674         
33675         return true;
33676     },
33677
33678     getValue : function()
33679     {
33680         var v = this.hiddenEl().getValue();
33681         
33682         return this.fixPrecision(this.parseValue(v));
33683     },
33684
33685     parseValue : function(value)
33686     {
33687         if(this.thousandsDelimiter) {
33688             value += "";
33689             r = new RegExp(",", "g");
33690             value = value.replace(r, "");
33691         }
33692         
33693         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33694         return isNaN(value) ? '' : value;
33695     },
33696
33697     fixPrecision : function(value)
33698     {
33699         if(this.thousandsDelimiter) {
33700             value += "";
33701             r = new RegExp(",", "g");
33702             value = value.replace(r, "");
33703         }
33704         
33705         var nan = isNaN(value);
33706         
33707         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33708             return nan ? '' : value;
33709         }
33710         return parseFloat(value).toFixed(this.decimalPrecision);
33711     },
33712
33713     setValue : function(v)
33714     {
33715         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33716         
33717         this.value = v;
33718         
33719         if(this.rendered){
33720             
33721             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33722             
33723             this.inputEl().dom.value = (v == '') ? '' :
33724                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33725             
33726             if(!this.allowZero && v === '0') {
33727                 this.hiddenEl().dom.value = '';
33728                 this.inputEl().dom.value = '';
33729             }
33730             
33731             this.validate();
33732         }
33733     },
33734
33735     decimalPrecisionFcn : function(v)
33736     {
33737         return Math.floor(v);
33738     },
33739
33740     beforeBlur : function()
33741     {
33742         var v = this.parseValue(this.getRawValue());
33743         
33744         if(v || v === 0 || v === ''){
33745             this.setValue(v);
33746         }
33747     },
33748     
33749     hiddenEl : function()
33750     {
33751         return this.el.select('input.hidden-number-input',true).first();
33752     }
33753     
33754 });
33755
33756  
33757
33758 /*
33759 * Licence: LGPL
33760 */
33761
33762 /**
33763  * @class Roo.bootstrap.DocumentSlider
33764  * @extends Roo.bootstrap.Component
33765  * Bootstrap DocumentSlider class
33766  * 
33767  * @constructor
33768  * Create a new DocumentViewer
33769  * @param {Object} config The config object
33770  */
33771
33772 Roo.bootstrap.DocumentSlider = function(config){
33773     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33774     
33775     this.files = [];
33776     
33777     this.addEvents({
33778         /**
33779          * @event initial
33780          * Fire after initEvent
33781          * @param {Roo.bootstrap.DocumentSlider} this
33782          */
33783         "initial" : true,
33784         /**
33785          * @event update
33786          * Fire after update
33787          * @param {Roo.bootstrap.DocumentSlider} this
33788          */
33789         "update" : true,
33790         /**
33791          * @event click
33792          * Fire after click
33793          * @param {Roo.bootstrap.DocumentSlider} this
33794          */
33795         "click" : true
33796     });
33797 };
33798
33799 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33800     
33801     files : false,
33802     
33803     indicator : 0,
33804     
33805     getAutoCreate : function()
33806     {
33807         var cfg = {
33808             tag : 'div',
33809             cls : 'roo-document-slider',
33810             cn : [
33811                 {
33812                     tag : 'div',
33813                     cls : 'roo-document-slider-header',
33814                     cn : [
33815                         {
33816                             tag : 'div',
33817                             cls : 'roo-document-slider-header-title'
33818                         }
33819                     ]
33820                 },
33821                 {
33822                     tag : 'div',
33823                     cls : 'roo-document-slider-body',
33824                     cn : [
33825                         {
33826                             tag : 'div',
33827                             cls : 'roo-document-slider-prev',
33828                             cn : [
33829                                 {
33830                                     tag : 'i',
33831                                     cls : 'fa fa-chevron-left'
33832                                 }
33833                             ]
33834                         },
33835                         {
33836                             tag : 'div',
33837                             cls : 'roo-document-slider-thumb',
33838                             cn : [
33839                                 {
33840                                     tag : 'img',
33841                                     cls : 'roo-document-slider-image'
33842                                 }
33843                             ]
33844                         },
33845                         {
33846                             tag : 'div',
33847                             cls : 'roo-document-slider-next',
33848                             cn : [
33849                                 {
33850                                     tag : 'i',
33851                                     cls : 'fa fa-chevron-right'
33852                                 }
33853                             ]
33854                         }
33855                     ]
33856                 }
33857             ]
33858         };
33859         
33860         return cfg;
33861     },
33862     
33863     initEvents : function()
33864     {
33865         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33866         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33867         
33868         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33869         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33870         
33871         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33872         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33873         
33874         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33875         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33876         
33877         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33878         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33879         
33880         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33881         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33882         
33883         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33884         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33885         
33886         this.thumbEl.on('click', this.onClick, this);
33887         
33888         this.prevIndicator.on('click', this.prev, this);
33889         
33890         this.nextIndicator.on('click', this.next, this);
33891         
33892     },
33893     
33894     initial : function()
33895     {
33896         if(this.files.length){
33897             this.indicator = 1;
33898             this.update()
33899         }
33900         
33901         this.fireEvent('initial', this);
33902     },
33903     
33904     update : function()
33905     {
33906         this.imageEl.attr('src', this.files[this.indicator - 1]);
33907         
33908         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33909         
33910         this.prevIndicator.show();
33911         
33912         if(this.indicator == 1){
33913             this.prevIndicator.hide();
33914         }
33915         
33916         this.nextIndicator.show();
33917         
33918         if(this.indicator == this.files.length){
33919             this.nextIndicator.hide();
33920         }
33921         
33922         this.thumbEl.scrollTo('top');
33923         
33924         this.fireEvent('update', this);
33925     },
33926     
33927     onClick : function(e)
33928     {
33929         e.preventDefault();
33930         
33931         this.fireEvent('click', this);
33932     },
33933     
33934     prev : function(e)
33935     {
33936         e.preventDefault();
33937         
33938         this.indicator = Math.max(1, this.indicator - 1);
33939         
33940         this.update();
33941     },
33942     
33943     next : function(e)
33944     {
33945         e.preventDefault();
33946         
33947         this.indicator = Math.min(this.files.length, this.indicator + 1);
33948         
33949         this.update();
33950     }
33951 });
33952 /*
33953  * - LGPL
33954  *
33955  * RadioSet
33956  *
33957  *
33958  */
33959
33960 /**
33961  * @class Roo.bootstrap.RadioSet
33962  * @extends Roo.bootstrap.Input
33963  * Bootstrap RadioSet class
33964  * @cfg {String} indicatorpos (left|right) default left
33965  * @cfg {Boolean} inline (true|false) inline the element (default true)
33966  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33967  * @constructor
33968  * Create a new RadioSet
33969  * @param {Object} config The config object
33970  */
33971
33972 Roo.bootstrap.RadioSet = function(config){
33973     
33974     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33975     
33976     this.radioes = [];
33977     
33978     Roo.bootstrap.RadioSet.register(this);
33979     
33980     this.addEvents({
33981         /**
33982         * @event check
33983         * Fires when the element is checked or unchecked.
33984         * @param {Roo.bootstrap.RadioSet} this This radio
33985         * @param {Roo.bootstrap.Radio} item The checked item
33986         */
33987        check : true,
33988        /**
33989         * @event click
33990         * Fires when the element is click.
33991         * @param {Roo.bootstrap.RadioSet} this This radio set
33992         * @param {Roo.bootstrap.Radio} item The checked item
33993         * @param {Roo.EventObject} e The event object
33994         */
33995        click : true
33996     });
33997     
33998 };
33999
34000 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34001
34002     radioes : false,
34003     
34004     inline : true,
34005     
34006     weight : '',
34007     
34008     indicatorpos : 'left',
34009     
34010     getAutoCreate : function()
34011     {
34012         var label = {
34013             tag : 'label',
34014             cls : 'roo-radio-set-label',
34015             cn : [
34016                 {
34017                     tag : 'span',
34018                     html : this.fieldLabel
34019                 }
34020             ]
34021         };
34022         
34023         if(this.indicatorpos == 'left'){
34024             label.cn.unshift({
34025                 tag : 'i',
34026                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34027                 tooltip : 'This field is required'
34028             });
34029         } else {
34030             label.cn.push({
34031                 tag : 'i',
34032                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34033                 tooltip : 'This field is required'
34034             });
34035         }
34036         
34037         var items = {
34038             tag : 'div',
34039             cls : 'roo-radio-set-items'
34040         };
34041         
34042         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34043         
34044         if (align === 'left' && this.fieldLabel.length) {
34045             
34046             items = {
34047                 cls : "roo-radio-set-right", 
34048                 cn: [
34049                     items
34050                 ]
34051             };
34052             
34053             if(this.labelWidth > 12){
34054                 label.style = "width: " + this.labelWidth + 'px';
34055             }
34056             
34057             if(this.labelWidth < 13 && this.labelmd == 0){
34058                 this.labelmd = this.labelWidth;
34059             }
34060             
34061             if(this.labellg > 0){
34062                 label.cls += ' col-lg-' + this.labellg;
34063                 items.cls += ' col-lg-' + (12 - this.labellg);
34064             }
34065             
34066             if(this.labelmd > 0){
34067                 label.cls += ' col-md-' + this.labelmd;
34068                 items.cls += ' col-md-' + (12 - this.labelmd);
34069             }
34070             
34071             if(this.labelsm > 0){
34072                 label.cls += ' col-sm-' + this.labelsm;
34073                 items.cls += ' col-sm-' + (12 - this.labelsm);
34074             }
34075             
34076             if(this.labelxs > 0){
34077                 label.cls += ' col-xs-' + this.labelxs;
34078                 items.cls += ' col-xs-' + (12 - this.labelxs);
34079             }
34080         }
34081         
34082         var cfg = {
34083             tag : 'div',
34084             cls : 'roo-radio-set',
34085             cn : [
34086                 {
34087                     tag : 'input',
34088                     cls : 'roo-radio-set-input',
34089                     type : 'hidden',
34090                     name : this.name,
34091                     value : this.value ? this.value :  ''
34092                 },
34093                 label,
34094                 items
34095             ]
34096         };
34097         
34098         if(this.weight.length){
34099             cfg.cls += ' roo-radio-' + this.weight;
34100         }
34101         
34102         if(this.inline) {
34103             cfg.cls += ' roo-radio-set-inline';
34104         }
34105         
34106         var settings=this;
34107         ['xs','sm','md','lg'].map(function(size){
34108             if (settings[size]) {
34109                 cfg.cls += ' col-' + size + '-' + settings[size];
34110             }
34111         });
34112         
34113         return cfg;
34114         
34115     },
34116
34117     initEvents : function()
34118     {
34119         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34120         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34121         
34122         if(!this.fieldLabel.length){
34123             this.labelEl.hide();
34124         }
34125         
34126         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34127         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34128         
34129         this.indicator = this.indicatorEl();
34130         
34131         if(this.indicator){
34132             this.indicator.addClass('invisible');
34133         }
34134         
34135         this.originalValue = this.getValue();
34136         
34137     },
34138     
34139     inputEl: function ()
34140     {
34141         return this.el.select('.roo-radio-set-input', true).first();
34142     },
34143     
34144     getChildContainer : function()
34145     {
34146         return this.itemsEl;
34147     },
34148     
34149     register : function(item)
34150     {
34151         this.radioes.push(item);
34152         
34153     },
34154     
34155     validate : function()
34156     {   
34157         if(this.getVisibilityEl().hasClass('hidden')){
34158             return true;
34159         }
34160         
34161         var valid = false;
34162         
34163         Roo.each(this.radioes, function(i){
34164             if(!i.checked){
34165                 return;
34166             }
34167             
34168             valid = true;
34169             return false;
34170         });
34171         
34172         if(this.allowBlank) {
34173             return true;
34174         }
34175         
34176         if(this.disabled || valid){
34177             this.markValid();
34178             return true;
34179         }
34180         
34181         this.markInvalid();
34182         return false;
34183         
34184     },
34185     
34186     markValid : function()
34187     {
34188         if(this.labelEl.isVisible(true)){
34189             this.indicatorEl().removeClass('visible');
34190             this.indicatorEl().addClass('invisible');
34191         }
34192         
34193         this.el.removeClass([this.invalidClass, this.validClass]);
34194         this.el.addClass(this.validClass);
34195         
34196         this.fireEvent('valid', this);
34197     },
34198     
34199     markInvalid : function(msg)
34200     {
34201         if(this.allowBlank || this.disabled){
34202             return;
34203         }
34204         
34205         if(this.labelEl.isVisible(true)){
34206             this.indicatorEl().removeClass('invisible');
34207             this.indicatorEl().addClass('visible');
34208         }
34209         
34210         this.el.removeClass([this.invalidClass, this.validClass]);
34211         this.el.addClass(this.invalidClass);
34212         
34213         this.fireEvent('invalid', this, msg);
34214         
34215     },
34216     
34217     setValue : function(v, suppressEvent)
34218     {   
34219         if(this.value === v){
34220             return;
34221         }
34222         
34223         this.value = v;
34224         
34225         if(this.rendered){
34226             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34227         }
34228         
34229         Roo.each(this.radioes, function(i){
34230             i.checked = false;
34231             i.el.removeClass('checked');
34232         });
34233         
34234         Roo.each(this.radioes, function(i){
34235             
34236             if(i.value === v || i.value.toString() === v.toString()){
34237                 i.checked = true;
34238                 i.el.addClass('checked');
34239                 
34240                 if(suppressEvent !== true){
34241                     this.fireEvent('check', this, i);
34242                 }
34243                 
34244                 return false;
34245             }
34246             
34247         }, this);
34248         
34249         this.validate();
34250     },
34251     
34252     clearInvalid : function(){
34253         
34254         if(!this.el || this.preventMark){
34255             return;
34256         }
34257         
34258         this.el.removeClass([this.invalidClass]);
34259         
34260         this.fireEvent('valid', this);
34261     }
34262     
34263 });
34264
34265 Roo.apply(Roo.bootstrap.RadioSet, {
34266     
34267     groups: {},
34268     
34269     register : function(set)
34270     {
34271         this.groups[set.name] = set;
34272     },
34273     
34274     get: function(name) 
34275     {
34276         if (typeof(this.groups[name]) == 'undefined') {
34277             return false;
34278         }
34279         
34280         return this.groups[name] ;
34281     }
34282     
34283 });
34284 /*
34285  * Based on:
34286  * Ext JS Library 1.1.1
34287  * Copyright(c) 2006-2007, Ext JS, LLC.
34288  *
34289  * Originally Released Under LGPL - original licence link has changed is not relivant.
34290  *
34291  * Fork - LGPL
34292  * <script type="text/javascript">
34293  */
34294
34295
34296 /**
34297  * @class Roo.bootstrap.SplitBar
34298  * @extends Roo.util.Observable
34299  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34300  * <br><br>
34301  * Usage:
34302  * <pre><code>
34303 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34304                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34305 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34306 split.minSize = 100;
34307 split.maxSize = 600;
34308 split.animate = true;
34309 split.on('moved', splitterMoved);
34310 </code></pre>
34311  * @constructor
34312  * Create a new SplitBar
34313  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34314  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34315  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34316  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34317                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34318                         position of the SplitBar).
34319  */
34320 Roo.bootstrap.SplitBar = function(cfg){
34321     
34322     /** @private */
34323     
34324     //{
34325     //  dragElement : elm
34326     //  resizingElement: el,
34327         // optional..
34328     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34329     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34330         // existingProxy ???
34331     //}
34332     
34333     this.el = Roo.get(cfg.dragElement, true);
34334     this.el.dom.unselectable = "on";
34335     /** @private */
34336     this.resizingEl = Roo.get(cfg.resizingElement, true);
34337
34338     /**
34339      * @private
34340      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34341      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34342      * @type Number
34343      */
34344     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34345     
34346     /**
34347      * The minimum size of the resizing element. (Defaults to 0)
34348      * @type Number
34349      */
34350     this.minSize = 0;
34351     
34352     /**
34353      * The maximum size of the resizing element. (Defaults to 2000)
34354      * @type Number
34355      */
34356     this.maxSize = 2000;
34357     
34358     /**
34359      * Whether to animate the transition to the new size
34360      * @type Boolean
34361      */
34362     this.animate = false;
34363     
34364     /**
34365      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34366      * @type Boolean
34367      */
34368     this.useShim = false;
34369     
34370     /** @private */
34371     this.shim = null;
34372     
34373     if(!cfg.existingProxy){
34374         /** @private */
34375         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34376     }else{
34377         this.proxy = Roo.get(cfg.existingProxy).dom;
34378     }
34379     /** @private */
34380     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34381     
34382     /** @private */
34383     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34384     
34385     /** @private */
34386     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34387     
34388     /** @private */
34389     this.dragSpecs = {};
34390     
34391     /**
34392      * @private The adapter to use to positon and resize elements
34393      */
34394     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34395     this.adapter.init(this);
34396     
34397     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34398         /** @private */
34399         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34400         this.el.addClass("roo-splitbar-h");
34401     }else{
34402         /** @private */
34403         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34404         this.el.addClass("roo-splitbar-v");
34405     }
34406     
34407     this.addEvents({
34408         /**
34409          * @event resize
34410          * Fires when the splitter is moved (alias for {@link #event-moved})
34411          * @param {Roo.bootstrap.SplitBar} this
34412          * @param {Number} newSize the new width or height
34413          */
34414         "resize" : true,
34415         /**
34416          * @event moved
34417          * Fires when the splitter is moved
34418          * @param {Roo.bootstrap.SplitBar} this
34419          * @param {Number} newSize the new width or height
34420          */
34421         "moved" : true,
34422         /**
34423          * @event beforeresize
34424          * Fires before the splitter is dragged
34425          * @param {Roo.bootstrap.SplitBar} this
34426          */
34427         "beforeresize" : true,
34428
34429         "beforeapply" : true
34430     });
34431
34432     Roo.util.Observable.call(this);
34433 };
34434
34435 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34436     onStartProxyDrag : function(x, y){
34437         this.fireEvent("beforeresize", this);
34438         if(!this.overlay){
34439             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34440             o.unselectable();
34441             o.enableDisplayMode("block");
34442             // all splitbars share the same overlay
34443             Roo.bootstrap.SplitBar.prototype.overlay = o;
34444         }
34445         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34446         this.overlay.show();
34447         Roo.get(this.proxy).setDisplayed("block");
34448         var size = this.adapter.getElementSize(this);
34449         this.activeMinSize = this.getMinimumSize();;
34450         this.activeMaxSize = this.getMaximumSize();;
34451         var c1 = size - this.activeMinSize;
34452         var c2 = Math.max(this.activeMaxSize - size, 0);
34453         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34454             this.dd.resetConstraints();
34455             this.dd.setXConstraint(
34456                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34457                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34458             );
34459             this.dd.setYConstraint(0, 0);
34460         }else{
34461             this.dd.resetConstraints();
34462             this.dd.setXConstraint(0, 0);
34463             this.dd.setYConstraint(
34464                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34465                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34466             );
34467          }
34468         this.dragSpecs.startSize = size;
34469         this.dragSpecs.startPoint = [x, y];
34470         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34471     },
34472     
34473     /** 
34474      * @private Called after the drag operation by the DDProxy
34475      */
34476     onEndProxyDrag : function(e){
34477         Roo.get(this.proxy).setDisplayed(false);
34478         var endPoint = Roo.lib.Event.getXY(e);
34479         if(this.overlay){
34480             this.overlay.hide();
34481         }
34482         var newSize;
34483         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34484             newSize = this.dragSpecs.startSize + 
34485                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34486                     endPoint[0] - this.dragSpecs.startPoint[0] :
34487                     this.dragSpecs.startPoint[0] - endPoint[0]
34488                 );
34489         }else{
34490             newSize = this.dragSpecs.startSize + 
34491                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34492                     endPoint[1] - this.dragSpecs.startPoint[1] :
34493                     this.dragSpecs.startPoint[1] - endPoint[1]
34494                 );
34495         }
34496         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34497         if(newSize != this.dragSpecs.startSize){
34498             if(this.fireEvent('beforeapply', this, newSize) !== false){
34499                 this.adapter.setElementSize(this, newSize);
34500                 this.fireEvent("moved", this, newSize);
34501                 this.fireEvent("resize", this, newSize);
34502             }
34503         }
34504     },
34505     
34506     /**
34507      * Get the adapter this SplitBar uses
34508      * @return The adapter object
34509      */
34510     getAdapter : function(){
34511         return this.adapter;
34512     },
34513     
34514     /**
34515      * Set the adapter this SplitBar uses
34516      * @param {Object} adapter A SplitBar adapter object
34517      */
34518     setAdapter : function(adapter){
34519         this.adapter = adapter;
34520         this.adapter.init(this);
34521     },
34522     
34523     /**
34524      * Gets the minimum size for the resizing element
34525      * @return {Number} The minimum size
34526      */
34527     getMinimumSize : function(){
34528         return this.minSize;
34529     },
34530     
34531     /**
34532      * Sets the minimum size for the resizing element
34533      * @param {Number} minSize The minimum size
34534      */
34535     setMinimumSize : function(minSize){
34536         this.minSize = minSize;
34537     },
34538     
34539     /**
34540      * Gets the maximum size for the resizing element
34541      * @return {Number} The maximum size
34542      */
34543     getMaximumSize : function(){
34544         return this.maxSize;
34545     },
34546     
34547     /**
34548      * Sets the maximum size for the resizing element
34549      * @param {Number} maxSize The maximum size
34550      */
34551     setMaximumSize : function(maxSize){
34552         this.maxSize = maxSize;
34553     },
34554     
34555     /**
34556      * Sets the initialize size for the resizing element
34557      * @param {Number} size The initial size
34558      */
34559     setCurrentSize : function(size){
34560         var oldAnimate = this.animate;
34561         this.animate = false;
34562         this.adapter.setElementSize(this, size);
34563         this.animate = oldAnimate;
34564     },
34565     
34566     /**
34567      * Destroy this splitbar. 
34568      * @param {Boolean} removeEl True to remove the element
34569      */
34570     destroy : function(removeEl){
34571         if(this.shim){
34572             this.shim.remove();
34573         }
34574         this.dd.unreg();
34575         this.proxy.parentNode.removeChild(this.proxy);
34576         if(removeEl){
34577             this.el.remove();
34578         }
34579     }
34580 });
34581
34582 /**
34583  * @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.
34584  */
34585 Roo.bootstrap.SplitBar.createProxy = function(dir){
34586     var proxy = new Roo.Element(document.createElement("div"));
34587     proxy.unselectable();
34588     var cls = 'roo-splitbar-proxy';
34589     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34590     document.body.appendChild(proxy.dom);
34591     return proxy.dom;
34592 };
34593
34594 /** 
34595  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34596  * Default Adapter. It assumes the splitter and resizing element are not positioned
34597  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34598  */
34599 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34600 };
34601
34602 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34603     // do nothing for now
34604     init : function(s){
34605     
34606     },
34607     /**
34608      * Called before drag operations to get the current size of the resizing element. 
34609      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34610      */
34611      getElementSize : function(s){
34612         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34613             return s.resizingEl.getWidth();
34614         }else{
34615             return s.resizingEl.getHeight();
34616         }
34617     },
34618     
34619     /**
34620      * Called after drag operations to set the size of the resizing element.
34621      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34622      * @param {Number} newSize The new size to set
34623      * @param {Function} onComplete A function to be invoked when resizing is complete
34624      */
34625     setElementSize : function(s, newSize, onComplete){
34626         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34627             if(!s.animate){
34628                 s.resizingEl.setWidth(newSize);
34629                 if(onComplete){
34630                     onComplete(s, newSize);
34631                 }
34632             }else{
34633                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34634             }
34635         }else{
34636             
34637             if(!s.animate){
34638                 s.resizingEl.setHeight(newSize);
34639                 if(onComplete){
34640                     onComplete(s, newSize);
34641                 }
34642             }else{
34643                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34644             }
34645         }
34646     }
34647 };
34648
34649 /** 
34650  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34651  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34652  * Adapter that  moves the splitter element to align with the resized sizing element. 
34653  * Used with an absolute positioned SplitBar.
34654  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34655  * document.body, make sure you assign an id to the body element.
34656  */
34657 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34658     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34659     this.container = Roo.get(container);
34660 };
34661
34662 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34663     init : function(s){
34664         this.basic.init(s);
34665     },
34666     
34667     getElementSize : function(s){
34668         return this.basic.getElementSize(s);
34669     },
34670     
34671     setElementSize : function(s, newSize, onComplete){
34672         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34673     },
34674     
34675     moveSplitter : function(s){
34676         var yes = Roo.bootstrap.SplitBar;
34677         switch(s.placement){
34678             case yes.LEFT:
34679                 s.el.setX(s.resizingEl.getRight());
34680                 break;
34681             case yes.RIGHT:
34682                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34683                 break;
34684             case yes.TOP:
34685                 s.el.setY(s.resizingEl.getBottom());
34686                 break;
34687             case yes.BOTTOM:
34688                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34689                 break;
34690         }
34691     }
34692 };
34693
34694 /**
34695  * Orientation constant - Create a vertical SplitBar
34696  * @static
34697  * @type Number
34698  */
34699 Roo.bootstrap.SplitBar.VERTICAL = 1;
34700
34701 /**
34702  * Orientation constant - Create a horizontal SplitBar
34703  * @static
34704  * @type Number
34705  */
34706 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34707
34708 /**
34709  * Placement constant - The resizing element is to the left of the splitter element
34710  * @static
34711  * @type Number
34712  */
34713 Roo.bootstrap.SplitBar.LEFT = 1;
34714
34715 /**
34716  * Placement constant - The resizing element is to the right of the splitter element
34717  * @static
34718  * @type Number
34719  */
34720 Roo.bootstrap.SplitBar.RIGHT = 2;
34721
34722 /**
34723  * Placement constant - The resizing element is positioned above the splitter element
34724  * @static
34725  * @type Number
34726  */
34727 Roo.bootstrap.SplitBar.TOP = 3;
34728
34729 /**
34730  * Placement constant - The resizing element is positioned under splitter element
34731  * @static
34732  * @type Number
34733  */
34734 Roo.bootstrap.SplitBar.BOTTOM = 4;
34735 Roo.namespace("Roo.bootstrap.layout");/*
34736  * Based on:
34737  * Ext JS Library 1.1.1
34738  * Copyright(c) 2006-2007, Ext JS, LLC.
34739  *
34740  * Originally Released Under LGPL - original licence link has changed is not relivant.
34741  *
34742  * Fork - LGPL
34743  * <script type="text/javascript">
34744  */
34745
34746 /**
34747  * @class Roo.bootstrap.layout.Manager
34748  * @extends Roo.bootstrap.Component
34749  * Base class for layout managers.
34750  */
34751 Roo.bootstrap.layout.Manager = function(config)
34752 {
34753     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34754
34755
34756
34757
34758
34759     /** false to disable window resize monitoring @type Boolean */
34760     this.monitorWindowResize = true;
34761     this.regions = {};
34762     this.addEvents({
34763         /**
34764          * @event layout
34765          * Fires when a layout is performed.
34766          * @param {Roo.LayoutManager} this
34767          */
34768         "layout" : true,
34769         /**
34770          * @event regionresized
34771          * Fires when the user resizes a region.
34772          * @param {Roo.LayoutRegion} region The resized region
34773          * @param {Number} newSize The new size (width for east/west, height for north/south)
34774          */
34775         "regionresized" : true,
34776         /**
34777          * @event regioncollapsed
34778          * Fires when a region is collapsed.
34779          * @param {Roo.LayoutRegion} region The collapsed region
34780          */
34781         "regioncollapsed" : true,
34782         /**
34783          * @event regionexpanded
34784          * Fires when a region is expanded.
34785          * @param {Roo.LayoutRegion} region The expanded region
34786          */
34787         "regionexpanded" : true
34788     });
34789     this.updating = false;
34790
34791     if (config.el) {
34792         this.el = Roo.get(config.el);
34793         this.initEvents();
34794     }
34795
34796 };
34797
34798 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34799
34800
34801     regions : null,
34802
34803     monitorWindowResize : true,
34804
34805
34806     updating : false,
34807
34808
34809     onRender : function(ct, position)
34810     {
34811         if(!this.el){
34812             this.el = Roo.get(ct);
34813             this.initEvents();
34814         }
34815         //this.fireEvent('render',this);
34816     },
34817
34818
34819     initEvents: function()
34820     {
34821
34822
34823         // ie scrollbar fix
34824         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34825             document.body.scroll = "no";
34826         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34827             this.el.position('relative');
34828         }
34829         this.id = this.el.id;
34830         this.el.addClass("roo-layout-container");
34831         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34832         if(this.el.dom != document.body ) {
34833             this.el.on('resize', this.layout,this);
34834             this.el.on('show', this.layout,this);
34835         }
34836
34837     },
34838
34839     /**
34840      * Returns true if this layout is currently being updated
34841      * @return {Boolean}
34842      */
34843     isUpdating : function(){
34844         return this.updating;
34845     },
34846
34847     /**
34848      * Suspend the LayoutManager from doing auto-layouts while
34849      * making multiple add or remove calls
34850      */
34851     beginUpdate : function(){
34852         this.updating = true;
34853     },
34854
34855     /**
34856      * Restore auto-layouts and optionally disable the manager from performing a layout
34857      * @param {Boolean} noLayout true to disable a layout update
34858      */
34859     endUpdate : function(noLayout){
34860         this.updating = false;
34861         if(!noLayout){
34862             this.layout();
34863         }
34864     },
34865
34866     layout: function(){
34867         // abstract...
34868     },
34869
34870     onRegionResized : function(region, newSize){
34871         this.fireEvent("regionresized", region, newSize);
34872         this.layout();
34873     },
34874
34875     onRegionCollapsed : function(region){
34876         this.fireEvent("regioncollapsed", region);
34877     },
34878
34879     onRegionExpanded : function(region){
34880         this.fireEvent("regionexpanded", region);
34881     },
34882
34883     /**
34884      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34885      * performs box-model adjustments.
34886      * @return {Object} The size as an object {width: (the width), height: (the height)}
34887      */
34888     getViewSize : function()
34889     {
34890         var size;
34891         if(this.el.dom != document.body){
34892             size = this.el.getSize();
34893         }else{
34894             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34895         }
34896         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34897         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34898         return size;
34899     },
34900
34901     /**
34902      * Returns the Element this layout is bound to.
34903      * @return {Roo.Element}
34904      */
34905     getEl : function(){
34906         return this.el;
34907     },
34908
34909     /**
34910      * Returns the specified region.
34911      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34912      * @return {Roo.LayoutRegion}
34913      */
34914     getRegion : function(target){
34915         return this.regions[target.toLowerCase()];
34916     },
34917
34918     onWindowResize : function(){
34919         if(this.monitorWindowResize){
34920             this.layout();
34921         }
34922     }
34923 });
34924 /*
34925  * Based on:
34926  * Ext JS Library 1.1.1
34927  * Copyright(c) 2006-2007, Ext JS, LLC.
34928  *
34929  * Originally Released Under LGPL - original licence link has changed is not relivant.
34930  *
34931  * Fork - LGPL
34932  * <script type="text/javascript">
34933  */
34934 /**
34935  * @class Roo.bootstrap.layout.Border
34936  * @extends Roo.bootstrap.layout.Manager
34937  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34938  * please see: examples/bootstrap/nested.html<br><br>
34939  
34940 <b>The container the layout is rendered into can be either the body element or any other element.
34941 If it is not the body element, the container needs to either be an absolute positioned element,
34942 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34943 the container size if it is not the body element.</b>
34944
34945 * @constructor
34946 * Create a new Border
34947 * @param {Object} config Configuration options
34948  */
34949 Roo.bootstrap.layout.Border = function(config){
34950     config = config || {};
34951     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34952     
34953     
34954     
34955     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34956         if(config[region]){
34957             config[region].region = region;
34958             this.addRegion(config[region]);
34959         }
34960     },this);
34961     
34962 };
34963
34964 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34965
34966 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34967     /**
34968      * Creates and adds a new region if it doesn't already exist.
34969      * @param {String} target The target region key (north, south, east, west or center).
34970      * @param {Object} config The regions config object
34971      * @return {BorderLayoutRegion} The new region
34972      */
34973     addRegion : function(config)
34974     {
34975         if(!this.regions[config.region]){
34976             var r = this.factory(config);
34977             this.bindRegion(r);
34978         }
34979         return this.regions[config.region];
34980     },
34981
34982     // private (kinda)
34983     bindRegion : function(r){
34984         this.regions[r.config.region] = r;
34985         
34986         r.on("visibilitychange",    this.layout, this);
34987         r.on("paneladded",          this.layout, this);
34988         r.on("panelremoved",        this.layout, this);
34989         r.on("invalidated",         this.layout, this);
34990         r.on("resized",             this.onRegionResized, this);
34991         r.on("collapsed",           this.onRegionCollapsed, this);
34992         r.on("expanded",            this.onRegionExpanded, this);
34993     },
34994
34995     /**
34996      * Performs a layout update.
34997      */
34998     layout : function()
34999     {
35000         if(this.updating) {
35001             return;
35002         }
35003         
35004         // render all the rebions if they have not been done alreayd?
35005         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35006             if(this.regions[region] && !this.regions[region].bodyEl){
35007                 this.regions[region].onRender(this.el)
35008             }
35009         },this);
35010         
35011         var size = this.getViewSize();
35012         var w = size.width;
35013         var h = size.height;
35014         var centerW = w;
35015         var centerH = h;
35016         var centerY = 0;
35017         var centerX = 0;
35018         //var x = 0, y = 0;
35019
35020         var rs = this.regions;
35021         var north = rs["north"];
35022         var south = rs["south"]; 
35023         var west = rs["west"];
35024         var east = rs["east"];
35025         var center = rs["center"];
35026         //if(this.hideOnLayout){ // not supported anymore
35027             //c.el.setStyle("display", "none");
35028         //}
35029         if(north && north.isVisible()){
35030             var b = north.getBox();
35031             var m = north.getMargins();
35032             b.width = w - (m.left+m.right);
35033             b.x = m.left;
35034             b.y = m.top;
35035             centerY = b.height + b.y + m.bottom;
35036             centerH -= centerY;
35037             north.updateBox(this.safeBox(b));
35038         }
35039         if(south && south.isVisible()){
35040             var b = south.getBox();
35041             var m = south.getMargins();
35042             b.width = w - (m.left+m.right);
35043             b.x = m.left;
35044             var totalHeight = (b.height + m.top + m.bottom);
35045             b.y = h - totalHeight + m.top;
35046             centerH -= totalHeight;
35047             south.updateBox(this.safeBox(b));
35048         }
35049         if(west && west.isVisible()){
35050             var b = west.getBox();
35051             var m = west.getMargins();
35052             b.height = centerH - (m.top+m.bottom);
35053             b.x = m.left;
35054             b.y = centerY + m.top;
35055             var totalWidth = (b.width + m.left + m.right);
35056             centerX += totalWidth;
35057             centerW -= totalWidth;
35058             west.updateBox(this.safeBox(b));
35059         }
35060         if(east && east.isVisible()){
35061             var b = east.getBox();
35062             var m = east.getMargins();
35063             b.height = centerH - (m.top+m.bottom);
35064             var totalWidth = (b.width + m.left + m.right);
35065             b.x = w - totalWidth + m.left;
35066             b.y = centerY + m.top;
35067             centerW -= totalWidth;
35068             east.updateBox(this.safeBox(b));
35069         }
35070         if(center){
35071             var m = center.getMargins();
35072             var centerBox = {
35073                 x: centerX + m.left,
35074                 y: centerY + m.top,
35075                 width: centerW - (m.left+m.right),
35076                 height: centerH - (m.top+m.bottom)
35077             };
35078             //if(this.hideOnLayout){
35079                 //center.el.setStyle("display", "block");
35080             //}
35081             center.updateBox(this.safeBox(centerBox));
35082         }
35083         this.el.repaint();
35084         this.fireEvent("layout", this);
35085     },
35086
35087     // private
35088     safeBox : function(box){
35089         box.width = Math.max(0, box.width);
35090         box.height = Math.max(0, box.height);
35091         return box;
35092     },
35093
35094     /**
35095      * Adds a ContentPanel (or subclass) to this layout.
35096      * @param {String} target The target region key (north, south, east, west or center).
35097      * @param {Roo.ContentPanel} panel The panel to add
35098      * @return {Roo.ContentPanel} The added panel
35099      */
35100     add : function(target, panel){
35101          
35102         target = target.toLowerCase();
35103         return this.regions[target].add(panel);
35104     },
35105
35106     /**
35107      * Remove a ContentPanel (or subclass) to this layout.
35108      * @param {String} target The target region key (north, south, east, west or center).
35109      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35110      * @return {Roo.ContentPanel} The removed panel
35111      */
35112     remove : function(target, panel){
35113         target = target.toLowerCase();
35114         return this.regions[target].remove(panel);
35115     },
35116
35117     /**
35118      * Searches all regions for a panel with the specified id
35119      * @param {String} panelId
35120      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35121      */
35122     findPanel : function(panelId){
35123         var rs = this.regions;
35124         for(var target in rs){
35125             if(typeof rs[target] != "function"){
35126                 var p = rs[target].getPanel(panelId);
35127                 if(p){
35128                     return p;
35129                 }
35130             }
35131         }
35132         return null;
35133     },
35134
35135     /**
35136      * Searches all regions for a panel with the specified id and activates (shows) it.
35137      * @param {String/ContentPanel} panelId The panels id or the panel itself
35138      * @return {Roo.ContentPanel} The shown panel or null
35139      */
35140     showPanel : function(panelId) {
35141       var rs = this.regions;
35142       for(var target in rs){
35143          var r = rs[target];
35144          if(typeof r != "function"){
35145             if(r.hasPanel(panelId)){
35146                return r.showPanel(panelId);
35147             }
35148          }
35149       }
35150       return null;
35151    },
35152
35153    /**
35154      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35155      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35156      */
35157    /*
35158     restoreState : function(provider){
35159         if(!provider){
35160             provider = Roo.state.Manager;
35161         }
35162         var sm = new Roo.LayoutStateManager();
35163         sm.init(this, provider);
35164     },
35165 */
35166  
35167  
35168     /**
35169      * Adds a xtype elements to the layout.
35170      * <pre><code>
35171
35172 layout.addxtype({
35173        xtype : 'ContentPanel',
35174        region: 'west',
35175        items: [ .... ]
35176    }
35177 );
35178
35179 layout.addxtype({
35180         xtype : 'NestedLayoutPanel',
35181         region: 'west',
35182         layout: {
35183            center: { },
35184            west: { }   
35185         },
35186         items : [ ... list of content panels or nested layout panels.. ]
35187    }
35188 );
35189 </code></pre>
35190      * @param {Object} cfg Xtype definition of item to add.
35191      */
35192     addxtype : function(cfg)
35193     {
35194         // basically accepts a pannel...
35195         // can accept a layout region..!?!?
35196         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35197         
35198         
35199         // theory?  children can only be panels??
35200         
35201         //if (!cfg.xtype.match(/Panel$/)) {
35202         //    return false;
35203         //}
35204         var ret = false;
35205         
35206         if (typeof(cfg.region) == 'undefined') {
35207             Roo.log("Failed to add Panel, region was not set");
35208             Roo.log(cfg);
35209             return false;
35210         }
35211         var region = cfg.region;
35212         delete cfg.region;
35213         
35214           
35215         var xitems = [];
35216         if (cfg.items) {
35217             xitems = cfg.items;
35218             delete cfg.items;
35219         }
35220         var nb = false;
35221         
35222         switch(cfg.xtype) 
35223         {
35224             case 'Content':  // ContentPanel (el, cfg)
35225             case 'Scroll':  // ContentPanel (el, cfg)
35226             case 'View': 
35227                 cfg.autoCreate = true;
35228                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35229                 //} else {
35230                 //    var el = this.el.createChild();
35231                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35232                 //}
35233                 
35234                 this.add(region, ret);
35235                 break;
35236             
35237             /*
35238             case 'TreePanel': // our new panel!
35239                 cfg.el = this.el.createChild();
35240                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35241                 this.add(region, ret);
35242                 break;
35243             */
35244             
35245             case 'Nest': 
35246                 // create a new Layout (which is  a Border Layout...
35247                 
35248                 var clayout = cfg.layout;
35249                 clayout.el  = this.el.createChild();
35250                 clayout.items   = clayout.items  || [];
35251                 
35252                 delete cfg.layout;
35253                 
35254                 // replace this exitems with the clayout ones..
35255                 xitems = clayout.items;
35256                  
35257                 // force background off if it's in center...
35258                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35259                     cfg.background = false;
35260                 }
35261                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35262                 
35263                 
35264                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35265                 //console.log('adding nested layout panel '  + cfg.toSource());
35266                 this.add(region, ret);
35267                 nb = {}; /// find first...
35268                 break;
35269             
35270             case 'Grid':
35271                 
35272                 // needs grid and region
35273                 
35274                 //var el = this.getRegion(region).el.createChild();
35275                 /*
35276                  *var el = this.el.createChild();
35277                 // create the grid first...
35278                 cfg.grid.container = el;
35279                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35280                 */
35281                 
35282                 if (region == 'center' && this.active ) {
35283                     cfg.background = false;
35284                 }
35285                 
35286                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35287                 
35288                 this.add(region, ret);
35289                 /*
35290                 if (cfg.background) {
35291                     // render grid on panel activation (if panel background)
35292                     ret.on('activate', function(gp) {
35293                         if (!gp.grid.rendered) {
35294                     //        gp.grid.render(el);
35295                         }
35296                     });
35297                 } else {
35298                   //  cfg.grid.render(el);
35299                 }
35300                 */
35301                 break;
35302            
35303            
35304             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35305                 // it was the old xcomponent building that caused this before.
35306                 // espeically if border is the top element in the tree.
35307                 ret = this;
35308                 break; 
35309                 
35310                     
35311                 
35312                 
35313                 
35314             default:
35315                 /*
35316                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35317                     
35318                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35319                     this.add(region, ret);
35320                 } else {
35321                 */
35322                     Roo.log(cfg);
35323                     throw "Can not add '" + cfg.xtype + "' to Border";
35324                     return null;
35325              
35326                                 
35327              
35328         }
35329         this.beginUpdate();
35330         // add children..
35331         var region = '';
35332         var abn = {};
35333         Roo.each(xitems, function(i)  {
35334             region = nb && i.region ? i.region : false;
35335             
35336             var add = ret.addxtype(i);
35337            
35338             if (region) {
35339                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35340                 if (!i.background) {
35341                     abn[region] = nb[region] ;
35342                 }
35343             }
35344             
35345         });
35346         this.endUpdate();
35347
35348         // make the last non-background panel active..
35349         //if (nb) { Roo.log(abn); }
35350         if (nb) {
35351             
35352             for(var r in abn) {
35353                 region = this.getRegion(r);
35354                 if (region) {
35355                     // tried using nb[r], but it does not work..
35356                      
35357                     region.showPanel(abn[r]);
35358                    
35359                 }
35360             }
35361         }
35362         return ret;
35363         
35364     },
35365     
35366     
35367 // private
35368     factory : function(cfg)
35369     {
35370         
35371         var validRegions = Roo.bootstrap.layout.Border.regions;
35372
35373         var target = cfg.region;
35374         cfg.mgr = this;
35375         
35376         var r = Roo.bootstrap.layout;
35377         Roo.log(target);
35378         switch(target){
35379             case "north":
35380                 return new r.North(cfg);
35381             case "south":
35382                 return new r.South(cfg);
35383             case "east":
35384                 return new r.East(cfg);
35385             case "west":
35386                 return new r.West(cfg);
35387             case "center":
35388                 return new r.Center(cfg);
35389         }
35390         throw 'Layout region "'+target+'" not supported.';
35391     }
35392     
35393     
35394 });
35395  /*
35396  * Based on:
35397  * Ext JS Library 1.1.1
35398  * Copyright(c) 2006-2007, Ext JS, LLC.
35399  *
35400  * Originally Released Under LGPL - original licence link has changed is not relivant.
35401  *
35402  * Fork - LGPL
35403  * <script type="text/javascript">
35404  */
35405  
35406 /**
35407  * @class Roo.bootstrap.layout.Basic
35408  * @extends Roo.util.Observable
35409  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35410  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35411  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35412  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35413  * @cfg {string}   region  the region that it inhabits..
35414  * @cfg {bool}   skipConfig skip config?
35415  * 
35416
35417  */
35418 Roo.bootstrap.layout.Basic = function(config){
35419     
35420     this.mgr = config.mgr;
35421     
35422     this.position = config.region;
35423     
35424     var skipConfig = config.skipConfig;
35425     
35426     this.events = {
35427         /**
35428          * @scope Roo.BasicLayoutRegion
35429          */
35430         
35431         /**
35432          * @event beforeremove
35433          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35434          * @param {Roo.LayoutRegion} this
35435          * @param {Roo.ContentPanel} panel The panel
35436          * @param {Object} e The cancel event object
35437          */
35438         "beforeremove" : true,
35439         /**
35440          * @event invalidated
35441          * Fires when the layout for this region is changed.
35442          * @param {Roo.LayoutRegion} this
35443          */
35444         "invalidated" : true,
35445         /**
35446          * @event visibilitychange
35447          * Fires when this region is shown or hidden 
35448          * @param {Roo.LayoutRegion} this
35449          * @param {Boolean} visibility true or false
35450          */
35451         "visibilitychange" : true,
35452         /**
35453          * @event paneladded
35454          * Fires when a panel is added. 
35455          * @param {Roo.LayoutRegion} this
35456          * @param {Roo.ContentPanel} panel The panel
35457          */
35458         "paneladded" : true,
35459         /**
35460          * @event panelremoved
35461          * Fires when a panel is removed. 
35462          * @param {Roo.LayoutRegion} this
35463          * @param {Roo.ContentPanel} panel The panel
35464          */
35465         "panelremoved" : true,
35466         /**
35467          * @event beforecollapse
35468          * Fires when this region before collapse.
35469          * @param {Roo.LayoutRegion} this
35470          */
35471         "beforecollapse" : true,
35472         /**
35473          * @event collapsed
35474          * Fires when this region is collapsed.
35475          * @param {Roo.LayoutRegion} this
35476          */
35477         "collapsed" : true,
35478         /**
35479          * @event expanded
35480          * Fires when this region is expanded.
35481          * @param {Roo.LayoutRegion} this
35482          */
35483         "expanded" : true,
35484         /**
35485          * @event slideshow
35486          * Fires when this region is slid into view.
35487          * @param {Roo.LayoutRegion} this
35488          */
35489         "slideshow" : true,
35490         /**
35491          * @event slidehide
35492          * Fires when this region slides out of view. 
35493          * @param {Roo.LayoutRegion} this
35494          */
35495         "slidehide" : true,
35496         /**
35497          * @event panelactivated
35498          * Fires when a panel is activated. 
35499          * @param {Roo.LayoutRegion} this
35500          * @param {Roo.ContentPanel} panel The activated panel
35501          */
35502         "panelactivated" : true,
35503         /**
35504          * @event resized
35505          * Fires when the user resizes this region. 
35506          * @param {Roo.LayoutRegion} this
35507          * @param {Number} newSize The new size (width for east/west, height for north/south)
35508          */
35509         "resized" : true
35510     };
35511     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35512     this.panels = new Roo.util.MixedCollection();
35513     this.panels.getKey = this.getPanelId.createDelegate(this);
35514     this.box = null;
35515     this.activePanel = null;
35516     // ensure listeners are added...
35517     
35518     if (config.listeners || config.events) {
35519         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35520             listeners : config.listeners || {},
35521             events : config.events || {}
35522         });
35523     }
35524     
35525     if(skipConfig !== true){
35526         this.applyConfig(config);
35527     }
35528 };
35529
35530 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35531 {
35532     getPanelId : function(p){
35533         return p.getId();
35534     },
35535     
35536     applyConfig : function(config){
35537         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35538         this.config = config;
35539         
35540     },
35541     
35542     /**
35543      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35544      * the width, for horizontal (north, south) the height.
35545      * @param {Number} newSize The new width or height
35546      */
35547     resizeTo : function(newSize){
35548         var el = this.el ? this.el :
35549                  (this.activePanel ? this.activePanel.getEl() : null);
35550         if(el){
35551             switch(this.position){
35552                 case "east":
35553                 case "west":
35554                     el.setWidth(newSize);
35555                     this.fireEvent("resized", this, newSize);
35556                 break;
35557                 case "north":
35558                 case "south":
35559                     el.setHeight(newSize);
35560                     this.fireEvent("resized", this, newSize);
35561                 break;                
35562             }
35563         }
35564     },
35565     
35566     getBox : function(){
35567         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35568     },
35569     
35570     getMargins : function(){
35571         return this.margins;
35572     },
35573     
35574     updateBox : function(box){
35575         this.box = box;
35576         var el = this.activePanel.getEl();
35577         el.dom.style.left = box.x + "px";
35578         el.dom.style.top = box.y + "px";
35579         this.activePanel.setSize(box.width, box.height);
35580     },
35581     
35582     /**
35583      * Returns the container element for this region.
35584      * @return {Roo.Element}
35585      */
35586     getEl : function(){
35587         return this.activePanel;
35588     },
35589     
35590     /**
35591      * Returns true if this region is currently visible.
35592      * @return {Boolean}
35593      */
35594     isVisible : function(){
35595         return this.activePanel ? true : false;
35596     },
35597     
35598     setActivePanel : function(panel){
35599         panel = this.getPanel(panel);
35600         if(this.activePanel && this.activePanel != panel){
35601             this.activePanel.setActiveState(false);
35602             this.activePanel.getEl().setLeftTop(-10000,-10000);
35603         }
35604         this.activePanel = panel;
35605         panel.setActiveState(true);
35606         if(this.box){
35607             panel.setSize(this.box.width, this.box.height);
35608         }
35609         this.fireEvent("panelactivated", this, panel);
35610         this.fireEvent("invalidated");
35611     },
35612     
35613     /**
35614      * Show the specified panel.
35615      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35616      * @return {Roo.ContentPanel} The shown panel or null
35617      */
35618     showPanel : function(panel){
35619         panel = this.getPanel(panel);
35620         if(panel){
35621             this.setActivePanel(panel);
35622         }
35623         return panel;
35624     },
35625     
35626     /**
35627      * Get the active panel for this region.
35628      * @return {Roo.ContentPanel} The active panel or null
35629      */
35630     getActivePanel : function(){
35631         return this.activePanel;
35632     },
35633     
35634     /**
35635      * Add the passed ContentPanel(s)
35636      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35637      * @return {Roo.ContentPanel} The panel added (if only one was added)
35638      */
35639     add : function(panel){
35640         if(arguments.length > 1){
35641             for(var i = 0, len = arguments.length; i < len; i++) {
35642                 this.add(arguments[i]);
35643             }
35644             return null;
35645         }
35646         if(this.hasPanel(panel)){
35647             this.showPanel(panel);
35648             return panel;
35649         }
35650         var el = panel.getEl();
35651         if(el.dom.parentNode != this.mgr.el.dom){
35652             this.mgr.el.dom.appendChild(el.dom);
35653         }
35654         if(panel.setRegion){
35655             panel.setRegion(this);
35656         }
35657         this.panels.add(panel);
35658         el.setStyle("position", "absolute");
35659         if(!panel.background){
35660             this.setActivePanel(panel);
35661             if(this.config.initialSize && this.panels.getCount()==1){
35662                 this.resizeTo(this.config.initialSize);
35663             }
35664         }
35665         this.fireEvent("paneladded", this, panel);
35666         return panel;
35667     },
35668     
35669     /**
35670      * Returns true if the panel is in this region.
35671      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35672      * @return {Boolean}
35673      */
35674     hasPanel : function(panel){
35675         if(typeof panel == "object"){ // must be panel obj
35676             panel = panel.getId();
35677         }
35678         return this.getPanel(panel) ? true : false;
35679     },
35680     
35681     /**
35682      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35683      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35684      * @param {Boolean} preservePanel Overrides the config preservePanel option
35685      * @return {Roo.ContentPanel} The panel that was removed
35686      */
35687     remove : function(panel, preservePanel){
35688         panel = this.getPanel(panel);
35689         if(!panel){
35690             return null;
35691         }
35692         var e = {};
35693         this.fireEvent("beforeremove", this, panel, e);
35694         if(e.cancel === true){
35695             return null;
35696         }
35697         var panelId = panel.getId();
35698         this.panels.removeKey(panelId);
35699         return panel;
35700     },
35701     
35702     /**
35703      * Returns the panel specified or null if it's not in this region.
35704      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35705      * @return {Roo.ContentPanel}
35706      */
35707     getPanel : function(id){
35708         if(typeof id == "object"){ // must be panel obj
35709             return id;
35710         }
35711         return this.panels.get(id);
35712     },
35713     
35714     /**
35715      * Returns this regions position (north/south/east/west/center).
35716      * @return {String} 
35717      */
35718     getPosition: function(){
35719         return this.position;    
35720     }
35721 });/*
35722  * Based on:
35723  * Ext JS Library 1.1.1
35724  * Copyright(c) 2006-2007, Ext JS, LLC.
35725  *
35726  * Originally Released Under LGPL - original licence link has changed is not relivant.
35727  *
35728  * Fork - LGPL
35729  * <script type="text/javascript">
35730  */
35731  
35732 /**
35733  * @class Roo.bootstrap.layout.Region
35734  * @extends Roo.bootstrap.layout.Basic
35735  * This class represents a region in a layout manager.
35736  
35737  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35738  * @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})
35739  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35740  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35741  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35742  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35743  * @cfg {String}    title           The title for the region (overrides panel titles)
35744  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35745  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35746  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35747  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35748  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35749  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35750  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35751  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35752  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35753  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35754
35755  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35756  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35757  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35758  * @cfg {Number}    width           For East/West panels
35759  * @cfg {Number}    height          For North/South panels
35760  * @cfg {Boolean}   split           To show the splitter
35761  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35762  * 
35763  * @cfg {string}   cls             Extra CSS classes to add to region
35764  * 
35765  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35766  * @cfg {string}   region  the region that it inhabits..
35767  *
35768
35769  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35770  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35771
35772  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35773  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35774  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35775  */
35776 Roo.bootstrap.layout.Region = function(config)
35777 {
35778     this.applyConfig(config);
35779
35780     var mgr = config.mgr;
35781     var pos = config.region;
35782     config.skipConfig = true;
35783     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35784     
35785     if (mgr.el) {
35786         this.onRender(mgr.el);   
35787     }
35788      
35789     this.visible = true;
35790     this.collapsed = false;
35791     this.unrendered_panels = [];
35792 };
35793
35794 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35795
35796     position: '', // set by wrapper (eg. north/south etc..)
35797     unrendered_panels : null,  // unrendered panels.
35798     createBody : function(){
35799         /** This region's body element 
35800         * @type Roo.Element */
35801         this.bodyEl = this.el.createChild({
35802                 tag: "div",
35803                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35804         });
35805     },
35806
35807     onRender: function(ctr, pos)
35808     {
35809         var dh = Roo.DomHelper;
35810         /** This region's container element 
35811         * @type Roo.Element */
35812         this.el = dh.append(ctr.dom, {
35813                 tag: "div",
35814                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35815             }, true);
35816         /** This region's title element 
35817         * @type Roo.Element */
35818     
35819         this.titleEl = dh.append(this.el.dom,
35820             {
35821                     tag: "div",
35822                     unselectable: "on",
35823                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35824                     children:[
35825                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35826                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35827                     ]}, true);
35828         
35829         this.titleEl.enableDisplayMode();
35830         /** This region's title text element 
35831         * @type HTMLElement */
35832         this.titleTextEl = this.titleEl.dom.firstChild;
35833         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35834         /*
35835         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35836         this.closeBtn.enableDisplayMode();
35837         this.closeBtn.on("click", this.closeClicked, this);
35838         this.closeBtn.hide();
35839     */
35840         this.createBody(this.config);
35841         if(this.config.hideWhenEmpty){
35842             this.hide();
35843             this.on("paneladded", this.validateVisibility, this);
35844             this.on("panelremoved", this.validateVisibility, this);
35845         }
35846         if(this.autoScroll){
35847             this.bodyEl.setStyle("overflow", "auto");
35848         }else{
35849             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35850         }
35851         //if(c.titlebar !== false){
35852             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35853                 this.titleEl.hide();
35854             }else{
35855                 this.titleEl.show();
35856                 if(this.config.title){
35857                     this.titleTextEl.innerHTML = this.config.title;
35858                 }
35859             }
35860         //}
35861         if(this.config.collapsed){
35862             this.collapse(true);
35863         }
35864         if(this.config.hidden){
35865             this.hide();
35866         }
35867         
35868         if (this.unrendered_panels && this.unrendered_panels.length) {
35869             for (var i =0;i< this.unrendered_panels.length; i++) {
35870                 this.add(this.unrendered_panels[i]);
35871             }
35872             this.unrendered_panels = null;
35873             
35874         }
35875         
35876     },
35877     
35878     applyConfig : function(c)
35879     {
35880         /*
35881          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35882             var dh = Roo.DomHelper;
35883             if(c.titlebar !== false){
35884                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35885                 this.collapseBtn.on("click", this.collapse, this);
35886                 this.collapseBtn.enableDisplayMode();
35887                 /*
35888                 if(c.showPin === true || this.showPin){
35889                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35890                     this.stickBtn.enableDisplayMode();
35891                     this.stickBtn.on("click", this.expand, this);
35892                     this.stickBtn.hide();
35893                 }
35894                 
35895             }
35896             */
35897             /** This region's collapsed element
35898             * @type Roo.Element */
35899             /*
35900              *
35901             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35902                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35903             ]}, true);
35904             
35905             if(c.floatable !== false){
35906                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35907                this.collapsedEl.on("click", this.collapseClick, this);
35908             }
35909
35910             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35911                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35912                    id: "message", unselectable: "on", style:{"float":"left"}});
35913                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35914              }
35915             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35916             this.expandBtn.on("click", this.expand, this);
35917             
35918         }
35919         
35920         if(this.collapseBtn){
35921             this.collapseBtn.setVisible(c.collapsible == true);
35922         }
35923         
35924         this.cmargins = c.cmargins || this.cmargins ||
35925                          (this.position == "west" || this.position == "east" ?
35926                              {top: 0, left: 2, right:2, bottom: 0} :
35927                              {top: 2, left: 0, right:0, bottom: 2});
35928         */
35929         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35930         
35931         
35932         this.bottomTabs = c.tabPosition != "top";
35933         
35934         this.autoScroll = c.autoScroll || false;
35935         
35936         
35937        
35938         
35939         this.duration = c.duration || .30;
35940         this.slideDuration = c.slideDuration || .45;
35941         this.config = c;
35942        
35943     },
35944     /**
35945      * Returns true if this region is currently visible.
35946      * @return {Boolean}
35947      */
35948     isVisible : function(){
35949         return this.visible;
35950     },
35951
35952     /**
35953      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35954      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35955      */
35956     //setCollapsedTitle : function(title){
35957     //    title = title || "&#160;";
35958      //   if(this.collapsedTitleTextEl){
35959       //      this.collapsedTitleTextEl.innerHTML = title;
35960        // }
35961     //},
35962
35963     getBox : function(){
35964         var b;
35965       //  if(!this.collapsed){
35966             b = this.el.getBox(false, true);
35967        // }else{
35968           //  b = this.collapsedEl.getBox(false, true);
35969         //}
35970         return b;
35971     },
35972
35973     getMargins : function(){
35974         return this.margins;
35975         //return this.collapsed ? this.cmargins : this.margins;
35976     },
35977 /*
35978     highlight : function(){
35979         this.el.addClass("x-layout-panel-dragover");
35980     },
35981
35982     unhighlight : function(){
35983         this.el.removeClass("x-layout-panel-dragover");
35984     },
35985 */
35986     updateBox : function(box)
35987     {
35988         if (!this.bodyEl) {
35989             return; // not rendered yet..
35990         }
35991         
35992         this.box = box;
35993         if(!this.collapsed){
35994             this.el.dom.style.left = box.x + "px";
35995             this.el.dom.style.top = box.y + "px";
35996             this.updateBody(box.width, box.height);
35997         }else{
35998             this.collapsedEl.dom.style.left = box.x + "px";
35999             this.collapsedEl.dom.style.top = box.y + "px";
36000             this.collapsedEl.setSize(box.width, box.height);
36001         }
36002         if(this.tabs){
36003             this.tabs.autoSizeTabs();
36004         }
36005     },
36006
36007     updateBody : function(w, h)
36008     {
36009         if(w !== null){
36010             this.el.setWidth(w);
36011             w -= this.el.getBorderWidth("rl");
36012             if(this.config.adjustments){
36013                 w += this.config.adjustments[0];
36014             }
36015         }
36016         if(h !== null && h > 0){
36017             this.el.setHeight(h);
36018             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36019             h -= this.el.getBorderWidth("tb");
36020             if(this.config.adjustments){
36021                 h += this.config.adjustments[1];
36022             }
36023             this.bodyEl.setHeight(h);
36024             if(this.tabs){
36025                 h = this.tabs.syncHeight(h);
36026             }
36027         }
36028         if(this.panelSize){
36029             w = w !== null ? w : this.panelSize.width;
36030             h = h !== null ? h : this.panelSize.height;
36031         }
36032         if(this.activePanel){
36033             var el = this.activePanel.getEl();
36034             w = w !== null ? w : el.getWidth();
36035             h = h !== null ? h : el.getHeight();
36036             this.panelSize = {width: w, height: h};
36037             this.activePanel.setSize(w, h);
36038         }
36039         if(Roo.isIE && this.tabs){
36040             this.tabs.el.repaint();
36041         }
36042     },
36043
36044     /**
36045      * Returns the container element for this region.
36046      * @return {Roo.Element}
36047      */
36048     getEl : function(){
36049         return this.el;
36050     },
36051
36052     /**
36053      * Hides this region.
36054      */
36055     hide : function(){
36056         //if(!this.collapsed){
36057             this.el.dom.style.left = "-2000px";
36058             this.el.hide();
36059         //}else{
36060          //   this.collapsedEl.dom.style.left = "-2000px";
36061          //   this.collapsedEl.hide();
36062        // }
36063         this.visible = false;
36064         this.fireEvent("visibilitychange", this, false);
36065     },
36066
36067     /**
36068      * Shows this region if it was previously hidden.
36069      */
36070     show : function(){
36071         //if(!this.collapsed){
36072             this.el.show();
36073         //}else{
36074         //    this.collapsedEl.show();
36075        // }
36076         this.visible = true;
36077         this.fireEvent("visibilitychange", this, true);
36078     },
36079 /*
36080     closeClicked : function(){
36081         if(this.activePanel){
36082             this.remove(this.activePanel);
36083         }
36084     },
36085
36086     collapseClick : function(e){
36087         if(this.isSlid){
36088            e.stopPropagation();
36089            this.slideIn();
36090         }else{
36091            e.stopPropagation();
36092            this.slideOut();
36093         }
36094     },
36095 */
36096     /**
36097      * Collapses this region.
36098      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36099      */
36100     /*
36101     collapse : function(skipAnim, skipCheck = false){
36102         if(this.collapsed) {
36103             return;
36104         }
36105         
36106         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36107             
36108             this.collapsed = true;
36109             if(this.split){
36110                 this.split.el.hide();
36111             }
36112             if(this.config.animate && skipAnim !== true){
36113                 this.fireEvent("invalidated", this);
36114                 this.animateCollapse();
36115             }else{
36116                 this.el.setLocation(-20000,-20000);
36117                 this.el.hide();
36118                 this.collapsedEl.show();
36119                 this.fireEvent("collapsed", this);
36120                 this.fireEvent("invalidated", this);
36121             }
36122         }
36123         
36124     },
36125 */
36126     animateCollapse : function(){
36127         // overridden
36128     },
36129
36130     /**
36131      * Expands this region if it was previously collapsed.
36132      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36133      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36134      */
36135     /*
36136     expand : function(e, skipAnim){
36137         if(e) {
36138             e.stopPropagation();
36139         }
36140         if(!this.collapsed || this.el.hasActiveFx()) {
36141             return;
36142         }
36143         if(this.isSlid){
36144             this.afterSlideIn();
36145             skipAnim = true;
36146         }
36147         this.collapsed = false;
36148         if(this.config.animate && skipAnim !== true){
36149             this.animateExpand();
36150         }else{
36151             this.el.show();
36152             if(this.split){
36153                 this.split.el.show();
36154             }
36155             this.collapsedEl.setLocation(-2000,-2000);
36156             this.collapsedEl.hide();
36157             this.fireEvent("invalidated", this);
36158             this.fireEvent("expanded", this);
36159         }
36160     },
36161 */
36162     animateExpand : function(){
36163         // overridden
36164     },
36165
36166     initTabs : function()
36167     {
36168         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36169         
36170         var ts = new Roo.bootstrap.panel.Tabs({
36171                 el: this.bodyEl.dom,
36172                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36173                 disableTooltips: this.config.disableTabTips,
36174                 toolbar : this.config.toolbar
36175             });
36176         
36177         if(this.config.hideTabs){
36178             ts.stripWrap.setDisplayed(false);
36179         }
36180         this.tabs = ts;
36181         ts.resizeTabs = this.config.resizeTabs === true;
36182         ts.minTabWidth = this.config.minTabWidth || 40;
36183         ts.maxTabWidth = this.config.maxTabWidth || 250;
36184         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36185         ts.monitorResize = false;
36186         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36187         ts.bodyEl.addClass('roo-layout-tabs-body');
36188         this.panels.each(this.initPanelAsTab, this);
36189     },
36190
36191     initPanelAsTab : function(panel){
36192         var ti = this.tabs.addTab(
36193             panel.getEl().id,
36194             panel.getTitle(),
36195             null,
36196             this.config.closeOnTab && panel.isClosable(),
36197             panel.tpl
36198         );
36199         if(panel.tabTip !== undefined){
36200             ti.setTooltip(panel.tabTip);
36201         }
36202         ti.on("activate", function(){
36203               this.setActivePanel(panel);
36204         }, this);
36205         
36206         if(this.config.closeOnTab){
36207             ti.on("beforeclose", function(t, e){
36208                 e.cancel = true;
36209                 this.remove(panel);
36210             }, this);
36211         }
36212         
36213         panel.tabItem = ti;
36214         
36215         return ti;
36216     },
36217
36218     updatePanelTitle : function(panel, title)
36219     {
36220         if(this.activePanel == panel){
36221             this.updateTitle(title);
36222         }
36223         if(this.tabs){
36224             var ti = this.tabs.getTab(panel.getEl().id);
36225             ti.setText(title);
36226             if(panel.tabTip !== undefined){
36227                 ti.setTooltip(panel.tabTip);
36228             }
36229         }
36230     },
36231
36232     updateTitle : function(title){
36233         if(this.titleTextEl && !this.config.title){
36234             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36235         }
36236     },
36237
36238     setActivePanel : function(panel)
36239     {
36240         panel = this.getPanel(panel);
36241         if(this.activePanel && this.activePanel != panel){
36242             if(this.activePanel.setActiveState(false) === false){
36243                 return;
36244             }
36245         }
36246         this.activePanel = panel;
36247         panel.setActiveState(true);
36248         if(this.panelSize){
36249             panel.setSize(this.panelSize.width, this.panelSize.height);
36250         }
36251         if(this.closeBtn){
36252             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36253         }
36254         this.updateTitle(panel.getTitle());
36255         if(this.tabs){
36256             this.fireEvent("invalidated", this);
36257         }
36258         this.fireEvent("panelactivated", this, panel);
36259     },
36260
36261     /**
36262      * Shows the specified panel.
36263      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36264      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36265      */
36266     showPanel : function(panel)
36267     {
36268         panel = this.getPanel(panel);
36269         if(panel){
36270             if(this.tabs){
36271                 var tab = this.tabs.getTab(panel.getEl().id);
36272                 if(tab.isHidden()){
36273                     this.tabs.unhideTab(tab.id);
36274                 }
36275                 tab.activate();
36276             }else{
36277                 this.setActivePanel(panel);
36278             }
36279         }
36280         return panel;
36281     },
36282
36283     /**
36284      * Get the active panel for this region.
36285      * @return {Roo.ContentPanel} The active panel or null
36286      */
36287     getActivePanel : function(){
36288         return this.activePanel;
36289     },
36290
36291     validateVisibility : function(){
36292         if(this.panels.getCount() < 1){
36293             this.updateTitle("&#160;");
36294             this.closeBtn.hide();
36295             this.hide();
36296         }else{
36297             if(!this.isVisible()){
36298                 this.show();
36299             }
36300         }
36301     },
36302
36303     /**
36304      * Adds the passed ContentPanel(s) to this region.
36305      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36306      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36307      */
36308     add : function(panel)
36309     {
36310         if(arguments.length > 1){
36311             for(var i = 0, len = arguments.length; i < len; i++) {
36312                 this.add(arguments[i]);
36313             }
36314             return null;
36315         }
36316         
36317         // if we have not been rendered yet, then we can not really do much of this..
36318         if (!this.bodyEl) {
36319             this.unrendered_panels.push(panel);
36320             return panel;
36321         }
36322         
36323         
36324         
36325         
36326         if(this.hasPanel(panel)){
36327             this.showPanel(panel);
36328             return panel;
36329         }
36330         panel.setRegion(this);
36331         this.panels.add(panel);
36332        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36333             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36334             // and hide them... ???
36335             this.bodyEl.dom.appendChild(panel.getEl().dom);
36336             if(panel.background !== true){
36337                 this.setActivePanel(panel);
36338             }
36339             this.fireEvent("paneladded", this, panel);
36340             return panel;
36341         }
36342         */
36343         if(!this.tabs){
36344             this.initTabs();
36345         }else{
36346             this.initPanelAsTab(panel);
36347         }
36348         
36349         
36350         if(panel.background !== true){
36351             this.tabs.activate(panel.getEl().id);
36352         }
36353         this.fireEvent("paneladded", this, panel);
36354         return panel;
36355     },
36356
36357     /**
36358      * Hides the tab for the specified panel.
36359      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36360      */
36361     hidePanel : function(panel){
36362         if(this.tabs && (panel = this.getPanel(panel))){
36363             this.tabs.hideTab(panel.getEl().id);
36364         }
36365     },
36366
36367     /**
36368      * Unhides the tab for a previously hidden panel.
36369      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36370      */
36371     unhidePanel : function(panel){
36372         if(this.tabs && (panel = this.getPanel(panel))){
36373             this.tabs.unhideTab(panel.getEl().id);
36374         }
36375     },
36376
36377     clearPanels : function(){
36378         while(this.panels.getCount() > 0){
36379              this.remove(this.panels.first());
36380         }
36381     },
36382
36383     /**
36384      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36385      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36386      * @param {Boolean} preservePanel Overrides the config preservePanel option
36387      * @return {Roo.ContentPanel} The panel that was removed
36388      */
36389     remove : function(panel, preservePanel)
36390     {
36391         panel = this.getPanel(panel);
36392         if(!panel){
36393             return null;
36394         }
36395         var e = {};
36396         this.fireEvent("beforeremove", this, panel, e);
36397         if(e.cancel === true){
36398             return null;
36399         }
36400         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36401         var panelId = panel.getId();
36402         this.panels.removeKey(panelId);
36403         if(preservePanel){
36404             document.body.appendChild(panel.getEl().dom);
36405         }
36406         if(this.tabs){
36407             this.tabs.removeTab(panel.getEl().id);
36408         }else if (!preservePanel){
36409             this.bodyEl.dom.removeChild(panel.getEl().dom);
36410         }
36411         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36412             var p = this.panels.first();
36413             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36414             tempEl.appendChild(p.getEl().dom);
36415             this.bodyEl.update("");
36416             this.bodyEl.dom.appendChild(p.getEl().dom);
36417             tempEl = null;
36418             this.updateTitle(p.getTitle());
36419             this.tabs = null;
36420             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36421             this.setActivePanel(p);
36422         }
36423         panel.setRegion(null);
36424         if(this.activePanel == panel){
36425             this.activePanel = null;
36426         }
36427         if(this.config.autoDestroy !== false && preservePanel !== true){
36428             try{panel.destroy();}catch(e){}
36429         }
36430         this.fireEvent("panelremoved", this, panel);
36431         return panel;
36432     },
36433
36434     /**
36435      * Returns the TabPanel component used by this region
36436      * @return {Roo.TabPanel}
36437      */
36438     getTabs : function(){
36439         return this.tabs;
36440     },
36441
36442     createTool : function(parentEl, className){
36443         var btn = Roo.DomHelper.append(parentEl, {
36444             tag: "div",
36445             cls: "x-layout-tools-button",
36446             children: [ {
36447                 tag: "div",
36448                 cls: "roo-layout-tools-button-inner " + className,
36449                 html: "&#160;"
36450             }]
36451         }, true);
36452         btn.addClassOnOver("roo-layout-tools-button-over");
36453         return btn;
36454     }
36455 });/*
36456  * Based on:
36457  * Ext JS Library 1.1.1
36458  * Copyright(c) 2006-2007, Ext JS, LLC.
36459  *
36460  * Originally Released Under LGPL - original licence link has changed is not relivant.
36461  *
36462  * Fork - LGPL
36463  * <script type="text/javascript">
36464  */
36465  
36466
36467
36468 /**
36469  * @class Roo.SplitLayoutRegion
36470  * @extends Roo.LayoutRegion
36471  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36472  */
36473 Roo.bootstrap.layout.Split = function(config){
36474     this.cursor = config.cursor;
36475     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36476 };
36477
36478 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36479 {
36480     splitTip : "Drag to resize.",
36481     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36482     useSplitTips : false,
36483
36484     applyConfig : function(config){
36485         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36486     },
36487     
36488     onRender : function(ctr,pos) {
36489         
36490         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36491         if(!this.config.split){
36492             return;
36493         }
36494         if(!this.split){
36495             
36496             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36497                             tag: "div",
36498                             id: this.el.id + "-split",
36499                             cls: "roo-layout-split roo-layout-split-"+this.position,
36500                             html: "&#160;"
36501             });
36502             /** The SplitBar for this region 
36503             * @type Roo.SplitBar */
36504             // does not exist yet...
36505             Roo.log([this.position, this.orientation]);
36506             
36507             this.split = new Roo.bootstrap.SplitBar({
36508                 dragElement : splitEl,
36509                 resizingElement: this.el,
36510                 orientation : this.orientation
36511             });
36512             
36513             this.split.on("moved", this.onSplitMove, this);
36514             this.split.useShim = this.config.useShim === true;
36515             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36516             if(this.useSplitTips){
36517                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36518             }
36519             //if(config.collapsible){
36520             //    this.split.el.on("dblclick", this.collapse,  this);
36521             //}
36522         }
36523         if(typeof this.config.minSize != "undefined"){
36524             this.split.minSize = this.config.minSize;
36525         }
36526         if(typeof this.config.maxSize != "undefined"){
36527             this.split.maxSize = this.config.maxSize;
36528         }
36529         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36530             this.hideSplitter();
36531         }
36532         
36533     },
36534
36535     getHMaxSize : function(){
36536          var cmax = this.config.maxSize || 10000;
36537          var center = this.mgr.getRegion("center");
36538          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36539     },
36540
36541     getVMaxSize : function(){
36542          var cmax = this.config.maxSize || 10000;
36543          var center = this.mgr.getRegion("center");
36544          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36545     },
36546
36547     onSplitMove : function(split, newSize){
36548         this.fireEvent("resized", this, newSize);
36549     },
36550     
36551     /** 
36552      * Returns the {@link Roo.SplitBar} for this region.
36553      * @return {Roo.SplitBar}
36554      */
36555     getSplitBar : function(){
36556         return this.split;
36557     },
36558     
36559     hide : function(){
36560         this.hideSplitter();
36561         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36562     },
36563
36564     hideSplitter : function(){
36565         if(this.split){
36566             this.split.el.setLocation(-2000,-2000);
36567             this.split.el.hide();
36568         }
36569     },
36570
36571     show : function(){
36572         if(this.split){
36573             this.split.el.show();
36574         }
36575         Roo.bootstrap.layout.Split.superclass.show.call(this);
36576     },
36577     
36578     beforeSlide: function(){
36579         if(Roo.isGecko){// firefox overflow auto bug workaround
36580             this.bodyEl.clip();
36581             if(this.tabs) {
36582                 this.tabs.bodyEl.clip();
36583             }
36584             if(this.activePanel){
36585                 this.activePanel.getEl().clip();
36586                 
36587                 if(this.activePanel.beforeSlide){
36588                     this.activePanel.beforeSlide();
36589                 }
36590             }
36591         }
36592     },
36593     
36594     afterSlide : function(){
36595         if(Roo.isGecko){// firefox overflow auto bug workaround
36596             this.bodyEl.unclip();
36597             if(this.tabs) {
36598                 this.tabs.bodyEl.unclip();
36599             }
36600             if(this.activePanel){
36601                 this.activePanel.getEl().unclip();
36602                 if(this.activePanel.afterSlide){
36603                     this.activePanel.afterSlide();
36604                 }
36605             }
36606         }
36607     },
36608
36609     initAutoHide : function(){
36610         if(this.autoHide !== false){
36611             if(!this.autoHideHd){
36612                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36613                 this.autoHideHd = {
36614                     "mouseout": function(e){
36615                         if(!e.within(this.el, true)){
36616                             st.delay(500);
36617                         }
36618                     },
36619                     "mouseover" : function(e){
36620                         st.cancel();
36621                     },
36622                     scope : this
36623                 };
36624             }
36625             this.el.on(this.autoHideHd);
36626         }
36627     },
36628
36629     clearAutoHide : function(){
36630         if(this.autoHide !== false){
36631             this.el.un("mouseout", this.autoHideHd.mouseout);
36632             this.el.un("mouseover", this.autoHideHd.mouseover);
36633         }
36634     },
36635
36636     clearMonitor : function(){
36637         Roo.get(document).un("click", this.slideInIf, this);
36638     },
36639
36640     // these names are backwards but not changed for compat
36641     slideOut : function(){
36642         if(this.isSlid || this.el.hasActiveFx()){
36643             return;
36644         }
36645         this.isSlid = true;
36646         if(this.collapseBtn){
36647             this.collapseBtn.hide();
36648         }
36649         this.closeBtnState = this.closeBtn.getStyle('display');
36650         this.closeBtn.hide();
36651         if(this.stickBtn){
36652             this.stickBtn.show();
36653         }
36654         this.el.show();
36655         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36656         this.beforeSlide();
36657         this.el.setStyle("z-index", 10001);
36658         this.el.slideIn(this.getSlideAnchor(), {
36659             callback: function(){
36660                 this.afterSlide();
36661                 this.initAutoHide();
36662                 Roo.get(document).on("click", this.slideInIf, this);
36663                 this.fireEvent("slideshow", this);
36664             },
36665             scope: this,
36666             block: true
36667         });
36668     },
36669
36670     afterSlideIn : function(){
36671         this.clearAutoHide();
36672         this.isSlid = false;
36673         this.clearMonitor();
36674         this.el.setStyle("z-index", "");
36675         if(this.collapseBtn){
36676             this.collapseBtn.show();
36677         }
36678         this.closeBtn.setStyle('display', this.closeBtnState);
36679         if(this.stickBtn){
36680             this.stickBtn.hide();
36681         }
36682         this.fireEvent("slidehide", this);
36683     },
36684
36685     slideIn : function(cb){
36686         if(!this.isSlid || this.el.hasActiveFx()){
36687             Roo.callback(cb);
36688             return;
36689         }
36690         this.isSlid = false;
36691         this.beforeSlide();
36692         this.el.slideOut(this.getSlideAnchor(), {
36693             callback: function(){
36694                 this.el.setLeftTop(-10000, -10000);
36695                 this.afterSlide();
36696                 this.afterSlideIn();
36697                 Roo.callback(cb);
36698             },
36699             scope: this,
36700             block: true
36701         });
36702     },
36703     
36704     slideInIf : function(e){
36705         if(!e.within(this.el)){
36706             this.slideIn();
36707         }
36708     },
36709
36710     animateCollapse : function(){
36711         this.beforeSlide();
36712         this.el.setStyle("z-index", 20000);
36713         var anchor = this.getSlideAnchor();
36714         this.el.slideOut(anchor, {
36715             callback : function(){
36716                 this.el.setStyle("z-index", "");
36717                 this.collapsedEl.slideIn(anchor, {duration:.3});
36718                 this.afterSlide();
36719                 this.el.setLocation(-10000,-10000);
36720                 this.el.hide();
36721                 this.fireEvent("collapsed", this);
36722             },
36723             scope: this,
36724             block: true
36725         });
36726     },
36727
36728     animateExpand : function(){
36729         this.beforeSlide();
36730         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36731         this.el.setStyle("z-index", 20000);
36732         this.collapsedEl.hide({
36733             duration:.1
36734         });
36735         this.el.slideIn(this.getSlideAnchor(), {
36736             callback : function(){
36737                 this.el.setStyle("z-index", "");
36738                 this.afterSlide();
36739                 if(this.split){
36740                     this.split.el.show();
36741                 }
36742                 this.fireEvent("invalidated", this);
36743                 this.fireEvent("expanded", this);
36744             },
36745             scope: this,
36746             block: true
36747         });
36748     },
36749
36750     anchors : {
36751         "west" : "left",
36752         "east" : "right",
36753         "north" : "top",
36754         "south" : "bottom"
36755     },
36756
36757     sanchors : {
36758         "west" : "l",
36759         "east" : "r",
36760         "north" : "t",
36761         "south" : "b"
36762     },
36763
36764     canchors : {
36765         "west" : "tl-tr",
36766         "east" : "tr-tl",
36767         "north" : "tl-bl",
36768         "south" : "bl-tl"
36769     },
36770
36771     getAnchor : function(){
36772         return this.anchors[this.position];
36773     },
36774
36775     getCollapseAnchor : function(){
36776         return this.canchors[this.position];
36777     },
36778
36779     getSlideAnchor : function(){
36780         return this.sanchors[this.position];
36781     },
36782
36783     getAlignAdj : function(){
36784         var cm = this.cmargins;
36785         switch(this.position){
36786             case "west":
36787                 return [0, 0];
36788             break;
36789             case "east":
36790                 return [0, 0];
36791             break;
36792             case "north":
36793                 return [0, 0];
36794             break;
36795             case "south":
36796                 return [0, 0];
36797             break;
36798         }
36799     },
36800
36801     getExpandAdj : function(){
36802         var c = this.collapsedEl, cm = this.cmargins;
36803         switch(this.position){
36804             case "west":
36805                 return [-(cm.right+c.getWidth()+cm.left), 0];
36806             break;
36807             case "east":
36808                 return [cm.right+c.getWidth()+cm.left, 0];
36809             break;
36810             case "north":
36811                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36812             break;
36813             case "south":
36814                 return [0, cm.top+cm.bottom+c.getHeight()];
36815             break;
36816         }
36817     }
36818 });/*
36819  * Based on:
36820  * Ext JS Library 1.1.1
36821  * Copyright(c) 2006-2007, Ext JS, LLC.
36822  *
36823  * Originally Released Under LGPL - original licence link has changed is not relivant.
36824  *
36825  * Fork - LGPL
36826  * <script type="text/javascript">
36827  */
36828 /*
36829  * These classes are private internal classes
36830  */
36831 Roo.bootstrap.layout.Center = function(config){
36832     config.region = "center";
36833     Roo.bootstrap.layout.Region.call(this, config);
36834     this.visible = true;
36835     this.minWidth = config.minWidth || 20;
36836     this.minHeight = config.minHeight || 20;
36837 };
36838
36839 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36840     hide : function(){
36841         // center panel can't be hidden
36842     },
36843     
36844     show : function(){
36845         // center panel can't be hidden
36846     },
36847     
36848     getMinWidth: function(){
36849         return this.minWidth;
36850     },
36851     
36852     getMinHeight: function(){
36853         return this.minHeight;
36854     }
36855 });
36856
36857
36858
36859
36860  
36861
36862
36863
36864
36865
36866 Roo.bootstrap.layout.North = function(config)
36867 {
36868     config.region = 'north';
36869     config.cursor = 'n-resize';
36870     
36871     Roo.bootstrap.layout.Split.call(this, config);
36872     
36873     
36874     if(this.split){
36875         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36876         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36877         this.split.el.addClass("roo-layout-split-v");
36878     }
36879     var size = config.initialSize || config.height;
36880     if(typeof size != "undefined"){
36881         this.el.setHeight(size);
36882     }
36883 };
36884 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36885 {
36886     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36887     
36888     
36889     
36890     getBox : function(){
36891         if(this.collapsed){
36892             return this.collapsedEl.getBox();
36893         }
36894         var box = this.el.getBox();
36895         if(this.split){
36896             box.height += this.split.el.getHeight();
36897         }
36898         return box;
36899     },
36900     
36901     updateBox : function(box){
36902         if(this.split && !this.collapsed){
36903             box.height -= this.split.el.getHeight();
36904             this.split.el.setLeft(box.x);
36905             this.split.el.setTop(box.y+box.height);
36906             this.split.el.setWidth(box.width);
36907         }
36908         if(this.collapsed){
36909             this.updateBody(box.width, null);
36910         }
36911         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36912     }
36913 });
36914
36915
36916
36917
36918
36919 Roo.bootstrap.layout.South = function(config){
36920     config.region = 'south';
36921     config.cursor = 's-resize';
36922     Roo.bootstrap.layout.Split.call(this, config);
36923     if(this.split){
36924         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36925         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36926         this.split.el.addClass("roo-layout-split-v");
36927     }
36928     var size = config.initialSize || config.height;
36929     if(typeof size != "undefined"){
36930         this.el.setHeight(size);
36931     }
36932 };
36933
36934 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36935     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36936     getBox : function(){
36937         if(this.collapsed){
36938             return this.collapsedEl.getBox();
36939         }
36940         var box = this.el.getBox();
36941         if(this.split){
36942             var sh = this.split.el.getHeight();
36943             box.height += sh;
36944             box.y -= sh;
36945         }
36946         return box;
36947     },
36948     
36949     updateBox : function(box){
36950         if(this.split && !this.collapsed){
36951             var sh = this.split.el.getHeight();
36952             box.height -= sh;
36953             box.y += sh;
36954             this.split.el.setLeft(box.x);
36955             this.split.el.setTop(box.y-sh);
36956             this.split.el.setWidth(box.width);
36957         }
36958         if(this.collapsed){
36959             this.updateBody(box.width, null);
36960         }
36961         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36962     }
36963 });
36964
36965 Roo.bootstrap.layout.East = function(config){
36966     config.region = "east";
36967     config.cursor = "e-resize";
36968     Roo.bootstrap.layout.Split.call(this, config);
36969     if(this.split){
36970         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36971         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36972         this.split.el.addClass("roo-layout-split-h");
36973     }
36974     var size = config.initialSize || config.width;
36975     if(typeof size != "undefined"){
36976         this.el.setWidth(size);
36977     }
36978 };
36979 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36980     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36981     getBox : function(){
36982         if(this.collapsed){
36983             return this.collapsedEl.getBox();
36984         }
36985         var box = this.el.getBox();
36986         if(this.split){
36987             var sw = this.split.el.getWidth();
36988             box.width += sw;
36989             box.x -= sw;
36990         }
36991         return box;
36992     },
36993
36994     updateBox : function(box){
36995         if(this.split && !this.collapsed){
36996             var sw = this.split.el.getWidth();
36997             box.width -= sw;
36998             this.split.el.setLeft(box.x);
36999             this.split.el.setTop(box.y);
37000             this.split.el.setHeight(box.height);
37001             box.x += sw;
37002         }
37003         if(this.collapsed){
37004             this.updateBody(null, box.height);
37005         }
37006         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37007     }
37008 });
37009
37010 Roo.bootstrap.layout.West = function(config){
37011     config.region = "west";
37012     config.cursor = "w-resize";
37013     
37014     Roo.bootstrap.layout.Split.call(this, config);
37015     if(this.split){
37016         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37017         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37018         this.split.el.addClass("roo-layout-split-h");
37019     }
37020     
37021 };
37022 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37023     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37024     
37025     onRender: function(ctr, pos)
37026     {
37027         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37028         var size = this.config.initialSize || this.config.width;
37029         if(typeof size != "undefined"){
37030             this.el.setWidth(size);
37031         }
37032     },
37033     
37034     getBox : function(){
37035         if(this.collapsed){
37036             return this.collapsedEl.getBox();
37037         }
37038         var box = this.el.getBox();
37039         if(this.split){
37040             box.width += this.split.el.getWidth();
37041         }
37042         return box;
37043     },
37044     
37045     updateBox : function(box){
37046         if(this.split && !this.collapsed){
37047             var sw = this.split.el.getWidth();
37048             box.width -= sw;
37049             this.split.el.setLeft(box.x+box.width);
37050             this.split.el.setTop(box.y);
37051             this.split.el.setHeight(box.height);
37052         }
37053         if(this.collapsed){
37054             this.updateBody(null, box.height);
37055         }
37056         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37057     }
37058 });
37059 Roo.namespace("Roo.bootstrap.panel");/*
37060  * Based on:
37061  * Ext JS Library 1.1.1
37062  * Copyright(c) 2006-2007, Ext JS, LLC.
37063  *
37064  * Originally Released Under LGPL - original licence link has changed is not relivant.
37065  *
37066  * Fork - LGPL
37067  * <script type="text/javascript">
37068  */
37069 /**
37070  * @class Roo.ContentPanel
37071  * @extends Roo.util.Observable
37072  * A basic ContentPanel element.
37073  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37074  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37075  * @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
37076  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37077  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37078  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37079  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37080  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37081  * @cfg {String} title          The title for this panel
37082  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37083  * @cfg {String} url            Calls {@link #setUrl} with this value
37084  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37085  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37086  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37087  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37088  * @cfg {Boolean} badges render the badges
37089
37090  * @constructor
37091  * Create a new ContentPanel.
37092  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37093  * @param {String/Object} config A string to set only the title or a config object
37094  * @param {String} content (optional) Set the HTML content for this panel
37095  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37096  */
37097 Roo.bootstrap.panel.Content = function( config){
37098     
37099     this.tpl = config.tpl || false;
37100     
37101     var el = config.el;
37102     var content = config.content;
37103
37104     if(config.autoCreate){ // xtype is available if this is called from factory
37105         el = Roo.id();
37106     }
37107     this.el = Roo.get(el);
37108     if(!this.el && config && config.autoCreate){
37109         if(typeof config.autoCreate == "object"){
37110             if(!config.autoCreate.id){
37111                 config.autoCreate.id = config.id||el;
37112             }
37113             this.el = Roo.DomHelper.append(document.body,
37114                         config.autoCreate, true);
37115         }else{
37116             var elcfg =  {   tag: "div",
37117                             cls: "roo-layout-inactive-content",
37118                             id: config.id||el
37119                             };
37120             if (config.html) {
37121                 elcfg.html = config.html;
37122                 
37123             }
37124                         
37125             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37126         }
37127     } 
37128     this.closable = false;
37129     this.loaded = false;
37130     this.active = false;
37131    
37132       
37133     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37134         
37135         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37136         
37137         this.wrapEl = this.el; //this.el.wrap();
37138         var ti = [];
37139         if (config.toolbar.items) {
37140             ti = config.toolbar.items ;
37141             delete config.toolbar.items ;
37142         }
37143         
37144         var nitems = [];
37145         this.toolbar.render(this.wrapEl, 'before');
37146         for(var i =0;i < ti.length;i++) {
37147           //  Roo.log(['add child', items[i]]);
37148             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37149         }
37150         this.toolbar.items = nitems;
37151         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37152         delete config.toolbar;
37153         
37154     }
37155     /*
37156     // xtype created footer. - not sure if will work as we normally have to render first..
37157     if (this.footer && !this.footer.el && this.footer.xtype) {
37158         if (!this.wrapEl) {
37159             this.wrapEl = this.el.wrap();
37160         }
37161     
37162         this.footer.container = this.wrapEl.createChild();
37163          
37164         this.footer = Roo.factory(this.footer, Roo);
37165         
37166     }
37167     */
37168     
37169      if(typeof config == "string"){
37170         this.title = config;
37171     }else{
37172         Roo.apply(this, config);
37173     }
37174     
37175     if(this.resizeEl){
37176         this.resizeEl = Roo.get(this.resizeEl, true);
37177     }else{
37178         this.resizeEl = this.el;
37179     }
37180     // handle view.xtype
37181     
37182  
37183     
37184     
37185     this.addEvents({
37186         /**
37187          * @event activate
37188          * Fires when this panel is activated. 
37189          * @param {Roo.ContentPanel} this
37190          */
37191         "activate" : true,
37192         /**
37193          * @event deactivate
37194          * Fires when this panel is activated. 
37195          * @param {Roo.ContentPanel} this
37196          */
37197         "deactivate" : true,
37198
37199         /**
37200          * @event resize
37201          * Fires when this panel is resized if fitToFrame is true.
37202          * @param {Roo.ContentPanel} this
37203          * @param {Number} width The width after any component adjustments
37204          * @param {Number} height The height after any component adjustments
37205          */
37206         "resize" : true,
37207         
37208          /**
37209          * @event render
37210          * Fires when this tab is created
37211          * @param {Roo.ContentPanel} this
37212          */
37213         "render" : true
37214         
37215         
37216         
37217     });
37218     
37219
37220     
37221     
37222     if(this.autoScroll){
37223         this.resizeEl.setStyle("overflow", "auto");
37224     } else {
37225         // fix randome scrolling
37226         //this.el.on('scroll', function() {
37227         //    Roo.log('fix random scolling');
37228         //    this.scrollTo('top',0); 
37229         //});
37230     }
37231     content = content || this.content;
37232     if(content){
37233         this.setContent(content);
37234     }
37235     if(config && config.url){
37236         this.setUrl(this.url, this.params, this.loadOnce);
37237     }
37238     
37239     
37240     
37241     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37242     
37243     if (this.view && typeof(this.view.xtype) != 'undefined') {
37244         this.view.el = this.el.appendChild(document.createElement("div"));
37245         this.view = Roo.factory(this.view); 
37246         this.view.render  &&  this.view.render(false, '');  
37247     }
37248     
37249     
37250     this.fireEvent('render', this);
37251 };
37252
37253 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37254     
37255     tabTip : '',
37256     
37257     setRegion : function(region){
37258         this.region = region;
37259         this.setActiveClass(region && !this.background);
37260     },
37261     
37262     
37263     setActiveClass: function(state)
37264     {
37265         if(state){
37266            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37267            this.el.setStyle('position','relative');
37268         }else{
37269            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37270            this.el.setStyle('position', 'absolute');
37271         } 
37272     },
37273     
37274     /**
37275      * Returns the toolbar for this Panel if one was configured. 
37276      * @return {Roo.Toolbar} 
37277      */
37278     getToolbar : function(){
37279         return this.toolbar;
37280     },
37281     
37282     setActiveState : function(active)
37283     {
37284         this.active = active;
37285         this.setActiveClass(active);
37286         if(!active){
37287             if(this.fireEvent("deactivate", this) === false){
37288                 return false;
37289             }
37290             return true;
37291         }
37292         this.fireEvent("activate", this);
37293         return true;
37294     },
37295     /**
37296      * Updates this panel's element
37297      * @param {String} content The new content
37298      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37299     */
37300     setContent : function(content, loadScripts){
37301         this.el.update(content, loadScripts);
37302     },
37303
37304     ignoreResize : function(w, h){
37305         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37306             return true;
37307         }else{
37308             this.lastSize = {width: w, height: h};
37309             return false;
37310         }
37311     },
37312     /**
37313      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37314      * @return {Roo.UpdateManager} The UpdateManager
37315      */
37316     getUpdateManager : function(){
37317         return this.el.getUpdateManager();
37318     },
37319      /**
37320      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37321      * @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:
37322 <pre><code>
37323 panel.load({
37324     url: "your-url.php",
37325     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37326     callback: yourFunction,
37327     scope: yourObject, //(optional scope)
37328     discardUrl: false,
37329     nocache: false,
37330     text: "Loading...",
37331     timeout: 30,
37332     scripts: false
37333 });
37334 </code></pre>
37335      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37336      * 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.
37337      * @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}
37338      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37339      * @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.
37340      * @return {Roo.ContentPanel} this
37341      */
37342     load : function(){
37343         var um = this.el.getUpdateManager();
37344         um.update.apply(um, arguments);
37345         return this;
37346     },
37347
37348
37349     /**
37350      * 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.
37351      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37352      * @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)
37353      * @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)
37354      * @return {Roo.UpdateManager} The UpdateManager
37355      */
37356     setUrl : function(url, params, loadOnce){
37357         if(this.refreshDelegate){
37358             this.removeListener("activate", this.refreshDelegate);
37359         }
37360         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37361         this.on("activate", this.refreshDelegate);
37362         return this.el.getUpdateManager();
37363     },
37364     
37365     _handleRefresh : function(url, params, loadOnce){
37366         if(!loadOnce || !this.loaded){
37367             var updater = this.el.getUpdateManager();
37368             updater.update(url, params, this._setLoaded.createDelegate(this));
37369         }
37370     },
37371     
37372     _setLoaded : function(){
37373         this.loaded = true;
37374     }, 
37375     
37376     /**
37377      * Returns this panel's id
37378      * @return {String} 
37379      */
37380     getId : function(){
37381         return this.el.id;
37382     },
37383     
37384     /** 
37385      * Returns this panel's element - used by regiosn to add.
37386      * @return {Roo.Element} 
37387      */
37388     getEl : function(){
37389         return this.wrapEl || this.el;
37390     },
37391     
37392    
37393     
37394     adjustForComponents : function(width, height)
37395     {
37396         //Roo.log('adjustForComponents ');
37397         if(this.resizeEl != this.el){
37398             width -= this.el.getFrameWidth('lr');
37399             height -= this.el.getFrameWidth('tb');
37400         }
37401         if(this.toolbar){
37402             var te = this.toolbar.getEl();
37403             te.setWidth(width);
37404             height -= te.getHeight();
37405         }
37406         if(this.footer){
37407             var te = this.footer.getEl();
37408             te.setWidth(width);
37409             height -= te.getHeight();
37410         }
37411         
37412         
37413         if(this.adjustments){
37414             width += this.adjustments[0];
37415             height += this.adjustments[1];
37416         }
37417         return {"width": width, "height": height};
37418     },
37419     
37420     setSize : function(width, height){
37421         if(this.fitToFrame && !this.ignoreResize(width, height)){
37422             if(this.fitContainer && this.resizeEl != this.el){
37423                 this.el.setSize(width, height);
37424             }
37425             var size = this.adjustForComponents(width, height);
37426             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37427             this.fireEvent('resize', this, size.width, size.height);
37428         }
37429     },
37430     
37431     /**
37432      * Returns this panel's title
37433      * @return {String} 
37434      */
37435     getTitle : function(){
37436         
37437         if (typeof(this.title) != 'object') {
37438             return this.title;
37439         }
37440         
37441         var t = '';
37442         for (var k in this.title) {
37443             if (!this.title.hasOwnProperty(k)) {
37444                 continue;
37445             }
37446             
37447             if (k.indexOf('-') >= 0) {
37448                 var s = k.split('-');
37449                 for (var i = 0; i<s.length; i++) {
37450                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37451                 }
37452             } else {
37453                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37454             }
37455         }
37456         return t;
37457     },
37458     
37459     /**
37460      * Set this panel's title
37461      * @param {String} title
37462      */
37463     setTitle : function(title){
37464         this.title = title;
37465         if(this.region){
37466             this.region.updatePanelTitle(this, title);
37467         }
37468     },
37469     
37470     /**
37471      * Returns true is this panel was configured to be closable
37472      * @return {Boolean} 
37473      */
37474     isClosable : function(){
37475         return this.closable;
37476     },
37477     
37478     beforeSlide : function(){
37479         this.el.clip();
37480         this.resizeEl.clip();
37481     },
37482     
37483     afterSlide : function(){
37484         this.el.unclip();
37485         this.resizeEl.unclip();
37486     },
37487     
37488     /**
37489      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37490      *   Will fail silently if the {@link #setUrl} method has not been called.
37491      *   This does not activate the panel, just updates its content.
37492      */
37493     refresh : function(){
37494         if(this.refreshDelegate){
37495            this.loaded = false;
37496            this.refreshDelegate();
37497         }
37498     },
37499     
37500     /**
37501      * Destroys this panel
37502      */
37503     destroy : function(){
37504         this.el.removeAllListeners();
37505         var tempEl = document.createElement("span");
37506         tempEl.appendChild(this.el.dom);
37507         tempEl.innerHTML = "";
37508         this.el.remove();
37509         this.el = null;
37510     },
37511     
37512     /**
37513      * form - if the content panel contains a form - this is a reference to it.
37514      * @type {Roo.form.Form}
37515      */
37516     form : false,
37517     /**
37518      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37519      *    This contains a reference to it.
37520      * @type {Roo.View}
37521      */
37522     view : false,
37523     
37524       /**
37525      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37526      * <pre><code>
37527
37528 layout.addxtype({
37529        xtype : 'Form',
37530        items: [ .... ]
37531    }
37532 );
37533
37534 </code></pre>
37535      * @param {Object} cfg Xtype definition of item to add.
37536      */
37537     
37538     
37539     getChildContainer: function () {
37540         return this.getEl();
37541     }
37542     
37543     
37544     /*
37545         var  ret = new Roo.factory(cfg);
37546         return ret;
37547         
37548         
37549         // add form..
37550         if (cfg.xtype.match(/^Form$/)) {
37551             
37552             var el;
37553             //if (this.footer) {
37554             //    el = this.footer.container.insertSibling(false, 'before');
37555             //} else {
37556                 el = this.el.createChild();
37557             //}
37558
37559             this.form = new  Roo.form.Form(cfg);
37560             
37561             
37562             if ( this.form.allItems.length) {
37563                 this.form.render(el.dom);
37564             }
37565             return this.form;
37566         }
37567         // should only have one of theses..
37568         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37569             // views.. should not be just added - used named prop 'view''
37570             
37571             cfg.el = this.el.appendChild(document.createElement("div"));
37572             // factory?
37573             
37574             var ret = new Roo.factory(cfg);
37575              
37576              ret.render && ret.render(false, ''); // render blank..
37577             this.view = ret;
37578             return ret;
37579         }
37580         return false;
37581     }
37582     \*/
37583 });
37584  
37585 /**
37586  * @class Roo.bootstrap.panel.Grid
37587  * @extends Roo.bootstrap.panel.Content
37588  * @constructor
37589  * Create a new GridPanel.
37590  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37591  * @param {Object} config A the config object
37592   
37593  */
37594
37595
37596
37597 Roo.bootstrap.panel.Grid = function(config)
37598 {
37599     
37600       
37601     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37602         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37603
37604     config.el = this.wrapper;
37605     //this.el = this.wrapper;
37606     
37607       if (config.container) {
37608         // ctor'ed from a Border/panel.grid
37609         
37610         
37611         this.wrapper.setStyle("overflow", "hidden");
37612         this.wrapper.addClass('roo-grid-container');
37613
37614     }
37615     
37616     
37617     if(config.toolbar){
37618         var tool_el = this.wrapper.createChild();    
37619         this.toolbar = Roo.factory(config.toolbar);
37620         var ti = [];
37621         if (config.toolbar.items) {
37622             ti = config.toolbar.items ;
37623             delete config.toolbar.items ;
37624         }
37625         
37626         var nitems = [];
37627         this.toolbar.render(tool_el);
37628         for(var i =0;i < ti.length;i++) {
37629           //  Roo.log(['add child', items[i]]);
37630             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37631         }
37632         this.toolbar.items = nitems;
37633         
37634         delete config.toolbar;
37635     }
37636     
37637     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37638     config.grid.scrollBody = true;;
37639     config.grid.monitorWindowResize = false; // turn off autosizing
37640     config.grid.autoHeight = false;
37641     config.grid.autoWidth = false;
37642     
37643     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37644     
37645     if (config.background) {
37646         // render grid on panel activation (if panel background)
37647         this.on('activate', function(gp) {
37648             if (!gp.grid.rendered) {
37649                 gp.grid.render(this.wrapper);
37650                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37651             }
37652         });
37653             
37654     } else {
37655         this.grid.render(this.wrapper);
37656         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37657
37658     }
37659     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37660     // ??? needed ??? config.el = this.wrapper;
37661     
37662     
37663     
37664   
37665     // xtype created footer. - not sure if will work as we normally have to render first..
37666     if (this.footer && !this.footer.el && this.footer.xtype) {
37667         
37668         var ctr = this.grid.getView().getFooterPanel(true);
37669         this.footer.dataSource = this.grid.dataSource;
37670         this.footer = Roo.factory(this.footer, Roo);
37671         this.footer.render(ctr);
37672         
37673     }
37674     
37675     
37676     
37677     
37678      
37679 };
37680
37681 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37682     getId : function(){
37683         return this.grid.id;
37684     },
37685     
37686     /**
37687      * Returns the grid for this panel
37688      * @return {Roo.bootstrap.Table} 
37689      */
37690     getGrid : function(){
37691         return this.grid;    
37692     },
37693     
37694     setSize : function(width, height){
37695         if(!this.ignoreResize(width, height)){
37696             var grid = this.grid;
37697             var size = this.adjustForComponents(width, height);
37698             var gridel = grid.getGridEl();
37699             gridel.setSize(size.width, size.height);
37700             /*
37701             var thd = grid.getGridEl().select('thead',true).first();
37702             var tbd = grid.getGridEl().select('tbody', true).first();
37703             if (tbd) {
37704                 tbd.setSize(width, height - thd.getHeight());
37705             }
37706             */
37707             grid.autoSize();
37708         }
37709     },
37710      
37711     
37712     
37713     beforeSlide : function(){
37714         this.grid.getView().scroller.clip();
37715     },
37716     
37717     afterSlide : function(){
37718         this.grid.getView().scroller.unclip();
37719     },
37720     
37721     destroy : function(){
37722         this.grid.destroy();
37723         delete this.grid;
37724         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37725     }
37726 });
37727
37728 /**
37729  * @class Roo.bootstrap.panel.Nest
37730  * @extends Roo.bootstrap.panel.Content
37731  * @constructor
37732  * Create a new Panel, that can contain a layout.Border.
37733  * 
37734  * 
37735  * @param {Roo.BorderLayout} layout The layout for this panel
37736  * @param {String/Object} config A string to set only the title or a config object
37737  */
37738 Roo.bootstrap.panel.Nest = function(config)
37739 {
37740     // construct with only one argument..
37741     /* FIXME - implement nicer consturctors
37742     if (layout.layout) {
37743         config = layout;
37744         layout = config.layout;
37745         delete config.layout;
37746     }
37747     if (layout.xtype && !layout.getEl) {
37748         // then layout needs constructing..
37749         layout = Roo.factory(layout, Roo);
37750     }
37751     */
37752     
37753     config.el =  config.layout.getEl();
37754     
37755     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37756     
37757     config.layout.monitorWindowResize = false; // turn off autosizing
37758     this.layout = config.layout;
37759     this.layout.getEl().addClass("roo-layout-nested-layout");
37760     
37761     
37762     
37763     
37764 };
37765
37766 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37767
37768     setSize : function(width, height){
37769         if(!this.ignoreResize(width, height)){
37770             var size = this.adjustForComponents(width, height);
37771             var el = this.layout.getEl();
37772             if (size.height < 1) {
37773                 el.setWidth(size.width);   
37774             } else {
37775                 el.setSize(size.width, size.height);
37776             }
37777             var touch = el.dom.offsetWidth;
37778             this.layout.layout();
37779             // ie requires a double layout on the first pass
37780             if(Roo.isIE && !this.initialized){
37781                 this.initialized = true;
37782                 this.layout.layout();
37783             }
37784         }
37785     },
37786     
37787     // activate all subpanels if not currently active..
37788     
37789     setActiveState : function(active){
37790         this.active = active;
37791         this.setActiveClass(active);
37792         
37793         if(!active){
37794             this.fireEvent("deactivate", this);
37795             return;
37796         }
37797         
37798         this.fireEvent("activate", this);
37799         // not sure if this should happen before or after..
37800         if (!this.layout) {
37801             return; // should not happen..
37802         }
37803         var reg = false;
37804         for (var r in this.layout.regions) {
37805             reg = this.layout.getRegion(r);
37806             if (reg.getActivePanel()) {
37807                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37808                 reg.setActivePanel(reg.getActivePanel());
37809                 continue;
37810             }
37811             if (!reg.panels.length) {
37812                 continue;
37813             }
37814             reg.showPanel(reg.getPanel(0));
37815         }
37816         
37817         
37818         
37819         
37820     },
37821     
37822     /**
37823      * Returns the nested BorderLayout for this panel
37824      * @return {Roo.BorderLayout} 
37825      */
37826     getLayout : function(){
37827         return this.layout;
37828     },
37829     
37830      /**
37831      * Adds a xtype elements to the layout of the nested panel
37832      * <pre><code>
37833
37834 panel.addxtype({
37835        xtype : 'ContentPanel',
37836        region: 'west',
37837        items: [ .... ]
37838    }
37839 );
37840
37841 panel.addxtype({
37842         xtype : 'NestedLayoutPanel',
37843         region: 'west',
37844         layout: {
37845            center: { },
37846            west: { }   
37847         },
37848         items : [ ... list of content panels or nested layout panels.. ]
37849    }
37850 );
37851 </code></pre>
37852      * @param {Object} cfg Xtype definition of item to add.
37853      */
37854     addxtype : function(cfg) {
37855         return this.layout.addxtype(cfg);
37856     
37857     }
37858 });        /*
37859  * Based on:
37860  * Ext JS Library 1.1.1
37861  * Copyright(c) 2006-2007, Ext JS, LLC.
37862  *
37863  * Originally Released Under LGPL - original licence link has changed is not relivant.
37864  *
37865  * Fork - LGPL
37866  * <script type="text/javascript">
37867  */
37868 /**
37869  * @class Roo.TabPanel
37870  * @extends Roo.util.Observable
37871  * A lightweight tab container.
37872  * <br><br>
37873  * Usage:
37874  * <pre><code>
37875 // basic tabs 1, built from existing content
37876 var tabs = new Roo.TabPanel("tabs1");
37877 tabs.addTab("script", "View Script");
37878 tabs.addTab("markup", "View Markup");
37879 tabs.activate("script");
37880
37881 // more advanced tabs, built from javascript
37882 var jtabs = new Roo.TabPanel("jtabs");
37883 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37884
37885 // set up the UpdateManager
37886 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37887 var updater = tab2.getUpdateManager();
37888 updater.setDefaultUrl("ajax1.htm");
37889 tab2.on('activate', updater.refresh, updater, true);
37890
37891 // Use setUrl for Ajax loading
37892 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37893 tab3.setUrl("ajax2.htm", null, true);
37894
37895 // Disabled tab
37896 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37897 tab4.disable();
37898
37899 jtabs.activate("jtabs-1");
37900  * </code></pre>
37901  * @constructor
37902  * Create a new TabPanel.
37903  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37904  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37905  */
37906 Roo.bootstrap.panel.Tabs = function(config){
37907     /**
37908     * The container element for this TabPanel.
37909     * @type Roo.Element
37910     */
37911     this.el = Roo.get(config.el);
37912     delete config.el;
37913     if(config){
37914         if(typeof config == "boolean"){
37915             this.tabPosition = config ? "bottom" : "top";
37916         }else{
37917             Roo.apply(this, config);
37918         }
37919     }
37920     
37921     if(this.tabPosition == "bottom"){
37922         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37923         this.el.addClass("roo-tabs-bottom");
37924     }
37925     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37926     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37927     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37928     if(Roo.isIE){
37929         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37930     }
37931     if(this.tabPosition != "bottom"){
37932         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37933          * @type Roo.Element
37934          */
37935         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37936         this.el.addClass("roo-tabs-top");
37937     }
37938     this.items = [];
37939
37940     this.bodyEl.setStyle("position", "relative");
37941
37942     this.active = null;
37943     this.activateDelegate = this.activate.createDelegate(this);
37944
37945     this.addEvents({
37946         /**
37947          * @event tabchange
37948          * Fires when the active tab changes
37949          * @param {Roo.TabPanel} this
37950          * @param {Roo.TabPanelItem} activePanel The new active tab
37951          */
37952         "tabchange": true,
37953         /**
37954          * @event beforetabchange
37955          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37956          * @param {Roo.TabPanel} this
37957          * @param {Object} e Set cancel to true on this object to cancel the tab change
37958          * @param {Roo.TabPanelItem} tab The tab being changed to
37959          */
37960         "beforetabchange" : true
37961     });
37962
37963     Roo.EventManager.onWindowResize(this.onResize, this);
37964     this.cpad = this.el.getPadding("lr");
37965     this.hiddenCount = 0;
37966
37967
37968     // toolbar on the tabbar support...
37969     if (this.toolbar) {
37970         alert("no toolbar support yet");
37971         this.toolbar  = false;
37972         /*
37973         var tcfg = this.toolbar;
37974         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37975         this.toolbar = new Roo.Toolbar(tcfg);
37976         if (Roo.isSafari) {
37977             var tbl = tcfg.container.child('table', true);
37978             tbl.setAttribute('width', '100%');
37979         }
37980         */
37981         
37982     }
37983    
37984
37985
37986     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37987 };
37988
37989 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37990     /*
37991      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37992      */
37993     tabPosition : "top",
37994     /*
37995      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37996      */
37997     currentTabWidth : 0,
37998     /*
37999      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38000      */
38001     minTabWidth : 40,
38002     /*
38003      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38004      */
38005     maxTabWidth : 250,
38006     /*
38007      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38008      */
38009     preferredTabWidth : 175,
38010     /*
38011      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38012      */
38013     resizeTabs : false,
38014     /*
38015      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38016      */
38017     monitorResize : true,
38018     /*
38019      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38020      */
38021     toolbar : false,
38022
38023     /**
38024      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38025      * @param {String} id The id of the div to use <b>or create</b>
38026      * @param {String} text The text for the tab
38027      * @param {String} content (optional) Content to put in the TabPanelItem body
38028      * @param {Boolean} closable (optional) True to create a close icon on the tab
38029      * @return {Roo.TabPanelItem} The created TabPanelItem
38030      */
38031     addTab : function(id, text, content, closable, tpl)
38032     {
38033         var item = new Roo.bootstrap.panel.TabItem({
38034             panel: this,
38035             id : id,
38036             text : text,
38037             closable : closable,
38038             tpl : tpl
38039         });
38040         this.addTabItem(item);
38041         if(content){
38042             item.setContent(content);
38043         }
38044         return item;
38045     },
38046
38047     /**
38048      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38049      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38050      * @return {Roo.TabPanelItem}
38051      */
38052     getTab : function(id){
38053         return this.items[id];
38054     },
38055
38056     /**
38057      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38058      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38059      */
38060     hideTab : function(id){
38061         var t = this.items[id];
38062         if(!t.isHidden()){
38063            t.setHidden(true);
38064            this.hiddenCount++;
38065            this.autoSizeTabs();
38066         }
38067     },
38068
38069     /**
38070      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38071      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38072      */
38073     unhideTab : function(id){
38074         var t = this.items[id];
38075         if(t.isHidden()){
38076            t.setHidden(false);
38077            this.hiddenCount--;
38078            this.autoSizeTabs();
38079         }
38080     },
38081
38082     /**
38083      * Adds an existing {@link Roo.TabPanelItem}.
38084      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38085      */
38086     addTabItem : function(item){
38087         this.items[item.id] = item;
38088         this.items.push(item);
38089       //  if(this.resizeTabs){
38090     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38091   //         this.autoSizeTabs();
38092 //        }else{
38093 //            item.autoSize();
38094        // }
38095     },
38096
38097     /**
38098      * Removes a {@link Roo.TabPanelItem}.
38099      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38100      */
38101     removeTab : function(id){
38102         var items = this.items;
38103         var tab = items[id];
38104         if(!tab) { return; }
38105         var index = items.indexOf(tab);
38106         if(this.active == tab && items.length > 1){
38107             var newTab = this.getNextAvailable(index);
38108             if(newTab) {
38109                 newTab.activate();
38110             }
38111         }
38112         this.stripEl.dom.removeChild(tab.pnode.dom);
38113         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38114             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38115         }
38116         items.splice(index, 1);
38117         delete this.items[tab.id];
38118         tab.fireEvent("close", tab);
38119         tab.purgeListeners();
38120         this.autoSizeTabs();
38121     },
38122
38123     getNextAvailable : function(start){
38124         var items = this.items;
38125         var index = start;
38126         // look for a next tab that will slide over to
38127         // replace the one being removed
38128         while(index < items.length){
38129             var item = items[++index];
38130             if(item && !item.isHidden()){
38131                 return item;
38132             }
38133         }
38134         // if one isn't found select the previous tab (on the left)
38135         index = start;
38136         while(index >= 0){
38137             var item = items[--index];
38138             if(item && !item.isHidden()){
38139                 return item;
38140             }
38141         }
38142         return null;
38143     },
38144
38145     /**
38146      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38147      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38148      */
38149     disableTab : function(id){
38150         var tab = this.items[id];
38151         if(tab && this.active != tab){
38152             tab.disable();
38153         }
38154     },
38155
38156     /**
38157      * Enables a {@link Roo.TabPanelItem} that is disabled.
38158      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38159      */
38160     enableTab : function(id){
38161         var tab = this.items[id];
38162         tab.enable();
38163     },
38164
38165     /**
38166      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38167      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38168      * @return {Roo.TabPanelItem} The TabPanelItem.
38169      */
38170     activate : function(id){
38171         var tab = this.items[id];
38172         if(!tab){
38173             return null;
38174         }
38175         if(tab == this.active || tab.disabled){
38176             return tab;
38177         }
38178         var e = {};
38179         this.fireEvent("beforetabchange", this, e, tab);
38180         if(e.cancel !== true && !tab.disabled){
38181             if(this.active){
38182                 this.active.hide();
38183             }
38184             this.active = this.items[id];
38185             this.active.show();
38186             this.fireEvent("tabchange", this, this.active);
38187         }
38188         return tab;
38189     },
38190
38191     /**
38192      * Gets the active {@link Roo.TabPanelItem}.
38193      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38194      */
38195     getActiveTab : function(){
38196         return this.active;
38197     },
38198
38199     /**
38200      * Updates the tab body element to fit the height of the container element
38201      * for overflow scrolling
38202      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38203      */
38204     syncHeight : function(targetHeight){
38205         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38206         var bm = this.bodyEl.getMargins();
38207         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38208         this.bodyEl.setHeight(newHeight);
38209         return newHeight;
38210     },
38211
38212     onResize : function(){
38213         if(this.monitorResize){
38214             this.autoSizeTabs();
38215         }
38216     },
38217
38218     /**
38219      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38220      */
38221     beginUpdate : function(){
38222         this.updating = true;
38223     },
38224
38225     /**
38226      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38227      */
38228     endUpdate : function(){
38229         this.updating = false;
38230         this.autoSizeTabs();
38231     },
38232
38233     /**
38234      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38235      */
38236     autoSizeTabs : function(){
38237         var count = this.items.length;
38238         var vcount = count - this.hiddenCount;
38239         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38240             return;
38241         }
38242         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38243         var availWidth = Math.floor(w / vcount);
38244         var b = this.stripBody;
38245         if(b.getWidth() > w){
38246             var tabs = this.items;
38247             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38248             if(availWidth < this.minTabWidth){
38249                 /*if(!this.sleft){    // incomplete scrolling code
38250                     this.createScrollButtons();
38251                 }
38252                 this.showScroll();
38253                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38254             }
38255         }else{
38256             if(this.currentTabWidth < this.preferredTabWidth){
38257                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38258             }
38259         }
38260     },
38261
38262     /**
38263      * Returns the number of tabs in this TabPanel.
38264      * @return {Number}
38265      */
38266      getCount : function(){
38267          return this.items.length;
38268      },
38269
38270     /**
38271      * Resizes all the tabs to the passed width
38272      * @param {Number} The new width
38273      */
38274     setTabWidth : function(width){
38275         this.currentTabWidth = width;
38276         for(var i = 0, len = this.items.length; i < len; i++) {
38277                 if(!this.items[i].isHidden()) {
38278                 this.items[i].setWidth(width);
38279             }
38280         }
38281     },
38282
38283     /**
38284      * Destroys this TabPanel
38285      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38286      */
38287     destroy : function(removeEl){
38288         Roo.EventManager.removeResizeListener(this.onResize, this);
38289         for(var i = 0, len = this.items.length; i < len; i++){
38290             this.items[i].purgeListeners();
38291         }
38292         if(removeEl === true){
38293             this.el.update("");
38294             this.el.remove();
38295         }
38296     },
38297     
38298     createStrip : function(container)
38299     {
38300         var strip = document.createElement("nav");
38301         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38302         container.appendChild(strip);
38303         return strip;
38304     },
38305     
38306     createStripList : function(strip)
38307     {
38308         // div wrapper for retard IE
38309         // returns the "tr" element.
38310         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38311         //'<div class="x-tabs-strip-wrap">'+
38312           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38313           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38314         return strip.firstChild; //.firstChild.firstChild.firstChild;
38315     },
38316     createBody : function(container)
38317     {
38318         var body = document.createElement("div");
38319         Roo.id(body, "tab-body");
38320         //Roo.fly(body).addClass("x-tabs-body");
38321         Roo.fly(body).addClass("tab-content");
38322         container.appendChild(body);
38323         return body;
38324     },
38325     createItemBody :function(bodyEl, id){
38326         var body = Roo.getDom(id);
38327         if(!body){
38328             body = document.createElement("div");
38329             body.id = id;
38330         }
38331         //Roo.fly(body).addClass("x-tabs-item-body");
38332         Roo.fly(body).addClass("tab-pane");
38333          bodyEl.insertBefore(body, bodyEl.firstChild);
38334         return body;
38335     },
38336     /** @private */
38337     createStripElements :  function(stripEl, text, closable, tpl)
38338     {
38339         var td = document.createElement("li"); // was td..
38340         
38341         
38342         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38343         
38344         
38345         stripEl.appendChild(td);
38346         /*if(closable){
38347             td.className = "x-tabs-closable";
38348             if(!this.closeTpl){
38349                 this.closeTpl = new Roo.Template(
38350                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38351                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38352                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38353                 );
38354             }
38355             var el = this.closeTpl.overwrite(td, {"text": text});
38356             var close = el.getElementsByTagName("div")[0];
38357             var inner = el.getElementsByTagName("em")[0];
38358             return {"el": el, "close": close, "inner": inner};
38359         } else {
38360         */
38361         // not sure what this is..
38362 //            if(!this.tabTpl){
38363                 //this.tabTpl = new Roo.Template(
38364                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38365                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38366                 //);
38367 //                this.tabTpl = new Roo.Template(
38368 //                   '<a href="#">' +
38369 //                   '<span unselectable="on"' +
38370 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38371 //                            ' >{text}</span></a>'
38372 //                );
38373 //                
38374 //            }
38375
38376
38377             var template = tpl || this.tabTpl || false;
38378             
38379             if(!template){
38380                 
38381                 template = new Roo.Template(
38382                    '<a href="#">' +
38383                    '<span unselectable="on"' +
38384                             (this.disableTooltips ? '' : ' title="{text}"') +
38385                             ' >{text}</span></a>'
38386                 );
38387             }
38388             
38389             switch (typeof(template)) {
38390                 case 'object' :
38391                     break;
38392                 case 'string' :
38393                     template = new Roo.Template(template);
38394                     break;
38395                 default :
38396                     break;
38397             }
38398             
38399             var el = template.overwrite(td, {"text": text});
38400             
38401             var inner = el.getElementsByTagName("span")[0];
38402             
38403             return {"el": el, "inner": inner};
38404             
38405     }
38406         
38407     
38408 });
38409
38410 /**
38411  * @class Roo.TabPanelItem
38412  * @extends Roo.util.Observable
38413  * Represents an individual item (tab plus body) in a TabPanel.
38414  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38415  * @param {String} id The id of this TabPanelItem
38416  * @param {String} text The text for the tab of this TabPanelItem
38417  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38418  */
38419 Roo.bootstrap.panel.TabItem = function(config){
38420     /**
38421      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38422      * @type Roo.TabPanel
38423      */
38424     this.tabPanel = config.panel;
38425     /**
38426      * The id for this TabPanelItem
38427      * @type String
38428      */
38429     this.id = config.id;
38430     /** @private */
38431     this.disabled = false;
38432     /** @private */
38433     this.text = config.text;
38434     /** @private */
38435     this.loaded = false;
38436     this.closable = config.closable;
38437
38438     /**
38439      * The body element for this TabPanelItem.
38440      * @type Roo.Element
38441      */
38442     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38443     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38444     this.bodyEl.setStyle("display", "block");
38445     this.bodyEl.setStyle("zoom", "1");
38446     //this.hideAction();
38447
38448     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38449     /** @private */
38450     this.el = Roo.get(els.el);
38451     this.inner = Roo.get(els.inner, true);
38452     this.textEl = Roo.get(this.el.dom.firstChild, true);
38453     this.pnode = Roo.get(els.el.parentNode, true);
38454 //    this.el.on("mousedown", this.onTabMouseDown, this);
38455     this.el.on("click", this.onTabClick, this);
38456     /** @private */
38457     if(config.closable){
38458         var c = Roo.get(els.close, true);
38459         c.dom.title = this.closeText;
38460         c.addClassOnOver("close-over");
38461         c.on("click", this.closeClick, this);
38462      }
38463
38464     this.addEvents({
38465          /**
38466          * @event activate
38467          * Fires when this tab becomes the active tab.
38468          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38469          * @param {Roo.TabPanelItem} this
38470          */
38471         "activate": true,
38472         /**
38473          * @event beforeclose
38474          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38475          * @param {Roo.TabPanelItem} this
38476          * @param {Object} e Set cancel to true on this object to cancel the close.
38477          */
38478         "beforeclose": true,
38479         /**
38480          * @event close
38481          * Fires when this tab is closed.
38482          * @param {Roo.TabPanelItem} this
38483          */
38484          "close": true,
38485         /**
38486          * @event deactivate
38487          * Fires when this tab is no longer the active tab.
38488          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38489          * @param {Roo.TabPanelItem} this
38490          */
38491          "deactivate" : true
38492     });
38493     this.hidden = false;
38494
38495     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38496 };
38497
38498 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38499            {
38500     purgeListeners : function(){
38501        Roo.util.Observable.prototype.purgeListeners.call(this);
38502        this.el.removeAllListeners();
38503     },
38504     /**
38505      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38506      */
38507     show : function(){
38508         this.pnode.addClass("active");
38509         this.showAction();
38510         if(Roo.isOpera){
38511             this.tabPanel.stripWrap.repaint();
38512         }
38513         this.fireEvent("activate", this.tabPanel, this);
38514     },
38515
38516     /**
38517      * Returns true if this tab is the active tab.
38518      * @return {Boolean}
38519      */
38520     isActive : function(){
38521         return this.tabPanel.getActiveTab() == this;
38522     },
38523
38524     /**
38525      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38526      */
38527     hide : function(){
38528         this.pnode.removeClass("active");
38529         this.hideAction();
38530         this.fireEvent("deactivate", this.tabPanel, this);
38531     },
38532
38533     hideAction : function(){
38534         this.bodyEl.hide();
38535         this.bodyEl.setStyle("position", "absolute");
38536         this.bodyEl.setLeft("-20000px");
38537         this.bodyEl.setTop("-20000px");
38538     },
38539
38540     showAction : function(){
38541         this.bodyEl.setStyle("position", "relative");
38542         this.bodyEl.setTop("");
38543         this.bodyEl.setLeft("");
38544         this.bodyEl.show();
38545     },
38546
38547     /**
38548      * Set the tooltip for the tab.
38549      * @param {String} tooltip The tab's tooltip
38550      */
38551     setTooltip : function(text){
38552         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38553             this.textEl.dom.qtip = text;
38554             this.textEl.dom.removeAttribute('title');
38555         }else{
38556             this.textEl.dom.title = text;
38557         }
38558     },
38559
38560     onTabClick : function(e){
38561         e.preventDefault();
38562         this.tabPanel.activate(this.id);
38563     },
38564
38565     onTabMouseDown : function(e){
38566         e.preventDefault();
38567         this.tabPanel.activate(this.id);
38568     },
38569 /*
38570     getWidth : function(){
38571         return this.inner.getWidth();
38572     },
38573
38574     setWidth : function(width){
38575         var iwidth = width - this.pnode.getPadding("lr");
38576         this.inner.setWidth(iwidth);
38577         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38578         this.pnode.setWidth(width);
38579     },
38580 */
38581     /**
38582      * Show or hide the tab
38583      * @param {Boolean} hidden True to hide or false to show.
38584      */
38585     setHidden : function(hidden){
38586         this.hidden = hidden;
38587         this.pnode.setStyle("display", hidden ? "none" : "");
38588     },
38589
38590     /**
38591      * Returns true if this tab is "hidden"
38592      * @return {Boolean}
38593      */
38594     isHidden : function(){
38595         return this.hidden;
38596     },
38597
38598     /**
38599      * Returns the text for this tab
38600      * @return {String}
38601      */
38602     getText : function(){
38603         return this.text;
38604     },
38605     /*
38606     autoSize : function(){
38607         //this.el.beginMeasure();
38608         this.textEl.setWidth(1);
38609         /*
38610          *  #2804 [new] Tabs in Roojs
38611          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38612          */
38613         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38614         //this.el.endMeasure();
38615     //},
38616
38617     /**
38618      * Sets the text for the tab (Note: this also sets the tooltip text)
38619      * @param {String} text The tab's text and tooltip
38620      */
38621     setText : function(text){
38622         this.text = text;
38623         this.textEl.update(text);
38624         this.setTooltip(text);
38625         //if(!this.tabPanel.resizeTabs){
38626         //    this.autoSize();
38627         //}
38628     },
38629     /**
38630      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38631      */
38632     activate : function(){
38633         this.tabPanel.activate(this.id);
38634     },
38635
38636     /**
38637      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38638      */
38639     disable : function(){
38640         if(this.tabPanel.active != this){
38641             this.disabled = true;
38642             this.pnode.addClass("disabled");
38643         }
38644     },
38645
38646     /**
38647      * Enables this TabPanelItem if it was previously disabled.
38648      */
38649     enable : function(){
38650         this.disabled = false;
38651         this.pnode.removeClass("disabled");
38652     },
38653
38654     /**
38655      * Sets the content for this TabPanelItem.
38656      * @param {String} content The content
38657      * @param {Boolean} loadScripts true to look for and load scripts
38658      */
38659     setContent : function(content, loadScripts){
38660         this.bodyEl.update(content, loadScripts);
38661     },
38662
38663     /**
38664      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38665      * @return {Roo.UpdateManager} The UpdateManager
38666      */
38667     getUpdateManager : function(){
38668         return this.bodyEl.getUpdateManager();
38669     },
38670
38671     /**
38672      * Set a URL to be used to load the content for this TabPanelItem.
38673      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38674      * @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)
38675      * @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)
38676      * @return {Roo.UpdateManager} The UpdateManager
38677      */
38678     setUrl : function(url, params, loadOnce){
38679         if(this.refreshDelegate){
38680             this.un('activate', this.refreshDelegate);
38681         }
38682         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38683         this.on("activate", this.refreshDelegate);
38684         return this.bodyEl.getUpdateManager();
38685     },
38686
38687     /** @private */
38688     _handleRefresh : function(url, params, loadOnce){
38689         if(!loadOnce || !this.loaded){
38690             var updater = this.bodyEl.getUpdateManager();
38691             updater.update(url, params, this._setLoaded.createDelegate(this));
38692         }
38693     },
38694
38695     /**
38696      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38697      *   Will fail silently if the setUrl method has not been called.
38698      *   This does not activate the panel, just updates its content.
38699      */
38700     refresh : function(){
38701         if(this.refreshDelegate){
38702            this.loaded = false;
38703            this.refreshDelegate();
38704         }
38705     },
38706
38707     /** @private */
38708     _setLoaded : function(){
38709         this.loaded = true;
38710     },
38711
38712     /** @private */
38713     closeClick : function(e){
38714         var o = {};
38715         e.stopEvent();
38716         this.fireEvent("beforeclose", this, o);
38717         if(o.cancel !== true){
38718             this.tabPanel.removeTab(this.id);
38719         }
38720     },
38721     /**
38722      * The text displayed in the tooltip for the close icon.
38723      * @type String
38724      */
38725     closeText : "Close this tab"
38726 });
38727 /**
38728 *    This script refer to:
38729 *    Title: International Telephone Input
38730 *    Author: Jack O'Connor
38731 *    Code version:  v12.1.12
38732 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38733 **/
38734
38735 Roo.bootstrap.PhoneInputData = function() {
38736     var d = [
38737       [
38738         "Afghanistan (‫افغانستان‬‎)",
38739         "af",
38740         "93"
38741       ],
38742       [
38743         "Albania (Shqipëri)",
38744         "al",
38745         "355"
38746       ],
38747       [
38748         "Algeria (‫الجزائر‬‎)",
38749         "dz",
38750         "213"
38751       ],
38752       [
38753         "American Samoa",
38754         "as",
38755         "1684"
38756       ],
38757       [
38758         "Andorra",
38759         "ad",
38760         "376"
38761       ],
38762       [
38763         "Angola",
38764         "ao",
38765         "244"
38766       ],
38767       [
38768         "Anguilla",
38769         "ai",
38770         "1264"
38771       ],
38772       [
38773         "Antigua and Barbuda",
38774         "ag",
38775         "1268"
38776       ],
38777       [
38778         "Argentina",
38779         "ar",
38780         "54"
38781       ],
38782       [
38783         "Armenia (Հայաստան)",
38784         "am",
38785         "374"
38786       ],
38787       [
38788         "Aruba",
38789         "aw",
38790         "297"
38791       ],
38792       [
38793         "Australia",
38794         "au",
38795         "61",
38796         0
38797       ],
38798       [
38799         "Austria (Österreich)",
38800         "at",
38801         "43"
38802       ],
38803       [
38804         "Azerbaijan (Azərbaycan)",
38805         "az",
38806         "994"
38807       ],
38808       [
38809         "Bahamas",
38810         "bs",
38811         "1242"
38812       ],
38813       [
38814         "Bahrain (‫البحرين‬‎)",
38815         "bh",
38816         "973"
38817       ],
38818       [
38819         "Bangladesh (বাংলাদেশ)",
38820         "bd",
38821         "880"
38822       ],
38823       [
38824         "Barbados",
38825         "bb",
38826         "1246"
38827       ],
38828       [
38829         "Belarus (Беларусь)",
38830         "by",
38831         "375"
38832       ],
38833       [
38834         "Belgium (België)",
38835         "be",
38836         "32"
38837       ],
38838       [
38839         "Belize",
38840         "bz",
38841         "501"
38842       ],
38843       [
38844         "Benin (Bénin)",
38845         "bj",
38846         "229"
38847       ],
38848       [
38849         "Bermuda",
38850         "bm",
38851         "1441"
38852       ],
38853       [
38854         "Bhutan (འབྲུག)",
38855         "bt",
38856         "975"
38857       ],
38858       [
38859         "Bolivia",
38860         "bo",
38861         "591"
38862       ],
38863       [
38864         "Bosnia and Herzegovina (Босна и Херцеговина)",
38865         "ba",
38866         "387"
38867       ],
38868       [
38869         "Botswana",
38870         "bw",
38871         "267"
38872       ],
38873       [
38874         "Brazil (Brasil)",
38875         "br",
38876         "55"
38877       ],
38878       [
38879         "British Indian Ocean Territory",
38880         "io",
38881         "246"
38882       ],
38883       [
38884         "British Virgin Islands",
38885         "vg",
38886         "1284"
38887       ],
38888       [
38889         "Brunei",
38890         "bn",
38891         "673"
38892       ],
38893       [
38894         "Bulgaria (България)",
38895         "bg",
38896         "359"
38897       ],
38898       [
38899         "Burkina Faso",
38900         "bf",
38901         "226"
38902       ],
38903       [
38904         "Burundi (Uburundi)",
38905         "bi",
38906         "257"
38907       ],
38908       [
38909         "Cambodia (កម្ពុជា)",
38910         "kh",
38911         "855"
38912       ],
38913       [
38914         "Cameroon (Cameroun)",
38915         "cm",
38916         "237"
38917       ],
38918       [
38919         "Canada",
38920         "ca",
38921         "1",
38922         1,
38923         ["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"]
38924       ],
38925       [
38926         "Cape Verde (Kabu Verdi)",
38927         "cv",
38928         "238"
38929       ],
38930       [
38931         "Caribbean Netherlands",
38932         "bq",
38933         "599",
38934         1
38935       ],
38936       [
38937         "Cayman Islands",
38938         "ky",
38939         "1345"
38940       ],
38941       [
38942         "Central African Republic (République centrafricaine)",
38943         "cf",
38944         "236"
38945       ],
38946       [
38947         "Chad (Tchad)",
38948         "td",
38949         "235"
38950       ],
38951       [
38952         "Chile",
38953         "cl",
38954         "56"
38955       ],
38956       [
38957         "China (中国)",
38958         "cn",
38959         "86"
38960       ],
38961       [
38962         "Christmas Island",
38963         "cx",
38964         "61",
38965         2
38966       ],
38967       [
38968         "Cocos (Keeling) Islands",
38969         "cc",
38970         "61",
38971         1
38972       ],
38973       [
38974         "Colombia",
38975         "co",
38976         "57"
38977       ],
38978       [
38979         "Comoros (‫جزر القمر‬‎)",
38980         "km",
38981         "269"
38982       ],
38983       [
38984         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38985         "cd",
38986         "243"
38987       ],
38988       [
38989         "Congo (Republic) (Congo-Brazzaville)",
38990         "cg",
38991         "242"
38992       ],
38993       [
38994         "Cook Islands",
38995         "ck",
38996         "682"
38997       ],
38998       [
38999         "Costa Rica",
39000         "cr",
39001         "506"
39002       ],
39003       [
39004         "Côte d’Ivoire",
39005         "ci",
39006         "225"
39007       ],
39008       [
39009         "Croatia (Hrvatska)",
39010         "hr",
39011         "385"
39012       ],
39013       [
39014         "Cuba",
39015         "cu",
39016         "53"
39017       ],
39018       [
39019         "Curaçao",
39020         "cw",
39021         "599",
39022         0
39023       ],
39024       [
39025         "Cyprus (Κύπρος)",
39026         "cy",
39027         "357"
39028       ],
39029       [
39030         "Czech Republic (Česká republika)",
39031         "cz",
39032         "420"
39033       ],
39034       [
39035         "Denmark (Danmark)",
39036         "dk",
39037         "45"
39038       ],
39039       [
39040         "Djibouti",
39041         "dj",
39042         "253"
39043       ],
39044       [
39045         "Dominica",
39046         "dm",
39047         "1767"
39048       ],
39049       [
39050         "Dominican Republic (República Dominicana)",
39051         "do",
39052         "1",
39053         2,
39054         ["809", "829", "849"]
39055       ],
39056       [
39057         "Ecuador",
39058         "ec",
39059         "593"
39060       ],
39061       [
39062         "Egypt (‫مصر‬‎)",
39063         "eg",
39064         "20"
39065       ],
39066       [
39067         "El Salvador",
39068         "sv",
39069         "503"
39070       ],
39071       [
39072         "Equatorial Guinea (Guinea Ecuatorial)",
39073         "gq",
39074         "240"
39075       ],
39076       [
39077         "Eritrea",
39078         "er",
39079         "291"
39080       ],
39081       [
39082         "Estonia (Eesti)",
39083         "ee",
39084         "372"
39085       ],
39086       [
39087         "Ethiopia",
39088         "et",
39089         "251"
39090       ],
39091       [
39092         "Falkland Islands (Islas Malvinas)",
39093         "fk",
39094         "500"
39095       ],
39096       [
39097         "Faroe Islands (Føroyar)",
39098         "fo",
39099         "298"
39100       ],
39101       [
39102         "Fiji",
39103         "fj",
39104         "679"
39105       ],
39106       [
39107         "Finland (Suomi)",
39108         "fi",
39109         "358",
39110         0
39111       ],
39112       [
39113         "France",
39114         "fr",
39115         "33"
39116       ],
39117       [
39118         "French Guiana (Guyane française)",
39119         "gf",
39120         "594"
39121       ],
39122       [
39123         "French Polynesia (Polynésie française)",
39124         "pf",
39125         "689"
39126       ],
39127       [
39128         "Gabon",
39129         "ga",
39130         "241"
39131       ],
39132       [
39133         "Gambia",
39134         "gm",
39135         "220"
39136       ],
39137       [
39138         "Georgia (საქართველო)",
39139         "ge",
39140         "995"
39141       ],
39142       [
39143         "Germany (Deutschland)",
39144         "de",
39145         "49"
39146       ],
39147       [
39148         "Ghana (Gaana)",
39149         "gh",
39150         "233"
39151       ],
39152       [
39153         "Gibraltar",
39154         "gi",
39155         "350"
39156       ],
39157       [
39158         "Greece (Ελλάδα)",
39159         "gr",
39160         "30"
39161       ],
39162       [
39163         "Greenland (Kalaallit Nunaat)",
39164         "gl",
39165         "299"
39166       ],
39167       [
39168         "Grenada",
39169         "gd",
39170         "1473"
39171       ],
39172       [
39173         "Guadeloupe",
39174         "gp",
39175         "590",
39176         0
39177       ],
39178       [
39179         "Guam",
39180         "gu",
39181         "1671"
39182       ],
39183       [
39184         "Guatemala",
39185         "gt",
39186         "502"
39187       ],
39188       [
39189         "Guernsey",
39190         "gg",
39191         "44",
39192         1
39193       ],
39194       [
39195         "Guinea (Guinée)",
39196         "gn",
39197         "224"
39198       ],
39199       [
39200         "Guinea-Bissau (Guiné Bissau)",
39201         "gw",
39202         "245"
39203       ],
39204       [
39205         "Guyana",
39206         "gy",
39207         "592"
39208       ],
39209       [
39210         "Haiti",
39211         "ht",
39212         "509"
39213       ],
39214       [
39215         "Honduras",
39216         "hn",
39217         "504"
39218       ],
39219       [
39220         "Hong Kong (香港)",
39221         "hk",
39222         "852"
39223       ],
39224       [
39225         "Hungary (Magyarország)",
39226         "hu",
39227         "36"
39228       ],
39229       [
39230         "Iceland (Ísland)",
39231         "is",
39232         "354"
39233       ],
39234       [
39235         "India (भारत)",
39236         "in",
39237         "91"
39238       ],
39239       [
39240         "Indonesia",
39241         "id",
39242         "62"
39243       ],
39244       [
39245         "Iran (‫ایران‬‎)",
39246         "ir",
39247         "98"
39248       ],
39249       [
39250         "Iraq (‫العراق‬‎)",
39251         "iq",
39252         "964"
39253       ],
39254       [
39255         "Ireland",
39256         "ie",
39257         "353"
39258       ],
39259       [
39260         "Isle of Man",
39261         "im",
39262         "44",
39263         2
39264       ],
39265       [
39266         "Israel (‫ישראל‬‎)",
39267         "il",
39268         "972"
39269       ],
39270       [
39271         "Italy (Italia)",
39272         "it",
39273         "39",
39274         0
39275       ],
39276       [
39277         "Jamaica",
39278         "jm",
39279         "1876"
39280       ],
39281       [
39282         "Japan (日本)",
39283         "jp",
39284         "81"
39285       ],
39286       [
39287         "Jersey",
39288         "je",
39289         "44",
39290         3
39291       ],
39292       [
39293         "Jordan (‫الأردن‬‎)",
39294         "jo",
39295         "962"
39296       ],
39297       [
39298         "Kazakhstan (Казахстан)",
39299         "kz",
39300         "7",
39301         1
39302       ],
39303       [
39304         "Kenya",
39305         "ke",
39306         "254"
39307       ],
39308       [
39309         "Kiribati",
39310         "ki",
39311         "686"
39312       ],
39313       [
39314         "Kosovo",
39315         "xk",
39316         "383"
39317       ],
39318       [
39319         "Kuwait (‫الكويت‬‎)",
39320         "kw",
39321         "965"
39322       ],
39323       [
39324         "Kyrgyzstan (Кыргызстан)",
39325         "kg",
39326         "996"
39327       ],
39328       [
39329         "Laos (ລາວ)",
39330         "la",
39331         "856"
39332       ],
39333       [
39334         "Latvia (Latvija)",
39335         "lv",
39336         "371"
39337       ],
39338       [
39339         "Lebanon (‫لبنان‬‎)",
39340         "lb",
39341         "961"
39342       ],
39343       [
39344         "Lesotho",
39345         "ls",
39346         "266"
39347       ],
39348       [
39349         "Liberia",
39350         "lr",
39351         "231"
39352       ],
39353       [
39354         "Libya (‫ليبيا‬‎)",
39355         "ly",
39356         "218"
39357       ],
39358       [
39359         "Liechtenstein",
39360         "li",
39361         "423"
39362       ],
39363       [
39364         "Lithuania (Lietuva)",
39365         "lt",
39366         "370"
39367       ],
39368       [
39369         "Luxembourg",
39370         "lu",
39371         "352"
39372       ],
39373       [
39374         "Macau (澳門)",
39375         "mo",
39376         "853"
39377       ],
39378       [
39379         "Macedonia (FYROM) (Македонија)",
39380         "mk",
39381         "389"
39382       ],
39383       [
39384         "Madagascar (Madagasikara)",
39385         "mg",
39386         "261"
39387       ],
39388       [
39389         "Malawi",
39390         "mw",
39391         "265"
39392       ],
39393       [
39394         "Malaysia",
39395         "my",
39396         "60"
39397       ],
39398       [
39399         "Maldives",
39400         "mv",
39401         "960"
39402       ],
39403       [
39404         "Mali",
39405         "ml",
39406         "223"
39407       ],
39408       [
39409         "Malta",
39410         "mt",
39411         "356"
39412       ],
39413       [
39414         "Marshall Islands",
39415         "mh",
39416         "692"
39417       ],
39418       [
39419         "Martinique",
39420         "mq",
39421         "596"
39422       ],
39423       [
39424         "Mauritania (‫موريتانيا‬‎)",
39425         "mr",
39426         "222"
39427       ],
39428       [
39429         "Mauritius (Moris)",
39430         "mu",
39431         "230"
39432       ],
39433       [
39434         "Mayotte",
39435         "yt",
39436         "262",
39437         1
39438       ],
39439       [
39440         "Mexico (México)",
39441         "mx",
39442         "52"
39443       ],
39444       [
39445         "Micronesia",
39446         "fm",
39447         "691"
39448       ],
39449       [
39450         "Moldova (Republica Moldova)",
39451         "md",
39452         "373"
39453       ],
39454       [
39455         "Monaco",
39456         "mc",
39457         "377"
39458       ],
39459       [
39460         "Mongolia (Монгол)",
39461         "mn",
39462         "976"
39463       ],
39464       [
39465         "Montenegro (Crna Gora)",
39466         "me",
39467         "382"
39468       ],
39469       [
39470         "Montserrat",
39471         "ms",
39472         "1664"
39473       ],
39474       [
39475         "Morocco (‫المغرب‬‎)",
39476         "ma",
39477         "212",
39478         0
39479       ],
39480       [
39481         "Mozambique (Moçambique)",
39482         "mz",
39483         "258"
39484       ],
39485       [
39486         "Myanmar (Burma) (မြန်မာ)",
39487         "mm",
39488         "95"
39489       ],
39490       [
39491         "Namibia (Namibië)",
39492         "na",
39493         "264"
39494       ],
39495       [
39496         "Nauru",
39497         "nr",
39498         "674"
39499       ],
39500       [
39501         "Nepal (नेपाल)",
39502         "np",
39503         "977"
39504       ],
39505       [
39506         "Netherlands (Nederland)",
39507         "nl",
39508         "31"
39509       ],
39510       [
39511         "New Caledonia (Nouvelle-Calédonie)",
39512         "nc",
39513         "687"
39514       ],
39515       [
39516         "New Zealand",
39517         "nz",
39518         "64"
39519       ],
39520       [
39521         "Nicaragua",
39522         "ni",
39523         "505"
39524       ],
39525       [
39526         "Niger (Nijar)",
39527         "ne",
39528         "227"
39529       ],
39530       [
39531         "Nigeria",
39532         "ng",
39533         "234"
39534       ],
39535       [
39536         "Niue",
39537         "nu",
39538         "683"
39539       ],
39540       [
39541         "Norfolk Island",
39542         "nf",
39543         "672"
39544       ],
39545       [
39546         "North Korea (조선 민주주의 인민 공화국)",
39547         "kp",
39548         "850"
39549       ],
39550       [
39551         "Northern Mariana Islands",
39552         "mp",
39553         "1670"
39554       ],
39555       [
39556         "Norway (Norge)",
39557         "no",
39558         "47",
39559         0
39560       ],
39561       [
39562         "Oman (‫عُمان‬‎)",
39563         "om",
39564         "968"
39565       ],
39566       [
39567         "Pakistan (‫پاکستان‬‎)",
39568         "pk",
39569         "92"
39570       ],
39571       [
39572         "Palau",
39573         "pw",
39574         "680"
39575       ],
39576       [
39577         "Palestine (‫فلسطين‬‎)",
39578         "ps",
39579         "970"
39580       ],
39581       [
39582         "Panama (Panamá)",
39583         "pa",
39584         "507"
39585       ],
39586       [
39587         "Papua New Guinea",
39588         "pg",
39589         "675"
39590       ],
39591       [
39592         "Paraguay",
39593         "py",
39594         "595"
39595       ],
39596       [
39597         "Peru (Perú)",
39598         "pe",
39599         "51"
39600       ],
39601       [
39602         "Philippines",
39603         "ph",
39604         "63"
39605       ],
39606       [
39607         "Poland (Polska)",
39608         "pl",
39609         "48"
39610       ],
39611       [
39612         "Portugal",
39613         "pt",
39614         "351"
39615       ],
39616       [
39617         "Puerto Rico",
39618         "pr",
39619         "1",
39620         3,
39621         ["787", "939"]
39622       ],
39623       [
39624         "Qatar (‫قطر‬‎)",
39625         "qa",
39626         "974"
39627       ],
39628       [
39629         "Réunion (La Réunion)",
39630         "re",
39631         "262",
39632         0
39633       ],
39634       [
39635         "Romania (România)",
39636         "ro",
39637         "40"
39638       ],
39639       [
39640         "Russia (Россия)",
39641         "ru",
39642         "7",
39643         0
39644       ],
39645       [
39646         "Rwanda",
39647         "rw",
39648         "250"
39649       ],
39650       [
39651         "Saint Barthélemy",
39652         "bl",
39653         "590",
39654         1
39655       ],
39656       [
39657         "Saint Helena",
39658         "sh",
39659         "290"
39660       ],
39661       [
39662         "Saint Kitts and Nevis",
39663         "kn",
39664         "1869"
39665       ],
39666       [
39667         "Saint Lucia",
39668         "lc",
39669         "1758"
39670       ],
39671       [
39672         "Saint Martin (Saint-Martin (partie française))",
39673         "mf",
39674         "590",
39675         2
39676       ],
39677       [
39678         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39679         "pm",
39680         "508"
39681       ],
39682       [
39683         "Saint Vincent and the Grenadines",
39684         "vc",
39685         "1784"
39686       ],
39687       [
39688         "Samoa",
39689         "ws",
39690         "685"
39691       ],
39692       [
39693         "San Marino",
39694         "sm",
39695         "378"
39696       ],
39697       [
39698         "São Tomé and Príncipe (São Tomé e Príncipe)",
39699         "st",
39700         "239"
39701       ],
39702       [
39703         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39704         "sa",
39705         "966"
39706       ],
39707       [
39708         "Senegal (Sénégal)",
39709         "sn",
39710         "221"
39711       ],
39712       [
39713         "Serbia (Србија)",
39714         "rs",
39715         "381"
39716       ],
39717       [
39718         "Seychelles",
39719         "sc",
39720         "248"
39721       ],
39722       [
39723         "Sierra Leone",
39724         "sl",
39725         "232"
39726       ],
39727       [
39728         "Singapore",
39729         "sg",
39730         "65"
39731       ],
39732       [
39733         "Sint Maarten",
39734         "sx",
39735         "1721"
39736       ],
39737       [
39738         "Slovakia (Slovensko)",
39739         "sk",
39740         "421"
39741       ],
39742       [
39743         "Slovenia (Slovenija)",
39744         "si",
39745         "386"
39746       ],
39747       [
39748         "Solomon Islands",
39749         "sb",
39750         "677"
39751       ],
39752       [
39753         "Somalia (Soomaaliya)",
39754         "so",
39755         "252"
39756       ],
39757       [
39758         "South Africa",
39759         "za",
39760         "27"
39761       ],
39762       [
39763         "South Korea (대한민국)",
39764         "kr",
39765         "82"
39766       ],
39767       [
39768         "South Sudan (‫جنوب السودان‬‎)",
39769         "ss",
39770         "211"
39771       ],
39772       [
39773         "Spain (España)",
39774         "es",
39775         "34"
39776       ],
39777       [
39778         "Sri Lanka (ශ්‍රී ලංකාව)",
39779         "lk",
39780         "94"
39781       ],
39782       [
39783         "Sudan (‫السودان‬‎)",
39784         "sd",
39785         "249"
39786       ],
39787       [
39788         "Suriname",
39789         "sr",
39790         "597"
39791       ],
39792       [
39793         "Svalbard and Jan Mayen",
39794         "sj",
39795         "47",
39796         1
39797       ],
39798       [
39799         "Swaziland",
39800         "sz",
39801         "268"
39802       ],
39803       [
39804         "Sweden (Sverige)",
39805         "se",
39806         "46"
39807       ],
39808       [
39809         "Switzerland (Schweiz)",
39810         "ch",
39811         "41"
39812       ],
39813       [
39814         "Syria (‫سوريا‬‎)",
39815         "sy",
39816         "963"
39817       ],
39818       [
39819         "Taiwan (台灣)",
39820         "tw",
39821         "886"
39822       ],
39823       [
39824         "Tajikistan",
39825         "tj",
39826         "992"
39827       ],
39828       [
39829         "Tanzania",
39830         "tz",
39831         "255"
39832       ],
39833       [
39834         "Thailand (ไทย)",
39835         "th",
39836         "66"
39837       ],
39838       [
39839         "Timor-Leste",
39840         "tl",
39841         "670"
39842       ],
39843       [
39844         "Togo",
39845         "tg",
39846         "228"
39847       ],
39848       [
39849         "Tokelau",
39850         "tk",
39851         "690"
39852       ],
39853       [
39854         "Tonga",
39855         "to",
39856         "676"
39857       ],
39858       [
39859         "Trinidad and Tobago",
39860         "tt",
39861         "1868"
39862       ],
39863       [
39864         "Tunisia (‫تونس‬‎)",
39865         "tn",
39866         "216"
39867       ],
39868       [
39869         "Turkey (Türkiye)",
39870         "tr",
39871         "90"
39872       ],
39873       [
39874         "Turkmenistan",
39875         "tm",
39876         "993"
39877       ],
39878       [
39879         "Turks and Caicos Islands",
39880         "tc",
39881         "1649"
39882       ],
39883       [
39884         "Tuvalu",
39885         "tv",
39886         "688"
39887       ],
39888       [
39889         "U.S. Virgin Islands",
39890         "vi",
39891         "1340"
39892       ],
39893       [
39894         "Uganda",
39895         "ug",
39896         "256"
39897       ],
39898       [
39899         "Ukraine (Україна)",
39900         "ua",
39901         "380"
39902       ],
39903       [
39904         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39905         "ae",
39906         "971"
39907       ],
39908       [
39909         "United Kingdom",
39910         "gb",
39911         "44",
39912         0
39913       ],
39914       [
39915         "United States",
39916         "us",
39917         "1",
39918         0
39919       ],
39920       [
39921         "Uruguay",
39922         "uy",
39923         "598"
39924       ],
39925       [
39926         "Uzbekistan (Oʻzbekiston)",
39927         "uz",
39928         "998"
39929       ],
39930       [
39931         "Vanuatu",
39932         "vu",
39933         "678"
39934       ],
39935       [
39936         "Vatican City (Città del Vaticano)",
39937         "va",
39938         "39",
39939         1
39940       ],
39941       [
39942         "Venezuela",
39943         "ve",
39944         "58"
39945       ],
39946       [
39947         "Vietnam (Việt Nam)",
39948         "vn",
39949         "84"
39950       ],
39951       [
39952         "Wallis and Futuna (Wallis-et-Futuna)",
39953         "wf",
39954         "681"
39955       ],
39956       [
39957         "Western Sahara (‫الصحراء الغربية‬‎)",
39958         "eh",
39959         "212",
39960         1
39961       ],
39962       [
39963         "Yemen (‫اليمن‬‎)",
39964         "ye",
39965         "967"
39966       ],
39967       [
39968         "Zambia",
39969         "zm",
39970         "260"
39971       ],
39972       [
39973         "Zimbabwe",
39974         "zw",
39975         "263"
39976       ],
39977       [
39978         "Åland Islands",
39979         "ax",
39980         "358",
39981         1
39982       ]
39983   ];
39984   
39985   return d;
39986 }/**
39987 *    This script refer to:
39988 *    Title: International Telephone Input
39989 *    Author: Jack O'Connor
39990 *    Code version:  v12.1.12
39991 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39992 **/
39993
39994 /**
39995  * @class Roo.bootstrap.PhoneInput
39996  * @extends Roo.bootstrap.TriggerField
39997  * An input with International dial-code selection
39998  
39999  * @cfg {String} defaultDialCode default '+852'
40000  * @cfg {Array} preferedCountries default []
40001   
40002  * @constructor
40003  * Create a new PhoneInput.
40004  * @param {Object} config Configuration options
40005  */
40006
40007 Roo.bootstrap.PhoneInput = function(config) {
40008     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40009 };
40010
40011 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40012         
40013         listWidth: undefined,
40014         
40015         selectedClass: 'active',
40016         
40017         invalidClass : "has-warning",
40018         
40019         validClass: 'has-success',
40020         
40021         allowed: '0123456789',
40022         
40023         max_length: 15,
40024         
40025         /**
40026          * @cfg {String} defaultDialCode The default dial code when initializing the input
40027          */
40028         defaultDialCode: '+852',
40029         
40030         /**
40031          * @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
40032          */
40033         preferedCountries: false,
40034         
40035         getAutoCreate : function()
40036         {
40037             var data = Roo.bootstrap.PhoneInputData();
40038             var align = this.labelAlign || this.parentLabelAlign();
40039             var id = Roo.id();
40040             
40041             this.allCountries = [];
40042             this.dialCodeMapping = [];
40043             
40044             for (var i = 0; i < data.length; i++) {
40045               var c = data[i];
40046               this.allCountries[i] = {
40047                 name: c[0],
40048                 iso2: c[1],
40049                 dialCode: c[2],
40050                 priority: c[3] || 0,
40051                 areaCodes: c[4] || null
40052               };
40053               this.dialCodeMapping[c[2]] = {
40054                   name: c[0],
40055                   iso2: c[1],
40056                   priority: c[3] || 0,
40057                   areaCodes: c[4] || null
40058               };
40059             }
40060             
40061             var cfg = {
40062                 cls: 'form-group',
40063                 cn: []
40064             };
40065             
40066             var input =  {
40067                 tag: 'input',
40068                 id : id,
40069                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40070                 maxlength: this.max_length,
40071                 cls : 'form-control tel-input',
40072                 autocomplete: 'new-password'
40073             };
40074             
40075             var hiddenInput = {
40076                 tag: 'input',
40077                 type: 'hidden',
40078                 cls: 'hidden-tel-input'
40079             };
40080             
40081             if (this.name) {
40082                 hiddenInput.name = this.name;
40083             }
40084             
40085             if (this.disabled) {
40086                 input.disabled = true;
40087             }
40088             
40089             var flag_container = {
40090                 tag: 'div',
40091                 cls: 'flag-box',
40092                 cn: [
40093                     {
40094                         tag: 'div',
40095                         cls: 'flag'
40096                     },
40097                     {
40098                         tag: 'div',
40099                         cls: 'caret'
40100                     }
40101                 ]
40102             };
40103             
40104             var box = {
40105                 tag: 'div',
40106                 cls: this.hasFeedback ? 'has-feedback' : '',
40107                 cn: [
40108                     hiddenInput,
40109                     input,
40110                     {
40111                         tag: 'input',
40112                         cls: 'dial-code-holder',
40113                         disabled: true
40114                     }
40115                 ]
40116             };
40117             
40118             var container = {
40119                 cls: 'roo-select2-container input-group',
40120                 cn: [
40121                     flag_container,
40122                     box
40123                 ]
40124             };
40125             
40126             if (this.fieldLabel.length) {
40127                 var indicator = {
40128                     tag: 'i',
40129                     tooltip: 'This field is required'
40130                 };
40131                 
40132                 var label = {
40133                     tag: 'label',
40134                     'for':  id,
40135                     cls: 'control-label',
40136                     cn: []
40137                 };
40138                 
40139                 var label_text = {
40140                     tag: 'span',
40141                     html: this.fieldLabel
40142                 };
40143                 
40144                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40145                 label.cn = [
40146                     indicator,
40147                     label_text
40148                 ];
40149                 
40150                 if(this.indicatorpos == 'right') {
40151                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40152                     label.cn = [
40153                         label_text,
40154                         indicator
40155                     ];
40156                 }
40157                 
40158                 if(align == 'left') {
40159                     container = {
40160                         tag: 'div',
40161                         cn: [
40162                             container
40163                         ]
40164                     };
40165                     
40166                     if(this.labelWidth > 12){
40167                         label.style = "width: " + this.labelWidth + 'px';
40168                     }
40169                     if(this.labelWidth < 13 && this.labelmd == 0){
40170                         this.labelmd = this.labelWidth;
40171                     }
40172                     if(this.labellg > 0){
40173                         label.cls += ' col-lg-' + this.labellg;
40174                         input.cls += ' col-lg-' + (12 - this.labellg);
40175                     }
40176                     if(this.labelmd > 0){
40177                         label.cls += ' col-md-' + this.labelmd;
40178                         container.cls += ' col-md-' + (12 - this.labelmd);
40179                     }
40180                     if(this.labelsm > 0){
40181                         label.cls += ' col-sm-' + this.labelsm;
40182                         container.cls += ' col-sm-' + (12 - this.labelsm);
40183                     }
40184                     if(this.labelxs > 0){
40185                         label.cls += ' col-xs-' + this.labelxs;
40186                         container.cls += ' col-xs-' + (12 - this.labelxs);
40187                     }
40188                 }
40189             }
40190             
40191             cfg.cn = [
40192                 label,
40193                 container
40194             ];
40195             
40196             var settings = this;
40197             
40198             ['xs','sm','md','lg'].map(function(size){
40199                 if (settings[size]) {
40200                     cfg.cls += ' col-' + size + '-' + settings[size];
40201                 }
40202             });
40203             
40204             this.store = new Roo.data.Store({
40205                 proxy : new Roo.data.MemoryProxy({}),
40206                 reader : new Roo.data.JsonReader({
40207                     fields : [
40208                         {
40209                             'name' : 'name',
40210                             'type' : 'string'
40211                         },
40212                         {
40213                             'name' : 'iso2',
40214                             'type' : 'string'
40215                         },
40216                         {
40217                             'name' : 'dialCode',
40218                             'type' : 'string'
40219                         },
40220                         {
40221                             'name' : 'priority',
40222                             'type' : 'string'
40223                         },
40224                         {
40225                             'name' : 'areaCodes',
40226                             'type' : 'string'
40227                         }
40228                     ]
40229                 })
40230             });
40231             
40232             if(!this.preferedCountries) {
40233                 this.preferedCountries = [
40234                     'hk',
40235                     'gb',
40236                     'us'
40237                 ];
40238             }
40239             
40240             var p = this.preferedCountries.reverse();
40241             
40242             if(p) {
40243                 for (var i = 0; i < p.length; i++) {
40244                     for (var j = 0; j < this.allCountries.length; j++) {
40245                         if(this.allCountries[j].iso2 == p[i]) {
40246                             var t = this.allCountries[j];
40247                             this.allCountries.splice(j,1);
40248                             this.allCountries.unshift(t);
40249                         }
40250                     } 
40251                 }
40252             }
40253             
40254             this.store.proxy.data = {
40255                 success: true,
40256                 data: this.allCountries
40257             };
40258             
40259             return cfg;
40260         },
40261         
40262         initEvents : function()
40263         {
40264             this.createList();
40265             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40266             
40267             this.indicator = this.indicatorEl();
40268             this.flag = this.flagEl();
40269             this.dialCodeHolder = this.dialCodeHolderEl();
40270             
40271             this.trigger = this.el.select('div.flag-box',true).first();
40272             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40273             
40274             var _this = this;
40275             
40276             (function(){
40277                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40278                 _this.list.setWidth(lw);
40279             }).defer(100);
40280             
40281             this.list.on('mouseover', this.onViewOver, this);
40282             this.list.on('mousemove', this.onViewMove, this);
40283             this.inputEl().on("keyup", this.onKeyUp, this);
40284             this.inputEl().on("keypress", this.onKeyPress, this);
40285             
40286             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40287
40288             this.view = new Roo.View(this.list, this.tpl, {
40289                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40290             });
40291             
40292             this.view.on('click', this.onViewClick, this);
40293             this.setValue(this.defaultDialCode);
40294         },
40295         
40296         onTriggerClick : function(e)
40297         {
40298             Roo.log('trigger click');
40299             if(this.disabled){
40300                 return;
40301             }
40302             
40303             if(this.isExpanded()){
40304                 this.collapse();
40305                 this.hasFocus = false;
40306             }else {
40307                 this.store.load({});
40308                 this.hasFocus = true;
40309                 this.expand();
40310             }
40311         },
40312         
40313         isExpanded : function()
40314         {
40315             return this.list.isVisible();
40316         },
40317         
40318         collapse : function()
40319         {
40320             if(!this.isExpanded()){
40321                 return;
40322             }
40323             this.list.hide();
40324             Roo.get(document).un('mousedown', this.collapseIf, this);
40325             Roo.get(document).un('mousewheel', this.collapseIf, this);
40326             this.fireEvent('collapse', this);
40327             this.validate();
40328         },
40329         
40330         expand : function()
40331         {
40332             Roo.log('expand');
40333
40334             if(this.isExpanded() || !this.hasFocus){
40335                 return;
40336             }
40337             
40338             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40339             this.list.setWidth(lw);
40340             
40341             this.list.show();
40342             this.restrictHeight();
40343             
40344             Roo.get(document).on('mousedown', this.collapseIf, this);
40345             Roo.get(document).on('mousewheel', this.collapseIf, this);
40346             
40347             this.fireEvent('expand', this);
40348         },
40349         
40350         restrictHeight : function()
40351         {
40352             this.list.alignTo(this.inputEl(), this.listAlign);
40353             this.list.alignTo(this.inputEl(), this.listAlign);
40354         },
40355         
40356         onViewOver : function(e, t)
40357         {
40358             if(this.inKeyMode){
40359                 return;
40360             }
40361             var item = this.view.findItemFromChild(t);
40362             
40363             if(item){
40364                 var index = this.view.indexOf(item);
40365                 this.select(index, false);
40366             }
40367         },
40368
40369         // private
40370         onViewClick : function(view, doFocus, el, e)
40371         {
40372             var index = this.view.getSelectedIndexes()[0];
40373             
40374             var r = this.store.getAt(index);
40375             
40376             if(r){
40377                 this.onSelect(r, index);
40378             }
40379             if(doFocus !== false && !this.blockFocus){
40380                 this.inputEl().focus();
40381             }
40382         },
40383         
40384         onViewMove : function(e, t)
40385         {
40386             this.inKeyMode = false;
40387         },
40388         
40389         select : function(index, scrollIntoView)
40390         {
40391             this.selectedIndex = index;
40392             this.view.select(index);
40393             if(scrollIntoView !== false){
40394                 var el = this.view.getNode(index);
40395                 if(el){
40396                     this.list.scrollChildIntoView(el, false);
40397                 }
40398             }
40399         },
40400         
40401         createList : function()
40402         {
40403             this.list = Roo.get(document.body).createChild({
40404                 tag: 'ul',
40405                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40406                 style: 'display:none'
40407             });
40408             
40409             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40410         },
40411         
40412         collapseIf : function(e)
40413         {
40414             var in_combo  = e.within(this.el);
40415             var in_list =  e.within(this.list);
40416             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40417             
40418             if (in_combo || in_list || is_list) {
40419                 return;
40420             }
40421             this.collapse();
40422         },
40423         
40424         onSelect : function(record, index)
40425         {
40426             if(this.fireEvent('beforeselect', this, record, index) !== false){
40427                 
40428                 this.setFlagClass(record.data.iso2);
40429                 this.setDialCode(record.data.dialCode);
40430                 this.hasFocus = false;
40431                 this.collapse();
40432                 this.fireEvent('select', this, record, index);
40433             }
40434         },
40435         
40436         flagEl : function()
40437         {
40438             var flag = this.el.select('div.flag',true).first();
40439             if(!flag){
40440                 return false;
40441             }
40442             return flag;
40443         },
40444         
40445         dialCodeHolderEl : function()
40446         {
40447             var d = this.el.select('input.dial-code-holder',true).first();
40448             if(!d){
40449                 return false;
40450             }
40451             return d;
40452         },
40453         
40454         setDialCode : function(v)
40455         {
40456             this.dialCodeHolder.dom.value = '+'+v;
40457         },
40458         
40459         setFlagClass : function(n)
40460         {
40461             this.flag.dom.className = 'flag '+n;
40462         },
40463         
40464         getValue : function()
40465         {
40466             var v = this.inputEl().getValue();
40467             if(this.dialCodeHolder) {
40468                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40469             }
40470             return v;
40471         },
40472         
40473         setValue : function(v)
40474         {
40475             var d = this.getDialCode(v);
40476             
40477             //invalid dial code
40478             if(v.length == 0 || !d || d.length == 0) {
40479                 if(this.rendered){
40480                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40481                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40482                 }
40483                 return;
40484             }
40485             
40486             //valid dial code
40487             this.setFlagClass(this.dialCodeMapping[d].iso2);
40488             this.setDialCode(d);
40489             this.inputEl().dom.value = v.replace('+'+d,'');
40490             this.hiddenEl().dom.value = this.getValue();
40491             
40492             this.validate();
40493         },
40494         
40495         getDialCode : function(v)
40496         {
40497             v = v ||  '';
40498             
40499             if (v.length == 0) {
40500                 return this.dialCodeHolder.dom.value;
40501             }
40502             
40503             var dialCode = "";
40504             if (v.charAt(0) != "+") {
40505                 return false;
40506             }
40507             var numericChars = "";
40508             for (var i = 1; i < v.length; i++) {
40509               var c = v.charAt(i);
40510               if (!isNaN(c)) {
40511                 numericChars += c;
40512                 if (this.dialCodeMapping[numericChars]) {
40513                   dialCode = v.substr(1, i);
40514                 }
40515                 if (numericChars.length == 4) {
40516                   break;
40517                 }
40518               }
40519             }
40520             return dialCode;
40521         },
40522         
40523         reset : function()
40524         {
40525             this.setValue(this.defaultDialCode);
40526             this.validate();
40527         },
40528         
40529         hiddenEl : function()
40530         {
40531             return this.el.select('input.hidden-tel-input',true).first();
40532         },
40533         
40534         // after setting val
40535         onKeyUp : function(e){
40536             this.setValue(this.getValue());
40537         },
40538         
40539         onKeyPress : function(e){
40540             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40541                 e.stopEvent();
40542             }
40543         }
40544         
40545 });
40546 /**
40547  * @class Roo.bootstrap.MoneyField
40548  * @extends Roo.bootstrap.ComboBox
40549  * Bootstrap MoneyField class
40550  * 
40551  * @constructor
40552  * Create a new MoneyField.
40553  * @param {Object} config Configuration options
40554  */
40555
40556 Roo.bootstrap.MoneyField = function(config) {
40557     
40558     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40559     
40560 };
40561
40562 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40563     
40564     /**
40565      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40566      */
40567     allowDecimals : true,
40568     /**
40569      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40570      */
40571     decimalSeparator : ".",
40572     /**
40573      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40574      */
40575     decimalPrecision : 0,
40576     /**
40577      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40578      */
40579     allowNegative : true,
40580     /**
40581      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40582      */
40583     allowZero: true,
40584     /**
40585      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40586      */
40587     minValue : Number.NEGATIVE_INFINITY,
40588     /**
40589      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40590      */
40591     maxValue : Number.MAX_VALUE,
40592     /**
40593      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40594      */
40595     minText : "The minimum value for this field is {0}",
40596     /**
40597      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40598      */
40599     maxText : "The maximum value for this field is {0}",
40600     /**
40601      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40602      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40603      */
40604     nanText : "{0} is not a valid number",
40605     /**
40606      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40607      */
40608     castInt : true,
40609     /**
40610      * @cfg {String} defaults currency of the MoneyField
40611      * value should be in lkey
40612      */
40613     defaultCurrency : false,
40614     /**
40615      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40616      */
40617     thousandsDelimiter : false,
40618     /**
40619      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40620      */
40621     max_length: false,
40622     
40623     inputlg : 9,
40624     inputmd : 9,
40625     inputsm : 9,
40626     inputxs : 6,
40627     
40628     store : false,
40629     
40630     getAutoCreate : function()
40631     {
40632         var align = this.labelAlign || this.parentLabelAlign();
40633         
40634         var id = Roo.id();
40635
40636         var cfg = {
40637             cls: 'form-group',
40638             cn: []
40639         };
40640
40641         var input =  {
40642             tag: 'input',
40643             id : id,
40644             cls : 'form-control roo-money-amount-input',
40645             autocomplete: 'new-password'
40646         };
40647         
40648         var hiddenInput = {
40649             tag: 'input',
40650             type: 'hidden',
40651             id: Roo.id(),
40652             cls: 'hidden-number-input'
40653         };
40654         
40655         if(this.max_length) {
40656             input.maxlength = this.max_length; 
40657         }
40658         
40659         if (this.name) {
40660             hiddenInput.name = this.name;
40661         }
40662
40663         if (this.disabled) {
40664             input.disabled = true;
40665         }
40666
40667         var clg = 12 - this.inputlg;
40668         var cmd = 12 - this.inputmd;
40669         var csm = 12 - this.inputsm;
40670         var cxs = 12 - this.inputxs;
40671         
40672         var container = {
40673             tag : 'div',
40674             cls : 'row roo-money-field',
40675             cn : [
40676                 {
40677                     tag : 'div',
40678                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40679                     cn : [
40680                         {
40681                             tag : 'div',
40682                             cls: 'roo-select2-container input-group',
40683                             cn: [
40684                                 {
40685                                     tag : 'input',
40686                                     cls : 'form-control roo-money-currency-input',
40687                                     autocomplete: 'new-password',
40688                                     readOnly : 1,
40689                                     name : this.currencyName
40690                                 },
40691                                 {
40692                                     tag :'span',
40693                                     cls : 'input-group-addon',
40694                                     cn : [
40695                                         {
40696                                             tag: 'span',
40697                                             cls: 'caret'
40698                                         }
40699                                     ]
40700                                 }
40701                             ]
40702                         }
40703                     ]
40704                 },
40705                 {
40706                     tag : 'div',
40707                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40708                     cn : [
40709                         {
40710                             tag: 'div',
40711                             cls: this.hasFeedback ? 'has-feedback' : '',
40712                             cn: [
40713                                 input
40714                             ]
40715                         }
40716                     ]
40717                 }
40718             ]
40719             
40720         };
40721         
40722         if (this.fieldLabel.length) {
40723             var indicator = {
40724                 tag: 'i',
40725                 tooltip: 'This field is required'
40726             };
40727
40728             var label = {
40729                 tag: 'label',
40730                 'for':  id,
40731                 cls: 'control-label',
40732                 cn: []
40733             };
40734
40735             var label_text = {
40736                 tag: 'span',
40737                 html: this.fieldLabel
40738             };
40739
40740             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40741             label.cn = [
40742                 indicator,
40743                 label_text
40744             ];
40745
40746             if(this.indicatorpos == 'right') {
40747                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40748                 label.cn = [
40749                     label_text,
40750                     indicator
40751                 ];
40752             }
40753
40754             if(align == 'left') {
40755                 container = {
40756                     tag: 'div',
40757                     cn: [
40758                         container
40759                     ]
40760                 };
40761
40762                 if(this.labelWidth > 12){
40763                     label.style = "width: " + this.labelWidth + 'px';
40764                 }
40765                 if(this.labelWidth < 13 && this.labelmd == 0){
40766                     this.labelmd = this.labelWidth;
40767                 }
40768                 if(this.labellg > 0){
40769                     label.cls += ' col-lg-' + this.labellg;
40770                     input.cls += ' col-lg-' + (12 - this.labellg);
40771                 }
40772                 if(this.labelmd > 0){
40773                     label.cls += ' col-md-' + this.labelmd;
40774                     container.cls += ' col-md-' + (12 - this.labelmd);
40775                 }
40776                 if(this.labelsm > 0){
40777                     label.cls += ' col-sm-' + this.labelsm;
40778                     container.cls += ' col-sm-' + (12 - this.labelsm);
40779                 }
40780                 if(this.labelxs > 0){
40781                     label.cls += ' col-xs-' + this.labelxs;
40782                     container.cls += ' col-xs-' + (12 - this.labelxs);
40783                 }
40784             }
40785         }
40786
40787         cfg.cn = [
40788             label,
40789             container,
40790             hiddenInput
40791         ];
40792         
40793         var settings = this;
40794
40795         ['xs','sm','md','lg'].map(function(size){
40796             if (settings[size]) {
40797                 cfg.cls += ' col-' + size + '-' + settings[size];
40798             }
40799         });
40800         
40801         return cfg;
40802     },
40803     
40804     initEvents : function()
40805     {
40806         this.indicator = this.indicatorEl();
40807         
40808         this.initCurrencyEvent();
40809         
40810         this.initNumberEvent();
40811     },
40812     
40813     initCurrencyEvent : function()
40814     {
40815         if (!this.store) {
40816             throw "can not find store for combo";
40817         }
40818         
40819         this.store = Roo.factory(this.store, Roo.data);
40820         this.store.parent = this;
40821         
40822         this.createList();
40823         
40824         this.triggerEl = this.el.select('.input-group-addon', true).first();
40825         
40826         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40827         
40828         var _this = this;
40829         
40830         (function(){
40831             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40832             _this.list.setWidth(lw);
40833         }).defer(100);
40834         
40835         this.list.on('mouseover', this.onViewOver, this);
40836         this.list.on('mousemove', this.onViewMove, this);
40837         this.list.on('scroll', this.onViewScroll, this);
40838         
40839         if(!this.tpl){
40840             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40841         }
40842         
40843         this.view = new Roo.View(this.list, this.tpl, {
40844             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40845         });
40846         
40847         this.view.on('click', this.onViewClick, this);
40848         
40849         this.store.on('beforeload', this.onBeforeLoad, this);
40850         this.store.on('load', this.onLoad, this);
40851         this.store.on('loadexception', this.onLoadException, this);
40852         
40853         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40854             "up" : function(e){
40855                 this.inKeyMode = true;
40856                 this.selectPrev();
40857             },
40858
40859             "down" : function(e){
40860                 if(!this.isExpanded()){
40861                     this.onTriggerClick();
40862                 }else{
40863                     this.inKeyMode = true;
40864                     this.selectNext();
40865                 }
40866             },
40867
40868             "enter" : function(e){
40869                 this.collapse();
40870                 
40871                 if(this.fireEvent("specialkey", this, e)){
40872                     this.onViewClick(false);
40873                 }
40874                 
40875                 return true;
40876             },
40877
40878             "esc" : function(e){
40879                 this.collapse();
40880             },
40881
40882             "tab" : function(e){
40883                 this.collapse();
40884                 
40885                 if(this.fireEvent("specialkey", this, e)){
40886                     this.onViewClick(false);
40887                 }
40888                 
40889                 return true;
40890             },
40891
40892             scope : this,
40893
40894             doRelay : function(foo, bar, hname){
40895                 if(hname == 'down' || this.scope.isExpanded()){
40896                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40897                 }
40898                 return true;
40899             },
40900
40901             forceKeyDown: true
40902         });
40903         
40904         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40905         
40906     },
40907     
40908     initNumberEvent : function(e)
40909     {
40910         this.inputEl().on("keydown" , this.fireKey,  this);
40911         this.inputEl().on("focus", this.onFocus,  this);
40912         this.inputEl().on("blur", this.onBlur,  this);
40913         
40914         this.inputEl().relayEvent('keyup', this);
40915         
40916         if(this.indicator){
40917             this.indicator.addClass('invisible');
40918         }
40919  
40920         this.originalValue = this.getValue();
40921         
40922         if(this.validationEvent == 'keyup'){
40923             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40924             this.inputEl().on('keyup', this.filterValidation, this);
40925         }
40926         else if(this.validationEvent !== false){
40927             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40928         }
40929         
40930         if(this.selectOnFocus){
40931             this.on("focus", this.preFocus, this);
40932             
40933         }
40934         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40935             this.inputEl().on("keypress", this.filterKeys, this);
40936         } else {
40937             this.inputEl().relayEvent('keypress', this);
40938         }
40939         
40940         var allowed = "0123456789";
40941         
40942         if(this.allowDecimals){
40943             allowed += this.decimalSeparator;
40944         }
40945         
40946         if(this.allowNegative){
40947             allowed += "-";
40948         }
40949         
40950         if(this.thousandsDelimiter) {
40951             allowed += ",";
40952         }
40953         
40954         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40955         
40956         var keyPress = function(e){
40957             
40958             var k = e.getKey();
40959             
40960             var c = e.getCharCode();
40961             
40962             if(
40963                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40964                     allowed.indexOf(String.fromCharCode(c)) === -1
40965             ){
40966                 e.stopEvent();
40967                 return;
40968             }
40969             
40970             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40971                 return;
40972             }
40973             
40974             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40975                 e.stopEvent();
40976             }
40977         };
40978         
40979         this.inputEl().on("keypress", keyPress, this);
40980         
40981     },
40982     
40983     onTriggerClick : function(e)
40984     {   
40985         if(this.disabled){
40986             return;
40987         }
40988         
40989         this.page = 0;
40990         this.loadNext = false;
40991         
40992         if(this.isExpanded()){
40993             this.collapse();
40994             return;
40995         }
40996         
40997         this.hasFocus = true;
40998         
40999         if(this.triggerAction == 'all') {
41000             this.doQuery(this.allQuery, true);
41001             return;
41002         }
41003         
41004         this.doQuery(this.getRawValue());
41005     },
41006     
41007     getCurrency : function()
41008     {   
41009         var v = this.currencyEl().getValue();
41010         
41011         return v;
41012     },
41013     
41014     restrictHeight : function()
41015     {
41016         this.list.alignTo(this.currencyEl(), this.listAlign);
41017         this.list.alignTo(this.currencyEl(), this.listAlign);
41018     },
41019     
41020     onViewClick : function(view, doFocus, el, e)
41021     {
41022         var index = this.view.getSelectedIndexes()[0];
41023         
41024         var r = this.store.getAt(index);
41025         
41026         if(r){
41027             this.onSelect(r, index);
41028         }
41029     },
41030     
41031     onSelect : function(record, index){
41032         
41033         if(this.fireEvent('beforeselect', this, record, index) !== false){
41034         
41035             this.setFromCurrencyData(index > -1 ? record.data : false);
41036             
41037             this.collapse();
41038             
41039             this.fireEvent('select', this, record, index);
41040         }
41041     },
41042     
41043     setFromCurrencyData : function(o)
41044     {
41045         var currency = '';
41046         
41047         this.lastCurrency = o;
41048         
41049         if (this.currencyField) {
41050             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41051         } else {
41052             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41053         }
41054         
41055         this.lastSelectionText = currency;
41056         
41057         //setting default currency
41058         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41059             this.setCurrency(this.defaultCurrency);
41060             return;
41061         }
41062         
41063         this.setCurrency(currency);
41064     },
41065     
41066     setFromData : function(o)
41067     {
41068         var c = {};
41069         
41070         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41071         
41072         this.setFromCurrencyData(c);
41073         
41074         var value = '';
41075         
41076         if (this.name) {
41077             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41078         } else {
41079             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41080         }
41081         
41082         this.setValue(value);
41083         
41084     },
41085     
41086     setCurrency : function(v)
41087     {   
41088         this.currencyValue = v;
41089         
41090         if(this.rendered){
41091             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41092             this.validate();
41093         }
41094     },
41095     
41096     setValue : function(v)
41097     {
41098         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41099         
41100         this.value = v;
41101         
41102         if(this.rendered){
41103             
41104             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41105             
41106             this.inputEl().dom.value = (v == '') ? '' :
41107                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41108             
41109             if(!this.allowZero && v === '0') {
41110                 this.hiddenEl().dom.value = '';
41111                 this.inputEl().dom.value = '';
41112             }
41113             
41114             this.validate();
41115         }
41116     },
41117     
41118     getRawValue : function()
41119     {
41120         var v = this.inputEl().getValue();
41121         
41122         return v;
41123     },
41124     
41125     getValue : function()
41126     {
41127         return this.fixPrecision(this.parseValue(this.getRawValue()));
41128     },
41129     
41130     parseValue : function(value)
41131     {
41132         if(this.thousandsDelimiter) {
41133             value += "";
41134             r = new RegExp(",", "g");
41135             value = value.replace(r, "");
41136         }
41137         
41138         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41139         return isNaN(value) ? '' : value;
41140         
41141     },
41142     
41143     fixPrecision : function(value)
41144     {
41145         if(this.thousandsDelimiter) {
41146             value += "";
41147             r = new RegExp(",", "g");
41148             value = value.replace(r, "");
41149         }
41150         
41151         var nan = isNaN(value);
41152         
41153         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41154             return nan ? '' : value;
41155         }
41156         return parseFloat(value).toFixed(this.decimalPrecision);
41157     },
41158     
41159     decimalPrecisionFcn : function(v)
41160     {
41161         return Math.floor(v);
41162     },
41163     
41164     validateValue : function(value)
41165     {
41166         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41167             return false;
41168         }
41169         
41170         var num = this.parseValue(value);
41171         
41172         if(isNaN(num)){
41173             this.markInvalid(String.format(this.nanText, value));
41174             return false;
41175         }
41176         
41177         if(num < this.minValue){
41178             this.markInvalid(String.format(this.minText, this.minValue));
41179             return false;
41180         }
41181         
41182         if(num > this.maxValue){
41183             this.markInvalid(String.format(this.maxText, this.maxValue));
41184             return false;
41185         }
41186         
41187         return true;
41188     },
41189     
41190     validate : function()
41191     {
41192         if(this.disabled || this.allowBlank){
41193             this.markValid();
41194             return true;
41195         }
41196         
41197         var currency = this.getCurrency();
41198         
41199         if(this.validateValue(this.getRawValue()) && currency.length){
41200             this.markValid();
41201             return true;
41202         }
41203         
41204         this.markInvalid();
41205         return false;
41206     },
41207     
41208     getName: function()
41209     {
41210         return this.name;
41211     },
41212     
41213     beforeBlur : function()
41214     {
41215         if(!this.castInt){
41216             return;
41217         }
41218         
41219         var v = this.parseValue(this.getRawValue());
41220         
41221         if(v || v == 0){
41222             this.setValue(v);
41223         }
41224     },
41225     
41226     onBlur : function()
41227     {
41228         this.beforeBlur();
41229         
41230         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41231             //this.el.removeClass(this.focusClass);
41232         }
41233         
41234         this.hasFocus = false;
41235         
41236         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41237             this.validate();
41238         }
41239         
41240         var v = this.getValue();
41241         
41242         if(String(v) !== String(this.startValue)){
41243             this.fireEvent('change', this, v, this.startValue);
41244         }
41245         
41246         this.fireEvent("blur", this);
41247     },
41248     
41249     inputEl : function()
41250     {
41251         return this.el.select('.roo-money-amount-input', true).first();
41252     },
41253     
41254     currencyEl : function()
41255     {
41256         return this.el.select('.roo-money-currency-input', true).first();
41257     },
41258     
41259     hiddenEl : function()
41260     {
41261         return this.el.select('input.hidden-number-input',true).first();
41262     }
41263     
41264 });