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                                 {
2874                                     cls : 'modal-footer',
2875                                     cn : [
2876                                         {
2877                                             tag: 'div',
2878                                             cls: 'btn-' + this.buttonPosition
2879                                         }
2880                                     ]
2881
2882                                 }
2883
2884
2885                             ]
2886
2887                         }
2888                     ]
2889
2890                 }
2891             ]
2892         };
2893
2894         if(this.animate){
2895             modal.cls += ' fade';
2896         }
2897
2898         return modal;
2899
2900     },
2901     getChildContainer : function() {
2902
2903          return this.bodyEl;
2904
2905     },
2906     getButtonContainer : function() {
2907         
2908          return Roo.bootstrap.version == 4 ?
2909             this.el.select('.modal-footer',true).first()
2910             : this.el.select('.modal-footer div',true).first();
2911
2912     },
2913     initEvents : function()
2914     {
2915         if (this.allow_close) {
2916             this.closeEl.on('click', this.hide, this);
2917         }
2918         Roo.EventManager.onWindowResize(this.resize, this, true);
2919
2920
2921     },
2922
2923     resize : function()
2924     {
2925         this.maskEl.setSize(
2926             Roo.lib.Dom.getViewWidth(true),
2927             Roo.lib.Dom.getViewHeight(true)
2928         );
2929         
2930         if (this.fitwindow) {
2931             this.setSize(
2932                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2934             );
2935             return;
2936         }
2937         
2938         if(this.max_width !== 0) {
2939             
2940             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2941             
2942             if(this.height) {
2943                 this.setSize(w, this.height);
2944                 return;
2945             }
2946             
2947             if(this.max_height) {
2948                 this.setSize(w,Math.min(
2949                     this.max_height,
2950                     Roo.lib.Dom.getViewportHeight(true) - 60
2951                 ));
2952                 
2953                 return;
2954             }
2955             
2956             if(!this.fit_content) {
2957                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2958                 return;
2959             }
2960             
2961             this.setSize(w, Math.min(
2962                 60 +
2963                 this.headerEl.getHeight() + 
2964                 this.footerEl.getHeight() + 
2965                 this.getChildHeight(this.bodyEl.dom.childNodes),
2966                 Roo.lib.Dom.getViewportHeight(true) - 60)
2967             );
2968         }
2969         
2970     },
2971
2972     setSize : function(w,h)
2973     {
2974         if (!w && !h) {
2975             return;
2976         }
2977         
2978         this.resizeTo(w,h);
2979     },
2980
2981     show : function() {
2982
2983         if (!this.rendered) {
2984             this.render();
2985         }
2986
2987         //this.el.setStyle('display', 'block');
2988         this.el.removeClass('hideing');
2989         this.el.dom.style.display='block';
2990         
2991         Roo.get(document.body).addClass('modal-open');
2992  
2993         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2994             var _this = this;
2995             (function(){
2996                 this.el.addClass('show');
2997                 this.el.addClass('in');
2998             }).defer(50, this);
2999         }else{
3000             this.el.addClass('show');
3001             this.el.addClass('in');
3002         }
3003
3004         // not sure how we can show data in here..
3005         //if (this.tmpl) {
3006         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3007         //}
3008
3009         Roo.get(document.body).addClass("x-body-masked");
3010         
3011         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3012         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013         this.maskEl.dom.style.display = 'block';
3014         this.maskEl.addClass('show');
3015         
3016         
3017         this.resize();
3018         
3019         this.fireEvent('show', this);
3020
3021         // set zindex here - otherwise it appears to be ignored...
3022         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3023
3024         (function () {
3025             this.items.forEach( function(e) {
3026                 e.layout ? e.layout() : false;
3027
3028             });
3029         }).defer(100,this);
3030
3031     },
3032     hide : function()
3033     {
3034         if(this.fireEvent("beforehide", this) !== false){
3035             
3036             this.maskEl.removeClass('show');
3037             
3038             this.maskEl.dom.style.display = '';
3039             Roo.get(document.body).removeClass("x-body-masked");
3040             this.el.removeClass('in');
3041             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3042
3043             if(this.animate){ // why
3044                 this.el.addClass('hideing');
3045                 this.el.removeClass('show');
3046                 (function(){
3047                     if (!this.el.hasClass('hideing')) {
3048                         return; // it's been shown again...
3049                     }
3050                     
3051                     this.el.dom.style.display='';
3052
3053                     Roo.get(document.body).removeClass('modal-open');
3054                     this.el.removeClass('hideing');
3055                 }).defer(150,this);
3056                 
3057             }else{
3058                 this.el.removeClass('show');
3059                 this.el.dom.style.display='';
3060                 Roo.get(document.body).removeClass('modal-open');
3061
3062             }
3063             this.fireEvent('hide', this);
3064         }
3065     },
3066     isVisible : function()
3067     {
3068         
3069         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3070         
3071     },
3072
3073     addButton : function(str, cb)
3074     {
3075
3076
3077         var b = Roo.apply({}, { html : str } );
3078         b.xns = b.xns || Roo.bootstrap;
3079         b.xtype = b.xtype || 'Button';
3080         if (typeof(b.listeners) == 'undefined') {
3081             b.listeners = { click : cb.createDelegate(this)  };
3082         }
3083
3084         var btn = Roo.factory(b);
3085
3086         btn.render(this.el.select('.modal-footer div').first());
3087
3088         return btn;
3089
3090     },
3091
3092     setDefaultButton : function(btn)
3093     {
3094         //this.el.select('.modal-footer').()
3095     },
3096     diff : false,
3097
3098     resizeTo: function(w,h)
3099     {
3100         // skip.. ?? why??
3101
3102         this.dialogEl.setWidth(w);
3103         if (this.diff === false) {
3104             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3105         }
3106
3107         this.bodyEl.setHeight(h - this.diff);
3108
3109         this.fireEvent('resize', this);
3110
3111     },
3112     setContentSize  : function(w, h)
3113     {
3114
3115     },
3116     onButtonClick: function(btn,e)
3117     {
3118         //Roo.log([a,b,c]);
3119         this.fireEvent('btnclick', btn.name, e);
3120     },
3121      /**
3122      * Set the title of the Dialog
3123      * @param {String} str new Title
3124      */
3125     setTitle: function(str) {
3126         this.titleEl.dom.innerHTML = str;
3127     },
3128     /**
3129      * Set the body of the Dialog
3130      * @param {String} str new Title
3131      */
3132     setBody: function(str) {
3133         this.bodyEl.dom.innerHTML = str;
3134     },
3135     /**
3136      * Set the body of the Dialog using the template
3137      * @param {Obj} data - apply this data to the template and replace the body contents.
3138      */
3139     applyBody: function(obj)
3140     {
3141         if (!this.tmpl) {
3142             Roo.log("Error - using apply Body without a template");
3143             //code
3144         }
3145         this.tmpl.overwrite(this.bodyEl, obj);
3146     },
3147     
3148     getChildHeight : function(child_nodes)
3149     {
3150         if(
3151             !child_nodes ||
3152             child_nodes.length == 0
3153         ) {
3154             return;
3155         }
3156         
3157         var child_height = 0;
3158         
3159         for(var i = 0; i < child_nodes.length; i++) {
3160             
3161             /*
3162             * for modal with tabs...
3163             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3164                 
3165                 var layout_childs = child_nodes[i].childNodes;
3166                 
3167                 for(var j = 0; j < layout_childs.length; j++) {
3168                     
3169                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3170                         
3171                         var layout_body_childs = layout_childs[j].childNodes;
3172                         
3173                         for(var k = 0; k < layout_body_childs.length; k++) {
3174                             
3175                             if(layout_body_childs[k].classList.contains('navbar')) {
3176                                 child_height += layout_body_childs[k].offsetHeight;
3177                                 continue;
3178                             }
3179                             
3180                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3181                                 
3182                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3183                                 
3184                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3185                                     
3186                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3187                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3188                                         continue;
3189                                     }
3190                                     
3191                                 }
3192                                 
3193                             }
3194                             
3195                         }
3196                     }
3197                 }
3198                 continue;
3199             }
3200             */
3201             
3202             child_height += child_nodes[i].offsetHeight;
3203             // Roo.log(child_nodes[i].offsetHeight);
3204         }
3205         
3206         return child_height;
3207     }
3208
3209 });
3210
3211
3212 Roo.apply(Roo.bootstrap.Modal,  {
3213     /**
3214          * Button config that displays a single OK button
3215          * @type Object
3216          */
3217         OK :  [{
3218             name : 'ok',
3219             weight : 'primary',
3220             html : 'OK'
3221         }],
3222         /**
3223          * Button config that displays Yes and No buttons
3224          * @type Object
3225          */
3226         YESNO : [
3227             {
3228                 name  : 'no',
3229                 html : 'No'
3230             },
3231             {
3232                 name  :'yes',
3233                 weight : 'primary',
3234                 html : 'Yes'
3235             }
3236         ],
3237
3238         /**
3239          * Button config that displays OK and Cancel buttons
3240          * @type Object
3241          */
3242         OKCANCEL : [
3243             {
3244                name : 'cancel',
3245                 html : 'Cancel'
3246             },
3247             {
3248                 name : 'ok',
3249                 weight : 'primary',
3250                 html : 'OK'
3251             }
3252         ],
3253         /**
3254          * Button config that displays Yes, No and Cancel buttons
3255          * @type Object
3256          */
3257         YESNOCANCEL : [
3258             {
3259                 name : 'yes',
3260                 weight : 'primary',
3261                 html : 'Yes'
3262             },
3263             {
3264                 name : 'no',
3265                 html : 'No'
3266             },
3267             {
3268                 name : 'cancel',
3269                 html : 'Cancel'
3270             }
3271         ],
3272         
3273         zIndex : 10001
3274 });
3275 /*
3276  * - LGPL
3277  *
3278  * messagebox - can be used as a replace
3279  * 
3280  */
3281 /**
3282  * @class Roo.MessageBox
3283  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3284  * Example usage:
3285  *<pre><code>
3286 // Basic alert:
3287 Roo.Msg.alert('Status', 'Changes saved successfully.');
3288
3289 // Prompt for user data:
3290 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3291     if (btn == 'ok'){
3292         // process text value...
3293     }
3294 });
3295
3296 // Show a dialog using config options:
3297 Roo.Msg.show({
3298    title:'Save Changes?',
3299    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3300    buttons: Roo.Msg.YESNOCANCEL,
3301    fn: processResult,
3302    animEl: 'elId'
3303 });
3304 </code></pre>
3305  * @singleton
3306  */
3307 Roo.bootstrap.MessageBox = function(){
3308     var dlg, opt, mask, waitTimer;
3309     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3310     var buttons, activeTextEl, bwidth;
3311
3312     
3313     // private
3314     var handleButton = function(button){
3315         dlg.hide();
3316         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3317     };
3318
3319     // private
3320     var handleHide = function(){
3321         if(opt && opt.cls){
3322             dlg.el.removeClass(opt.cls);
3323         }
3324         //if(waitTimer){
3325         //    Roo.TaskMgr.stop(waitTimer);
3326         //    waitTimer = null;
3327         //}
3328     };
3329
3330     // private
3331     var updateButtons = function(b){
3332         var width = 0;
3333         if(!b){
3334             buttons["ok"].hide();
3335             buttons["cancel"].hide();
3336             buttons["yes"].hide();
3337             buttons["no"].hide();
3338             //dlg.footer.dom.style.display = 'none';
3339             return width;
3340         }
3341         dlg.footerEl.dom.style.display = '';
3342         for(var k in buttons){
3343             if(typeof buttons[k] != "function"){
3344                 if(b[k]){
3345                     buttons[k].show();
3346                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3347                     width += buttons[k].el.getWidth()+15;
3348                 }else{
3349                     buttons[k].hide();
3350                 }
3351             }
3352         }
3353         return width;
3354     };
3355
3356     // private
3357     var handleEsc = function(d, k, e){
3358         if(opt && opt.closable !== false){
3359             dlg.hide();
3360         }
3361         if(e){
3362             e.stopEvent();
3363         }
3364     };
3365
3366     return {
3367         /**
3368          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3369          * @return {Roo.BasicDialog} The BasicDialog element
3370          */
3371         getDialog : function(){
3372            if(!dlg){
3373                 dlg = new Roo.bootstrap.Modal( {
3374                     //draggable: true,
3375                     //resizable:false,
3376                     //constraintoviewport:false,
3377                     //fixedcenter:true,
3378                     //collapsible : false,
3379                     //shim:true,
3380                     //modal: true,
3381                 //    width: 'auto',
3382                   //  height:100,
3383                     //buttonAlign:"center",
3384                     closeClick : function(){
3385                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3386                             handleButton("no");
3387                         }else{
3388                             handleButton("cancel");
3389                         }
3390                     }
3391                 });
3392                 dlg.render();
3393                 dlg.on("hide", handleHide);
3394                 mask = dlg.mask;
3395                 //dlg.addKeyListener(27, handleEsc);
3396                 buttons = {};
3397                 this.buttons = buttons;
3398                 var bt = this.buttonText;
3399                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3400                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3401                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3402                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3403                 //Roo.log(buttons);
3404                 bodyEl = dlg.bodyEl.createChild({
3405
3406                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3407                         '<textarea class="roo-mb-textarea"></textarea>' +
3408                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3409                 });
3410                 msgEl = bodyEl.dom.firstChild;
3411                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3412                 textboxEl.enableDisplayMode();
3413                 textboxEl.addKeyListener([10,13], function(){
3414                     if(dlg.isVisible() && opt && opt.buttons){
3415                         if(opt.buttons.ok){
3416                             handleButton("ok");
3417                         }else if(opt.buttons.yes){
3418                             handleButton("yes");
3419                         }
3420                     }
3421                 });
3422                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3423                 textareaEl.enableDisplayMode();
3424                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3425                 progressEl.enableDisplayMode();
3426                 
3427                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3428                 var pf = progressEl.dom.firstChild;
3429                 if (pf) {
3430                     pp = Roo.get(pf.firstChild);
3431                     pp.setHeight(pf.offsetHeight);
3432                 }
3433                 
3434             }
3435             return dlg;
3436         },
3437
3438         /**
3439          * Updates the message box body text
3440          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3441          * the XHTML-compliant non-breaking space character '&amp;#160;')
3442          * @return {Roo.MessageBox} This message box
3443          */
3444         updateText : function(text)
3445         {
3446             if(!dlg.isVisible() && !opt.width){
3447                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3448                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3449             }
3450             msgEl.innerHTML = text || '&#160;';
3451       
3452             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3453             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3454             var w = Math.max(
3455                     Math.min(opt.width || cw , this.maxWidth), 
3456                     Math.max(opt.minWidth || this.minWidth, bwidth)
3457             );
3458             if(opt.prompt){
3459                 activeTextEl.setWidth(w);
3460             }
3461             if(dlg.isVisible()){
3462                 dlg.fixedcenter = false;
3463             }
3464             // to big, make it scroll. = But as usual stupid IE does not support
3465             // !important..
3466             
3467             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3468                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3469                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3470             } else {
3471                 bodyEl.dom.style.height = '';
3472                 bodyEl.dom.style.overflowY = '';
3473             }
3474             if (cw > w) {
3475                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3476             } else {
3477                 bodyEl.dom.style.overflowX = '';
3478             }
3479             
3480             dlg.setContentSize(w, bodyEl.getHeight());
3481             if(dlg.isVisible()){
3482                 dlg.fixedcenter = true;
3483             }
3484             return this;
3485         },
3486
3487         /**
3488          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3489          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3490          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3491          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3492          * @return {Roo.MessageBox} This message box
3493          */
3494         updateProgress : function(value, text){
3495             if(text){
3496                 this.updateText(text);
3497             }
3498             
3499             if (pp) { // weird bug on my firefox - for some reason this is not defined
3500                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3501                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3502             }
3503             return this;
3504         },        
3505
3506         /**
3507          * Returns true if the message box is currently displayed
3508          * @return {Boolean} True if the message box is visible, else false
3509          */
3510         isVisible : function(){
3511             return dlg && dlg.isVisible();  
3512         },
3513
3514         /**
3515          * Hides the message box if it is displayed
3516          */
3517         hide : function(){
3518             if(this.isVisible()){
3519                 dlg.hide();
3520             }  
3521         },
3522
3523         /**
3524          * Displays a new message box, or reinitializes an existing message box, based on the config options
3525          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3526          * The following config object properties are supported:
3527          * <pre>
3528 Property    Type             Description
3529 ----------  ---------------  ------------------------------------------------------------------------------------
3530 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3531                                    closes (defaults to undefined)
3532 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3533                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3534 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3535                                    progress and wait dialogs will ignore this property and always hide the
3536                                    close button as they can only be closed programmatically.
3537 cls               String           A custom CSS class to apply to the message box element
3538 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3539                                    displayed (defaults to 75)
3540 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3541                                    function will be btn (the name of the button that was clicked, if applicable,
3542                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3543                                    Progress and wait dialogs will ignore this option since they do not respond to
3544                                    user actions and can only be closed programmatically, so any required function
3545                                    should be called by the same code after it closes the dialog.
3546 icon              String           A CSS class that provides a background image to be used as an icon for
3547                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3548 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3549 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3550 modal             Boolean          False to allow user interaction with the page while the message box is
3551                                    displayed (defaults to true)
3552 msg               String           A string that will replace the existing message box body text (defaults
3553                                    to the XHTML-compliant non-breaking space character '&#160;')
3554 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3555 progress          Boolean          True to display a progress bar (defaults to false)
3556 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3557 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3558 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3559 title             String           The title text
3560 value             String           The string value to set into the active textbox element if displayed
3561 wait              Boolean          True to display a progress bar (defaults to false)
3562 width             Number           The width of the dialog in pixels
3563 </pre>
3564          *
3565          * Example usage:
3566          * <pre><code>
3567 Roo.Msg.show({
3568    title: 'Address',
3569    msg: 'Please enter your address:',
3570    width: 300,
3571    buttons: Roo.MessageBox.OKCANCEL,
3572    multiline: true,
3573    fn: saveAddress,
3574    animEl: 'addAddressBtn'
3575 });
3576 </code></pre>
3577          * @param {Object} config Configuration options
3578          * @return {Roo.MessageBox} This message box
3579          */
3580         show : function(options)
3581         {
3582             
3583             // this causes nightmares if you show one dialog after another
3584             // especially on callbacks..
3585              
3586             if(this.isVisible()){
3587                 
3588                 this.hide();
3589                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3590                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3591                 Roo.log("New Dialog Message:" +  options.msg )
3592                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3593                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3594                 
3595             }
3596             var d = this.getDialog();
3597             opt = options;
3598             d.setTitle(opt.title || "&#160;");
3599             d.closeEl.setDisplayed(opt.closable !== false);
3600             activeTextEl = textboxEl;
3601             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3602             if(opt.prompt){
3603                 if(opt.multiline){
3604                     textboxEl.hide();
3605                     textareaEl.show();
3606                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3607                         opt.multiline : this.defaultTextHeight);
3608                     activeTextEl = textareaEl;
3609                 }else{
3610                     textboxEl.show();
3611                     textareaEl.hide();
3612                 }
3613             }else{
3614                 textboxEl.hide();
3615                 textareaEl.hide();
3616             }
3617             progressEl.setDisplayed(opt.progress === true);
3618             this.updateProgress(0);
3619             activeTextEl.dom.value = opt.value || "";
3620             if(opt.prompt){
3621                 dlg.setDefaultButton(activeTextEl);
3622             }else{
3623                 var bs = opt.buttons;
3624                 var db = null;
3625                 if(bs && bs.ok){
3626                     db = buttons["ok"];
3627                 }else if(bs && bs.yes){
3628                     db = buttons["yes"];
3629                 }
3630                 dlg.setDefaultButton(db);
3631             }
3632             bwidth = updateButtons(opt.buttons);
3633             this.updateText(opt.msg);
3634             if(opt.cls){
3635                 d.el.addClass(opt.cls);
3636             }
3637             d.proxyDrag = opt.proxyDrag === true;
3638             d.modal = opt.modal !== false;
3639             d.mask = opt.modal !== false ? mask : false;
3640             if(!d.isVisible()){
3641                 // force it to the end of the z-index stack so it gets a cursor in FF
3642                 document.body.appendChild(dlg.el.dom);
3643                 d.animateTarget = null;
3644                 d.show(options.animEl);
3645             }
3646             return this;
3647         },
3648
3649         /**
3650          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3651          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652          * and closing the message box when the process is complete.
3653          * @param {String} title The title bar text
3654          * @param {String} msg The message box body text
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         progress : function(title, msg){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: false,
3662                 progress:true,
3663                 closable:false,
3664                 minWidth: this.minProgressWidth,
3665                 modal : true
3666             });
3667             return this;
3668         },
3669
3670         /**
3671          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672          * If a callback function is passed it will be called after the user clicks the button, and the
3673          * id of the button that was clicked will be passed as the only parameter to the callback
3674          * (could also be the top-right close button).
3675          * @param {String} title The title bar text
3676          * @param {String} msg The message box body text
3677          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678          * @param {Object} scope (optional) The scope of the callback function
3679          * @return {Roo.MessageBox} This message box
3680          */
3681         alert : function(title, msg, fn, scope)
3682         {
3683             this.show({
3684                 title : title,
3685                 msg : msg,
3686                 buttons: this.OK,
3687                 fn: fn,
3688                 closable : false,
3689                 scope : scope,
3690                 modal : true
3691             });
3692             return this;
3693         },
3694
3695         /**
3696          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3697          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698          * You are responsible for closing the message box when the process is complete.
3699          * @param {String} msg The message box body text
3700          * @param {String} title (optional) The title bar text
3701          * @return {Roo.MessageBox} This message box
3702          */
3703         wait : function(msg, title){
3704             this.show({
3705                 title : title,
3706                 msg : msg,
3707                 buttons: false,
3708                 closable:false,
3709                 progress:true,
3710                 modal:true,
3711                 width:300,
3712                 wait:true
3713             });
3714             waitTimer = Roo.TaskMgr.start({
3715                 run: function(i){
3716                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3717                 },
3718                 interval: 1000
3719             });
3720             return this;
3721         },
3722
3723         /**
3724          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @return {Roo.MessageBox} This message box
3732          */
3733         confirm : function(title, msg, fn, scope){
3734             this.show({
3735                 title : title,
3736                 msg : msg,
3737                 buttons: this.YESNO,
3738                 fn: fn,
3739                 scope : scope,
3740                 modal : true
3741             });
3742             return this;
3743         },
3744
3745         /**
3746          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3748          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749          * (could also be the top-right close button) and the text that was entered will be passed as the two
3750          * parameters to the callback.
3751          * @param {String} title The title bar text
3752          * @param {String} msg The message box body text
3753          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754          * @param {Object} scope (optional) The scope of the callback function
3755          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757          * @return {Roo.MessageBox} This message box
3758          */
3759         prompt : function(title, msg, fn, scope, multiline){
3760             this.show({
3761                 title : title,
3762                 msg : msg,
3763                 buttons: this.OKCANCEL,
3764                 fn: fn,
3765                 minWidth:250,
3766                 scope : scope,
3767                 prompt:true,
3768                 multiline: multiline,
3769                 modal : true
3770             });
3771             return this;
3772         },
3773
3774         /**
3775          * Button config that displays a single OK button
3776          * @type Object
3777          */
3778         OK : {ok:true},
3779         /**
3780          * Button config that displays Yes and No buttons
3781          * @type Object
3782          */
3783         YESNO : {yes:true, no:true},
3784         /**
3785          * Button config that displays OK and Cancel buttons
3786          * @type Object
3787          */
3788         OKCANCEL : {ok:true, cancel:true},
3789         /**
3790          * Button config that displays Yes, No and Cancel buttons
3791          * @type Object
3792          */
3793         YESNOCANCEL : {yes:true, no:true, cancel:true},
3794
3795         /**
3796          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3797          * @type Number
3798          */
3799         defaultTextHeight : 75,
3800         /**
3801          * The maximum width in pixels of the message box (defaults to 600)
3802          * @type Number
3803          */
3804         maxWidth : 600,
3805         /**
3806          * The minimum width in pixels of the message box (defaults to 100)
3807          * @type Number
3808          */
3809         minWidth : 100,
3810         /**
3811          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3812          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3813          * @type Number
3814          */
3815         minProgressWidth : 250,
3816         /**
3817          * An object containing the default button text strings that can be overriden for localized language support.
3818          * Supported properties are: ok, cancel, yes and no.
3819          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3820          * @type Object
3821          */
3822         buttonText : {
3823             ok : "OK",
3824             cancel : "Cancel",
3825             yes : "Yes",
3826             no : "No"
3827         }
3828     };
3829 }();
3830
3831 /**
3832  * Shorthand for {@link Roo.MessageBox}
3833  */
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3836 /*
3837  * - LGPL
3838  *
3839  * navbar
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.Navbar
3845  * @extends Roo.bootstrap.Component
3846  * Bootstrap Navbar class
3847
3848  * @constructor
3849  * Create a new Navbar
3850  * @param {Object} config The config object
3851  */
3852
3853
3854 Roo.bootstrap.Navbar = function(config){
3855     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3856     this.addEvents({
3857         // raw events
3858         /**
3859          * @event beforetoggle
3860          * Fire before toggle the menu
3861          * @param {Roo.EventObject} e
3862          */
3863         "beforetoggle" : true
3864     });
3865 };
3866
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3868     
3869     
3870    
3871     // private
3872     navItems : false,
3873     loadMask : false,
3874     
3875     
3876     getAutoCreate : function(){
3877         
3878         
3879         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3880         
3881     },
3882     
3883     initEvents :function ()
3884     {
3885         //Roo.log(this.el.select('.navbar-toggle',true));
3886         this.el.select('.navbar-toggle',true).on('click', function() {
3887             if(this.fireEvent('beforetoggle', this) !== false){
3888                 var ce = this.el.select('.navbar-collapse',true).first();
3889                 ce.toggleClass('in'); // old...
3890                 if (ce.hasClass('collapse')) {
3891                     // show it...
3892                     ce.removeClass('collapse');
3893                     ce.addClass('show');
3894                     var h = ce.getHeight();
3895                     Roo.log(h);
3896                     ce.removeClass('show');
3897                     // at this point we should be able to see it..
3898                     ce.addClass('collapsing');
3899                     
3900                     ce.setHeight(0); // resize it ...
3901                     ce.on('transitionend', function() {
3902                         Roo.log('done transition');
3903                         ce.removeClass('collapsing');
3904                         ce.addClass('show');
3905                         ce.removeClass('collapse');
3906
3907                         ce.dom.style.height = '';
3908                     }, this, { single: true} );
3909                     ce.setHeight(h);
3910                     
3911                 } else {
3912                     ce.setHeight(ce.getHeight());
3913                     ce.removeClass('show');
3914                     ce.addClass('collapsing');
3915                     
3916                     ce.on('transitionend', function() {
3917                         ce.dom.style.height = '';
3918                         ce.removeClass('collapsing');
3919                         ce.addClass('collapse');
3920                     }, this, { single: true} );
3921                     ce.setHeight(0);
3922                 }
3923             }
3924             
3925         }, this);
3926         
3927         var mark = {
3928             tag: "div",
3929             cls:"x-dlg-mask"
3930         };
3931         
3932         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3933         
3934         var size = this.el.getSize();
3935         this.maskEl.setSize(size.width, size.height);
3936         this.maskEl.enableDisplayMode("block");
3937         this.maskEl.hide();
3938         
3939         if(this.loadMask){
3940             this.maskEl.show();
3941         }
3942     },
3943     
3944     
3945     getChildContainer : function()
3946     {
3947         if (this.el.select('.collapse').getCount()) {
3948             return this.el.select('.collapse',true).first();
3949         }
3950         
3951         return this.el;
3952     },
3953     
3954     mask : function()
3955     {
3956         this.maskEl.show();
3957     },
3958     
3959     unmask : function()
3960     {
3961         this.maskEl.hide();
3962     } 
3963     
3964     
3965     
3966     
3967 });
3968
3969
3970
3971  
3972
3973  /*
3974  * - LGPL
3975  *
3976  * navbar
3977  * 
3978  */
3979
3980 /**
3981  * @class Roo.bootstrap.NavSimplebar
3982  * @extends Roo.bootstrap.Navbar
3983  * Bootstrap Sidebar class
3984  *
3985  * @cfg {Boolean} inverse is inverted color
3986  * 
3987  * @cfg {String} type (nav | pills | tabs)
3988  * @cfg {Boolean} arrangement stacked | justified
3989  * @cfg {String} align (left | right) alignment
3990  * 
3991  * @cfg {Boolean} main (true|false) main nav bar? default false
3992  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3993  * 
3994  * @cfg {String} tag (header|footer|nav|div) default is nav 
3995
3996  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3997  * 
3998  * 
3999  * @constructor
4000  * Create a new Sidebar
4001  * @param {Object} config The config object
4002  */
4003
4004
4005 Roo.bootstrap.NavSimplebar = function(config){
4006     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4007 };
4008
4009 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4010     
4011     inverse: false,
4012     
4013     type: false,
4014     arrangement: '',
4015     align : false,
4016     
4017     weight : 'light',
4018     
4019     main : false,
4020     
4021     
4022     tag : false,
4023     
4024     
4025     getAutoCreate : function(){
4026         
4027         
4028         var cfg = {
4029             tag : this.tag || 'div',
4030             cls : 'navbar navbar-expand-lg'
4031         };
4032         if (['light','white'].indexOf(this.weight) > -1) {
4033             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4034         }
4035         cfg.cls += ' bg-' + this.weight;
4036         
4037           
4038         
4039         cfg.cn = [
4040             {
4041                 cls: 'nav',
4042                 tag : 'ul'
4043             }
4044         ];
4045         
4046          
4047         this.type = this.type || 'nav';
4048         if (['tabs','pills'].indexOf(this.type)!==-1) {
4049             cfg.cn[0].cls += ' nav-' + this.type
4050         
4051         
4052         } else {
4053             if (this.type!=='nav') {
4054                 Roo.log('nav type must be nav/tabs/pills')
4055             }
4056             cfg.cn[0].cls += ' navbar-nav'
4057         }
4058         
4059         
4060         
4061         
4062         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4063             cfg.cn[0].cls += ' nav-' + this.arrangement;
4064         }
4065         
4066         
4067         if (this.align === 'right') {
4068             cfg.cn[0].cls += ' navbar-right';
4069         }
4070         
4071         if (this.inverse) {
4072             cfg.cls += ' navbar-inverse';
4073             
4074         }
4075         
4076         
4077         return cfg;
4078     
4079         
4080     }
4081     
4082     
4083     
4084 });
4085
4086
4087
4088  
4089
4090  
4091        /*
4092  * - LGPL
4093  *
4094  * navbar
4095  * navbar-fixed-top
4096  * navbar-expand-md  fixed-top 
4097  */
4098
4099 /**
4100  * @class Roo.bootstrap.NavHeaderbar
4101  * @extends Roo.bootstrap.NavSimplebar
4102  * Bootstrap Sidebar class
4103  *
4104  * @cfg {String} brand what is brand
4105  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4106  * @cfg {String} brand_href href of the brand
4107  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4108  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4109  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4110  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4111  * 
4112  * @constructor
4113  * Create a new Sidebar
4114  * @param {Object} config The config object
4115  */
4116
4117
4118 Roo.bootstrap.NavHeaderbar = function(config){
4119     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4120       
4121 };
4122
4123 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4124     
4125     position: '',
4126     brand: '',
4127     brand_href: false,
4128     srButton : true,
4129     autohide : false,
4130     desktopCenter : false,
4131    
4132     
4133     getAutoCreate : function(){
4134         
4135         var   cfg = {
4136             tag: this.nav || 'nav',
4137             cls: 'navbar navbar-expand-md',
4138             role: 'navigation',
4139             cn: []
4140         };
4141         
4142         var cn = cfg.cn;
4143         if (this.desktopCenter) {
4144             cn.push({cls : 'container', cn : []});
4145             cn = cn[0].cn;
4146         }
4147         
4148         if(this.srButton){
4149             var btn = {
4150                 tag: 'button',
4151                 type: 'button',
4152                 cls: 'navbar-toggle navbar-toggler',
4153                 'data-toggle': 'collapse',
4154                 cn: [
4155                     {
4156                         tag: 'span',
4157                         cls: 'sr-only',
4158                         html: 'Toggle navigation'
4159                     },
4160                     {
4161                         tag: 'span',
4162                         cls: 'icon-bar navbar-toggler-icon'
4163                     },
4164                     {
4165                         tag: 'span',
4166                         cls: 'icon-bar'
4167                     },
4168                     {
4169                         tag: 'span',
4170                         cls: 'icon-bar'
4171                     }
4172                 ]
4173             };
4174             
4175             cn.push( Roo.bootstrap.version == 4 ? btn : {
4176                 tag: 'div',
4177                 cls: 'navbar-header',
4178                 cn: [
4179                     btn
4180                 ]
4181             });
4182         }
4183         
4184         cn.push({
4185             tag: 'div',
4186             cls: 'collapse navbar-collapse',
4187             cn : []
4188         });
4189         
4190         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4191         
4192         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4193             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4194             
4195             // tag can override this..
4196             
4197             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4198         }
4199         
4200         if (this.brand !== '') {
4201             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4202             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4203                 tag: 'a',
4204                 href: this.brand_href ? this.brand_href : '#',
4205                 cls: 'navbar-brand',
4206                 cn: [
4207                 this.brand
4208                 ]
4209             });
4210         }
4211         
4212         if(this.main){
4213             cfg.cls += ' main-nav';
4214         }
4215         
4216         
4217         return cfg;
4218
4219         
4220     },
4221     getHeaderChildContainer : function()
4222     {
4223         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4224             return this.el.select('.navbar-header',true).first();
4225         }
4226         
4227         return this.getChildContainer();
4228     },
4229     
4230     
4231     initEvents : function()
4232     {
4233         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4234         
4235         if (this.autohide) {
4236             
4237             var prevScroll = 0;
4238             var ft = this.el;
4239             
4240             Roo.get(document).on('scroll',function(e) {
4241                 var ns = Roo.get(document).getScroll().top;
4242                 var os = prevScroll;
4243                 prevScroll = ns;
4244                 
4245                 if(ns > os){
4246                     ft.removeClass('slideDown');
4247                     ft.addClass('slideUp');
4248                     return;
4249                 }
4250                 ft.removeClass('slideUp');
4251                 ft.addClass('slideDown');
4252                  
4253               
4254           },this);
4255         }
4256     }    
4257     
4258 });
4259
4260
4261
4262  
4263
4264  /*
4265  * - LGPL
4266  *
4267  * navbar
4268  * 
4269  */
4270
4271 /**
4272  * @class Roo.bootstrap.NavSidebar
4273  * @extends Roo.bootstrap.Navbar
4274  * Bootstrap Sidebar class
4275  * 
4276  * @constructor
4277  * Create a new Sidebar
4278  * @param {Object} config The config object
4279  */
4280
4281
4282 Roo.bootstrap.NavSidebar = function(config){
4283     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4284 };
4285
4286 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4287     
4288     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4289     
4290     getAutoCreate : function(){
4291         
4292         
4293         return  {
4294             tag: 'div',
4295             cls: 'sidebar sidebar-nav'
4296         };
4297     
4298         
4299     }
4300     
4301     
4302     
4303 });
4304
4305
4306
4307  
4308
4309  /*
4310  * - LGPL
4311  *
4312  * nav group
4313  * 
4314  */
4315
4316 /**
4317  * @class Roo.bootstrap.NavGroup
4318  * @extends Roo.bootstrap.Component
4319  * Bootstrap NavGroup class
4320  * @cfg {String} align (left|right)
4321  * @cfg {Boolean} inverse
4322  * @cfg {String} type (nav|pills|tab) default nav
4323  * @cfg {String} navId - reference Id for navbar.
4324
4325  * 
4326  * @constructor
4327  * Create a new nav group
4328  * @param {Object} config The config object
4329  */
4330
4331 Roo.bootstrap.NavGroup = function(config){
4332     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4333     this.navItems = [];
4334    
4335     Roo.bootstrap.NavGroup.register(this);
4336      this.addEvents({
4337         /**
4338              * @event changed
4339              * Fires when the active item changes
4340              * @param {Roo.bootstrap.NavGroup} this
4341              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4342              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4343          */
4344         'changed': true
4345      });
4346     
4347 };
4348
4349 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4350     
4351     align: '',
4352     inverse: false,
4353     form: false,
4354     type: 'nav',
4355     navId : '',
4356     // private
4357     
4358     navItems : false, 
4359     
4360     getAutoCreate : function()
4361     {
4362         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4363         
4364         cfg = {
4365             tag : 'ul',
4366             cls: 'nav' 
4367         };
4368         
4369         if (['tabs','pills'].indexOf(this.type)!==-1) {
4370             cfg.cls += ' nav-' + this.type
4371         } else {
4372             if (this.type!=='nav') {
4373                 Roo.log('nav type must be nav/tabs/pills')
4374             }
4375             cfg.cls += ' navbar-nav'
4376         }
4377         
4378         if (this.parent() && this.parent().sidebar) {
4379             cfg = {
4380                 tag: 'ul',
4381                 cls: 'dashboard-menu sidebar-menu'
4382             };
4383             
4384             return cfg;
4385         }
4386         
4387         if (this.form === true) {
4388             cfg = {
4389                 tag: 'form',
4390                 cls: 'navbar-form'
4391             };
4392             
4393             if (this.align === 'right') {
4394                 cfg.cls += ' navbar-right ml-md-auto';
4395             } else {
4396                 cfg.cls += ' navbar-left';
4397             }
4398         }
4399         
4400         if (this.align === 'right') {
4401             cfg.cls += ' navbar-right ml-md-auto';
4402         } else {
4403             cfg.cls += ' mr-auto';
4404         }
4405         
4406         if (this.inverse) {
4407             cfg.cls += ' navbar-inverse';
4408             
4409         }
4410         
4411         
4412         return cfg;
4413     },
4414     /**
4415     * sets the active Navigation item
4416     * @param {Roo.bootstrap.NavItem} the new current navitem
4417     */
4418     setActiveItem : function(item)
4419     {
4420         var prev = false;
4421         Roo.each(this.navItems, function(v){
4422             if (v == item) {
4423                 return ;
4424             }
4425             if (v.isActive()) {
4426                 v.setActive(false, true);
4427                 prev = v;
4428                 
4429             }
4430             
4431         });
4432
4433         item.setActive(true, true);
4434         this.fireEvent('changed', this, item, prev);
4435         
4436         
4437     },
4438     /**
4439     * gets the active Navigation item
4440     * @return {Roo.bootstrap.NavItem} the current navitem
4441     */
4442     getActive : function()
4443     {
4444         
4445         var prev = false;
4446         Roo.each(this.navItems, function(v){
4447             
4448             if (v.isActive()) {
4449                 prev = v;
4450                 
4451             }
4452             
4453         });
4454         return prev;
4455     },
4456     
4457     indexOfNav : function()
4458     {
4459         
4460         var prev = false;
4461         Roo.each(this.navItems, function(v,i){
4462             
4463             if (v.isActive()) {
4464                 prev = i;
4465                 
4466             }
4467             
4468         });
4469         return prev;
4470     },
4471     /**
4472     * adds a Navigation item
4473     * @param {Roo.bootstrap.NavItem} the navitem to add
4474     */
4475     addItem : function(cfg)
4476     {
4477         var cn = new Roo.bootstrap.NavItem(cfg);
4478         this.register(cn);
4479         cn.parentId = this.id;
4480         cn.onRender(this.el, null);
4481         return cn;
4482     },
4483     /**
4484     * register a Navigation item
4485     * @param {Roo.bootstrap.NavItem} the navitem to add
4486     */
4487     register : function(item)
4488     {
4489         this.navItems.push( item);
4490         item.navId = this.navId;
4491     
4492     },
4493     
4494     /**
4495     * clear all the Navigation item
4496     */
4497    
4498     clearAll : function()
4499     {
4500         this.navItems = [];
4501         this.el.dom.innerHTML = '';
4502     },
4503     
4504     getNavItem: function(tabId)
4505     {
4506         var ret = false;
4507         Roo.each(this.navItems, function(e) {
4508             if (e.tabId == tabId) {
4509                ret =  e;
4510                return false;
4511             }
4512             return true;
4513             
4514         });
4515         return ret;
4516     },
4517     
4518     setActiveNext : function()
4519     {
4520         var i = this.indexOfNav(this.getActive());
4521         if (i > this.navItems.length) {
4522             return;
4523         }
4524         this.setActiveItem(this.navItems[i+1]);
4525     },
4526     setActivePrev : function()
4527     {
4528         var i = this.indexOfNav(this.getActive());
4529         if (i  < 1) {
4530             return;
4531         }
4532         this.setActiveItem(this.navItems[i-1]);
4533     },
4534     clearWasActive : function(except) {
4535         Roo.each(this.navItems, function(e) {
4536             if (e.tabId != except.tabId && e.was_active) {
4537                e.was_active = false;
4538                return false;
4539             }
4540             return true;
4541             
4542         });
4543     },
4544     getWasActive : function ()
4545     {
4546         var r = false;
4547         Roo.each(this.navItems, function(e) {
4548             if (e.was_active) {
4549                r = e;
4550                return false;
4551             }
4552             return true;
4553             
4554         });
4555         return r;
4556     }
4557     
4558     
4559 });
4560
4561  
4562 Roo.apply(Roo.bootstrap.NavGroup, {
4563     
4564     groups: {},
4565      /**
4566     * register a Navigation Group
4567     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4568     */
4569     register : function(navgrp)
4570     {
4571         this.groups[navgrp.navId] = navgrp;
4572         
4573     },
4574     /**
4575     * fetch a Navigation Group based on the navigation ID
4576     * @param {string} the navgroup to add
4577     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4578     */
4579     get: function(navId) {
4580         if (typeof(this.groups[navId]) == 'undefined') {
4581             return false;
4582             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4583         }
4584         return this.groups[navId] ;
4585     }
4586     
4587     
4588     
4589 });
4590
4591  /*
4592  * - LGPL
4593  *
4594  * row
4595  * 
4596  */
4597
4598 /**
4599  * @class Roo.bootstrap.NavItem
4600  * @extends Roo.bootstrap.Component
4601  * Bootstrap Navbar.NavItem class
4602  * @cfg {String} href  link to
4603  * @cfg {String} html content of button
4604  * @cfg {String} badge text inside badge
4605  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4606  * @cfg {String} glyphicon DEPRICATED - use fa
4607  * @cfg {String} icon DEPRICATED - use fa
4608  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4609  * @cfg {Boolean} active Is item active
4610  * @cfg {Boolean} disabled Is item disabled
4611  
4612  * @cfg {Boolean} preventDefault (true | false) default false
4613  * @cfg {String} tabId the tab that this item activates.
4614  * @cfg {String} tagtype (a|span) render as a href or span?
4615  * @cfg {Boolean} animateRef (true|false) link to element default false  
4616   
4617  * @constructor
4618  * Create a new Navbar Item
4619  * @param {Object} config The config object
4620  */
4621 Roo.bootstrap.NavItem = function(config){
4622     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4623     this.addEvents({
4624         // raw events
4625         /**
4626          * @event click
4627          * The raw click event for the entire grid.
4628          * @param {Roo.EventObject} e
4629          */
4630         "click" : true,
4631          /**
4632             * @event changed
4633             * Fires when the active item active state changes
4634             * @param {Roo.bootstrap.NavItem} this
4635             * @param {boolean} state the new state
4636              
4637          */
4638         'changed': true,
4639         /**
4640             * @event scrollto
4641             * Fires when scroll to element
4642             * @param {Roo.bootstrap.NavItem} this
4643             * @param {Object} options
4644             * @param {Roo.EventObject} e
4645              
4646          */
4647         'scrollto': true
4648     });
4649    
4650 };
4651
4652 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4653     
4654     href: false,
4655     html: '',
4656     badge: '',
4657     icon: false,
4658     fa : false,
4659     glyphicon: false,
4660     active: false,
4661     preventDefault : false,
4662     tabId : false,
4663     tagtype : 'a',
4664     disabled : false,
4665     animateRef : false,
4666     was_active : false,
4667     
4668     getAutoCreate : function(){
4669          
4670         var cfg = {
4671             tag: 'li',
4672             cls: 'nav-item'
4673             
4674         };
4675         
4676         if (this.active) {
4677             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4678         }
4679         if (this.disabled) {
4680             cfg.cls += ' disabled';
4681         }
4682         
4683         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4684             cfg.cn = [
4685                 {
4686                     tag: this.tagtype,
4687                     href : this.href || "#",
4688                     html: this.html || ''
4689                 }
4690             ];
4691             if (this.tagtype == 'a') {
4692                 cfg.cn[0].cls = 'nav-link';
4693             }
4694             if (this.icon) {
4695                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4696             }
4697             if (this.fa) {
4698                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4699             }
4700             if(this.glyphicon) {
4701                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4702             }
4703             
4704             if (this.menu) {
4705                 
4706                 cfg.cn[0].html += " <span class='caret'></span>";
4707              
4708             }
4709             
4710             if (this.badge !== '') {
4711                  
4712                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4713             }
4714         }
4715         
4716         
4717         
4718         return cfg;
4719     },
4720     initEvents: function() 
4721     {
4722         if (typeof (this.menu) != 'undefined') {
4723             this.menu.parentType = this.xtype;
4724             this.menu.triggerEl = this.el;
4725             this.menu = this.addxtype(Roo.apply({}, this.menu));
4726         }
4727         
4728         this.el.select('a',true).on('click', this.onClick, this);
4729         
4730         if(this.tagtype == 'span'){
4731             this.el.select('span',true).on('click', this.onClick, this);
4732         }
4733        
4734         // at this point parent should be available..
4735         this.parent().register(this);
4736     },
4737     
4738     onClick : function(e)
4739     {
4740         if (e.getTarget('.dropdown-menu-item')) {
4741             // did you click on a menu itemm.... - then don't trigger onclick..
4742             return;
4743         }
4744         
4745         if(
4746                 this.preventDefault || 
4747                 this.href == '#' 
4748         ){
4749             Roo.log("NavItem - prevent Default?");
4750             e.preventDefault();
4751         }
4752         
4753         if (this.disabled) {
4754             return;
4755         }
4756         
4757         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4758         if (tg && tg.transition) {
4759             Roo.log("waiting for the transitionend");
4760             return;
4761         }
4762         
4763         
4764         
4765         //Roo.log("fire event clicked");
4766         if(this.fireEvent('click', this, e) === false){
4767             return;
4768         };
4769         
4770         if(this.tagtype == 'span'){
4771             return;
4772         }
4773         
4774         //Roo.log(this.href);
4775         var ael = this.el.select('a',true).first();
4776         //Roo.log(ael);
4777         
4778         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4779             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4780             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4781                 return; // ignore... - it's a 'hash' to another page.
4782             }
4783             Roo.log("NavItem - prevent Default?");
4784             e.preventDefault();
4785             this.scrollToElement(e);
4786         }
4787         
4788         
4789         var p =  this.parent();
4790    
4791         if (['tabs','pills'].indexOf(p.type)!==-1) {
4792             if (typeof(p.setActiveItem) !== 'undefined') {
4793                 p.setActiveItem(this);
4794             }
4795         }
4796         
4797         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4798         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4799             // remove the collapsed menu expand...
4800             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4801         }
4802     },
4803     
4804     isActive: function () {
4805         return this.active
4806     },
4807     setActive : function(state, fire, is_was_active)
4808     {
4809         if (this.active && !state && this.navId) {
4810             this.was_active = true;
4811             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4812             if (nv) {
4813                 nv.clearWasActive(this);
4814             }
4815             
4816         }
4817         this.active = state;
4818         
4819         if (!state ) {
4820             this.el.removeClass('active');
4821         } else if (!this.el.hasClass('active')) {
4822             this.el.addClass('active');
4823         }
4824         if (fire) {
4825             this.fireEvent('changed', this, state);
4826         }
4827         
4828         // show a panel if it's registered and related..
4829         
4830         if (!this.navId || !this.tabId || !state || is_was_active) {
4831             return;
4832         }
4833         
4834         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4835         if (!tg) {
4836             return;
4837         }
4838         var pan = tg.getPanelByName(this.tabId);
4839         if (!pan) {
4840             return;
4841         }
4842         // if we can not flip to new panel - go back to old nav highlight..
4843         if (false == tg.showPanel(pan)) {
4844             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4845             if (nv) {
4846                 var onav = nv.getWasActive();
4847                 if (onav) {
4848                     onav.setActive(true, false, true);
4849                 }
4850             }
4851             
4852         }
4853         
4854         
4855         
4856     },
4857      // this should not be here...
4858     setDisabled : function(state)
4859     {
4860         this.disabled = state;
4861         if (!state ) {
4862             this.el.removeClass('disabled');
4863         } else if (!this.el.hasClass('disabled')) {
4864             this.el.addClass('disabled');
4865         }
4866         
4867     },
4868     
4869     /**
4870      * Fetch the element to display the tooltip on.
4871      * @return {Roo.Element} defaults to this.el
4872      */
4873     tooltipEl : function()
4874     {
4875         return this.el.select('' + this.tagtype + '', true).first();
4876     },
4877     
4878     scrollToElement : function(e)
4879     {
4880         var c = document.body;
4881         
4882         /*
4883          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4884          */
4885         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4886             c = document.documentElement;
4887         }
4888         
4889         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4890         
4891         if(!target){
4892             return;
4893         }
4894
4895         var o = target.calcOffsetsTo(c);
4896         
4897         var options = {
4898             target : target,
4899             value : o[1]
4900         };
4901         
4902         this.fireEvent('scrollto', this, options, e);
4903         
4904         Roo.get(c).scrollTo('top', options.value, true);
4905         
4906         return;
4907     }
4908 });
4909  
4910
4911  /*
4912  * - LGPL
4913  *
4914  * sidebar item
4915  *
4916  *  li
4917  *    <span> icon </span>
4918  *    <span> text </span>
4919  *    <span>badge </span>
4920  */
4921
4922 /**
4923  * @class Roo.bootstrap.NavSidebarItem
4924  * @extends Roo.bootstrap.NavItem
4925  * Bootstrap Navbar.NavSidebarItem class
4926  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4927  * {Boolean} open is the menu open
4928  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4929  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4930  * {String} buttonSize (sm|md|lg)the extra classes for the button
4931  * {Boolean} showArrow show arrow next to the text (default true)
4932  * @constructor
4933  * Create a new Navbar Button
4934  * @param {Object} config The config object
4935  */
4936 Roo.bootstrap.NavSidebarItem = function(config){
4937     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4938     this.addEvents({
4939         // raw events
4940         /**
4941          * @event click
4942          * The raw click event for the entire grid.
4943          * @param {Roo.EventObject} e
4944          */
4945         "click" : true,
4946          /**
4947             * @event changed
4948             * Fires when the active item active state changes
4949             * @param {Roo.bootstrap.NavSidebarItem} this
4950             * @param {boolean} state the new state
4951              
4952          */
4953         'changed': true
4954     });
4955    
4956 };
4957
4958 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4959     
4960     badgeWeight : 'default',
4961     
4962     open: false,
4963     
4964     buttonView : false,
4965     
4966     buttonWeight : 'default',
4967     
4968     buttonSize : 'md',
4969     
4970     showArrow : true,
4971     
4972     getAutoCreate : function(){
4973         
4974         
4975         var a = {
4976                 tag: 'a',
4977                 href : this.href || '#',
4978                 cls: '',
4979                 html : '',
4980                 cn : []
4981         };
4982         
4983         if(this.buttonView){
4984             a = {
4985                 tag: 'button',
4986                 href : this.href || '#',
4987                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4988                 html : this.html,
4989                 cn : []
4990             };
4991         }
4992         
4993         var cfg = {
4994             tag: 'li',
4995             cls: '',
4996             cn: [ a ]
4997         };
4998         
4999         if (this.active) {
5000             cfg.cls += ' active';
5001         }
5002         
5003         if (this.disabled) {
5004             cfg.cls += ' disabled';
5005         }
5006         if (this.open) {
5007             cfg.cls += ' open x-open';
5008         }
5009         // left icon..
5010         if (this.glyphicon || this.icon) {
5011             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5012             a.cn.push({ tag : 'i', cls : c }) ;
5013         }
5014         
5015         if(!this.buttonView){
5016             var span = {
5017                 tag: 'span',
5018                 html : this.html || ''
5019             };
5020
5021             a.cn.push(span);
5022             
5023         }
5024         
5025         if (this.badge !== '') {
5026             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5027         }
5028         
5029         if (this.menu) {
5030             
5031             if(this.showArrow){
5032                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5033             }
5034             
5035             a.cls += ' dropdown-toggle treeview' ;
5036         }
5037         
5038         return cfg;
5039     },
5040     
5041     initEvents : function()
5042     { 
5043         if (typeof (this.menu) != 'undefined') {
5044             this.menu.parentType = this.xtype;
5045             this.menu.triggerEl = this.el;
5046             this.menu = this.addxtype(Roo.apply({}, this.menu));
5047         }
5048         
5049         this.el.on('click', this.onClick, this);
5050         
5051         if(this.badge !== ''){
5052             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5053         }
5054         
5055     },
5056     
5057     onClick : function(e)
5058     {
5059         if(this.disabled){
5060             e.preventDefault();
5061             return;
5062         }
5063         
5064         if(this.preventDefault){
5065             e.preventDefault();
5066         }
5067         
5068         this.fireEvent('click', this);
5069     },
5070     
5071     disable : function()
5072     {
5073         this.setDisabled(true);
5074     },
5075     
5076     enable : function()
5077     {
5078         this.setDisabled(false);
5079     },
5080     
5081     setDisabled : function(state)
5082     {
5083         if(this.disabled == state){
5084             return;
5085         }
5086         
5087         this.disabled = state;
5088         
5089         if (state) {
5090             this.el.addClass('disabled');
5091             return;
5092         }
5093         
5094         this.el.removeClass('disabled');
5095         
5096         return;
5097     },
5098     
5099     setActive : function(state)
5100     {
5101         if(this.active == state){
5102             return;
5103         }
5104         
5105         this.active = state;
5106         
5107         if (state) {
5108             this.el.addClass('active');
5109             return;
5110         }
5111         
5112         this.el.removeClass('active');
5113         
5114         return;
5115     },
5116     
5117     isActive: function () 
5118     {
5119         return this.active;
5120     },
5121     
5122     setBadge : function(str)
5123     {
5124         if(!this.badgeEl){
5125             return;
5126         }
5127         
5128         this.badgeEl.dom.innerHTML = str;
5129     }
5130     
5131    
5132      
5133  
5134 });
5135  
5136
5137  /*
5138  * - LGPL
5139  *
5140  * row
5141  * 
5142  */
5143
5144 /**
5145  * @class Roo.bootstrap.Row
5146  * @extends Roo.bootstrap.Component
5147  * Bootstrap Row class (contains columns...)
5148  * 
5149  * @constructor
5150  * Create a new Row
5151  * @param {Object} config The config object
5152  */
5153
5154 Roo.bootstrap.Row = function(config){
5155     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5156 };
5157
5158 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5159     
5160     getAutoCreate : function(){
5161        return {
5162             cls: 'row clearfix'
5163        };
5164     }
5165     
5166     
5167 });
5168
5169  
5170
5171  /*
5172  * - LGPL
5173  *
5174  * element
5175  * 
5176  */
5177
5178 /**
5179  * @class Roo.bootstrap.Element
5180  * @extends Roo.bootstrap.Component
5181  * Bootstrap Element class
5182  * @cfg {String} html contents of the element
5183  * @cfg {String} tag tag of the element
5184  * @cfg {String} cls class of the element
5185  * @cfg {Boolean} preventDefault (true|false) default false
5186  * @cfg {Boolean} clickable (true|false) default false
5187  * 
5188  * @constructor
5189  * Create a new Element
5190  * @param {Object} config The config object
5191  */
5192
5193 Roo.bootstrap.Element = function(config){
5194     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5195     
5196     this.addEvents({
5197         // raw events
5198         /**
5199          * @event click
5200          * When a element is chick
5201          * @param {Roo.bootstrap.Element} this
5202          * @param {Roo.EventObject} e
5203          */
5204         "click" : true
5205     });
5206 };
5207
5208 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5209     
5210     tag: 'div',
5211     cls: '',
5212     html: '',
5213     preventDefault: false, 
5214     clickable: false,
5215     
5216     getAutoCreate : function(){
5217         
5218         var cfg = {
5219             tag: this.tag,
5220             // cls: this.cls, double assign in parent class Component.js :: onRender
5221             html: this.html
5222         };
5223         
5224         return cfg;
5225     },
5226     
5227     initEvents: function() 
5228     {
5229         Roo.bootstrap.Element.superclass.initEvents.call(this);
5230         
5231         if(this.clickable){
5232             this.el.on('click', this.onClick, this);
5233         }
5234         
5235     },
5236     
5237     onClick : function(e)
5238     {
5239         if(this.preventDefault){
5240             e.preventDefault();
5241         }
5242         
5243         this.fireEvent('click', this, e);
5244     },
5245     
5246     getValue : function()
5247     {
5248         return this.el.dom.innerHTML;
5249     },
5250     
5251     setValue : function(value)
5252     {
5253         this.el.dom.innerHTML = value;
5254     }
5255    
5256 });
5257
5258  
5259
5260  /*
5261  * - LGPL
5262  *
5263  * pagination
5264  * 
5265  */
5266
5267 /**
5268  * @class Roo.bootstrap.Pagination
5269  * @extends Roo.bootstrap.Component
5270  * Bootstrap Pagination class
5271  * @cfg {String} size xs | sm | md | lg
5272  * @cfg {Boolean} inverse false | true
5273  * 
5274  * @constructor
5275  * Create a new Pagination
5276  * @param {Object} config The config object
5277  */
5278
5279 Roo.bootstrap.Pagination = function(config){
5280     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5281 };
5282
5283 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5284     
5285     cls: false,
5286     size: false,
5287     inverse: false,
5288     
5289     getAutoCreate : function(){
5290         var cfg = {
5291             tag: 'ul',
5292                 cls: 'pagination'
5293         };
5294         if (this.inverse) {
5295             cfg.cls += ' inverse';
5296         }
5297         if (this.html) {
5298             cfg.html=this.html;
5299         }
5300         if (this.cls) {
5301             cfg.cls += " " + this.cls;
5302         }
5303         return cfg;
5304     }
5305    
5306 });
5307
5308  
5309
5310  /*
5311  * - LGPL
5312  *
5313  * Pagination item
5314  * 
5315  */
5316
5317
5318 /**
5319  * @class Roo.bootstrap.PaginationItem
5320  * @extends Roo.bootstrap.Component
5321  * Bootstrap PaginationItem class
5322  * @cfg {String} html text
5323  * @cfg {String} href the link
5324  * @cfg {Boolean} preventDefault (true | false) default true
5325  * @cfg {Boolean} active (true | false) default false
5326  * @cfg {Boolean} disabled default false
5327  * 
5328  * 
5329  * @constructor
5330  * Create a new PaginationItem
5331  * @param {Object} config The config object
5332  */
5333
5334
5335 Roo.bootstrap.PaginationItem = function(config){
5336     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5337     this.addEvents({
5338         // raw events
5339         /**
5340          * @event click
5341          * The raw click event for the entire grid.
5342          * @param {Roo.EventObject} e
5343          */
5344         "click" : true
5345     });
5346 };
5347
5348 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5349     
5350     href : false,
5351     html : false,
5352     preventDefault: true,
5353     active : false,
5354     cls : false,
5355     disabled: false,
5356     
5357     getAutoCreate : function(){
5358         var cfg= {
5359             tag: 'li',
5360             cn: [
5361                 {
5362                     tag : 'a',
5363                     href : this.href ? this.href : '#',
5364                     html : this.html ? this.html : ''
5365                 }
5366             ]
5367         };
5368         
5369         if(this.cls){
5370             cfg.cls = this.cls;
5371         }
5372         
5373         if(this.disabled){
5374             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5375         }
5376         
5377         if(this.active){
5378             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5379         }
5380         
5381         return cfg;
5382     },
5383     
5384     initEvents: function() {
5385         
5386         this.el.on('click', this.onClick, this);
5387         
5388     },
5389     onClick : function(e)
5390     {
5391         Roo.log('PaginationItem on click ');
5392         if(this.preventDefault){
5393             e.preventDefault();
5394         }
5395         
5396         if(this.disabled){
5397             return;
5398         }
5399         
5400         this.fireEvent('click', this, e);
5401     }
5402    
5403 });
5404
5405  
5406
5407  /*
5408  * - LGPL
5409  *
5410  * slider
5411  * 
5412  */
5413
5414
5415 /**
5416  * @class Roo.bootstrap.Slider
5417  * @extends Roo.bootstrap.Component
5418  * Bootstrap Slider class
5419  *    
5420  * @constructor
5421  * Create a new Slider
5422  * @param {Object} config The config object
5423  */
5424
5425 Roo.bootstrap.Slider = function(config){
5426     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5427 };
5428
5429 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5430     
5431     getAutoCreate : function(){
5432         
5433         var cfg = {
5434             tag: 'div',
5435             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5436             cn: [
5437                 {
5438                     tag: 'a',
5439                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5440                 }
5441             ]
5442         };
5443         
5444         return cfg;
5445     }
5446    
5447 });
5448
5449  /*
5450  * Based on:
5451  * Ext JS Library 1.1.1
5452  * Copyright(c) 2006-2007, Ext JS, LLC.
5453  *
5454  * Originally Released Under LGPL - original licence link has changed is not relivant.
5455  *
5456  * Fork - LGPL
5457  * <script type="text/javascript">
5458  */
5459  
5460
5461 /**
5462  * @class Roo.grid.ColumnModel
5463  * @extends Roo.util.Observable
5464  * This is the default implementation of a ColumnModel used by the Grid. It defines
5465  * the columns in the grid.
5466  * <br>Usage:<br>
5467  <pre><code>
5468  var colModel = new Roo.grid.ColumnModel([
5469         {header: "Ticker", width: 60, sortable: true, locked: true},
5470         {header: "Company Name", width: 150, sortable: true},
5471         {header: "Market Cap.", width: 100, sortable: true},
5472         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5473         {header: "Employees", width: 100, sortable: true, resizable: false}
5474  ]);
5475  </code></pre>
5476  * <p>
5477  
5478  * The config options listed for this class are options which may appear in each
5479  * individual column definition.
5480  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5481  * @constructor
5482  * @param {Object} config An Array of column config objects. See this class's
5483  * config objects for details.
5484 */
5485 Roo.grid.ColumnModel = function(config){
5486         /**
5487      * The config passed into the constructor
5488      */
5489     this.config = config;
5490     this.lookup = {};
5491
5492     // if no id, create one
5493     // if the column does not have a dataIndex mapping,
5494     // map it to the order it is in the config
5495     for(var i = 0, len = config.length; i < len; i++){
5496         var c = config[i];
5497         if(typeof c.dataIndex == "undefined"){
5498             c.dataIndex = i;
5499         }
5500         if(typeof c.renderer == "string"){
5501             c.renderer = Roo.util.Format[c.renderer];
5502         }
5503         if(typeof c.id == "undefined"){
5504             c.id = Roo.id();
5505         }
5506         if(c.editor && c.editor.xtype){
5507             c.editor  = Roo.factory(c.editor, Roo.grid);
5508         }
5509         if(c.editor && c.editor.isFormField){
5510             c.editor = new Roo.grid.GridEditor(c.editor);
5511         }
5512         this.lookup[c.id] = c;
5513     }
5514
5515     /**
5516      * The width of columns which have no width specified (defaults to 100)
5517      * @type Number
5518      */
5519     this.defaultWidth = 100;
5520
5521     /**
5522      * Default sortable of columns which have no sortable specified (defaults to false)
5523      * @type Boolean
5524      */
5525     this.defaultSortable = false;
5526
5527     this.addEvents({
5528         /**
5529              * @event widthchange
5530              * Fires when the width of a column changes.
5531              * @param {ColumnModel} this
5532              * @param {Number} columnIndex The column index
5533              * @param {Number} newWidth The new width
5534              */
5535             "widthchange": true,
5536         /**
5537              * @event headerchange
5538              * Fires when the text of a header changes.
5539              * @param {ColumnModel} this
5540              * @param {Number} columnIndex The column index
5541              * @param {Number} newText The new header text
5542              */
5543             "headerchange": true,
5544         /**
5545              * @event hiddenchange
5546              * Fires when a column is hidden or "unhidden".
5547              * @param {ColumnModel} this
5548              * @param {Number} columnIndex The column index
5549              * @param {Boolean} hidden true if hidden, false otherwise
5550              */
5551             "hiddenchange": true,
5552             /**
5553          * @event columnmoved
5554          * Fires when a column is moved.
5555          * @param {ColumnModel} this
5556          * @param {Number} oldIndex
5557          * @param {Number} newIndex
5558          */
5559         "columnmoved" : true,
5560         /**
5561          * @event columlockchange
5562          * Fires when a column's locked state is changed
5563          * @param {ColumnModel} this
5564          * @param {Number} colIndex
5565          * @param {Boolean} locked true if locked
5566          */
5567         "columnlockchange" : true
5568     });
5569     Roo.grid.ColumnModel.superclass.constructor.call(this);
5570 };
5571 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5572     /**
5573      * @cfg {String} header The header text to display in the Grid view.
5574      */
5575     /**
5576      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5577      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5578      * specified, the column's index is used as an index into the Record's data Array.
5579      */
5580     /**
5581      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5582      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5583      */
5584     /**
5585      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5586      * Defaults to the value of the {@link #defaultSortable} property.
5587      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5588      */
5589     /**
5590      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5591      */
5592     /**
5593      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5594      */
5595     /**
5596      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5597      */
5598     /**
5599      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5600      */
5601     /**
5602      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5603      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5604      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5605      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5606      */
5607        /**
5608      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5609      */
5610     /**
5611      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5612      */
5613     /**
5614      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5615      */
5616     /**
5617      * @cfg {String} cursor (Optional)
5618      */
5619     /**
5620      * @cfg {String} tooltip (Optional)
5621      */
5622     /**
5623      * @cfg {Number} xs (Optional)
5624      */
5625     /**
5626      * @cfg {Number} sm (Optional)
5627      */
5628     /**
5629      * @cfg {Number} md (Optional)
5630      */
5631     /**
5632      * @cfg {Number} lg (Optional)
5633      */
5634     /**
5635      * Returns the id of the column at the specified index.
5636      * @param {Number} index The column index
5637      * @return {String} the id
5638      */
5639     getColumnId : function(index){
5640         return this.config[index].id;
5641     },
5642
5643     /**
5644      * Returns the column for a specified id.
5645      * @param {String} id The column id
5646      * @return {Object} the column
5647      */
5648     getColumnById : function(id){
5649         return this.lookup[id];
5650     },
5651
5652     
5653     /**
5654      * Returns the column for a specified dataIndex.
5655      * @param {String} dataIndex The column dataIndex
5656      * @return {Object|Boolean} the column or false if not found
5657      */
5658     getColumnByDataIndex: function(dataIndex){
5659         var index = this.findColumnIndex(dataIndex);
5660         return index > -1 ? this.config[index] : false;
5661     },
5662     
5663     /**
5664      * Returns the index for a specified column id.
5665      * @param {String} id The column id
5666      * @return {Number} the index, or -1 if not found
5667      */
5668     getIndexById : function(id){
5669         for(var i = 0, len = this.config.length; i < len; i++){
5670             if(this.config[i].id == id){
5671                 return i;
5672             }
5673         }
5674         return -1;
5675     },
5676     
5677     /**
5678      * Returns the index for a specified column dataIndex.
5679      * @param {String} dataIndex The column dataIndex
5680      * @return {Number} the index, or -1 if not found
5681      */
5682     
5683     findColumnIndex : function(dataIndex){
5684         for(var i = 0, len = this.config.length; i < len; i++){
5685             if(this.config[i].dataIndex == dataIndex){
5686                 return i;
5687             }
5688         }
5689         return -1;
5690     },
5691     
5692     
5693     moveColumn : function(oldIndex, newIndex){
5694         var c = this.config[oldIndex];
5695         this.config.splice(oldIndex, 1);
5696         this.config.splice(newIndex, 0, c);
5697         this.dataMap = null;
5698         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5699     },
5700
5701     isLocked : function(colIndex){
5702         return this.config[colIndex].locked === true;
5703     },
5704
5705     setLocked : function(colIndex, value, suppressEvent){
5706         if(this.isLocked(colIndex) == value){
5707             return;
5708         }
5709         this.config[colIndex].locked = value;
5710         if(!suppressEvent){
5711             this.fireEvent("columnlockchange", this, colIndex, value);
5712         }
5713     },
5714
5715     getTotalLockedWidth : function(){
5716         var totalWidth = 0;
5717         for(var i = 0; i < this.config.length; i++){
5718             if(this.isLocked(i) && !this.isHidden(i)){
5719                 this.totalWidth += this.getColumnWidth(i);
5720             }
5721         }
5722         return totalWidth;
5723     },
5724
5725     getLockedCount : function(){
5726         for(var i = 0, len = this.config.length; i < len; i++){
5727             if(!this.isLocked(i)){
5728                 return i;
5729             }
5730         }
5731         
5732         return this.config.length;
5733     },
5734
5735     /**
5736      * Returns the number of columns.
5737      * @return {Number}
5738      */
5739     getColumnCount : function(visibleOnly){
5740         if(visibleOnly === true){
5741             var c = 0;
5742             for(var i = 0, len = this.config.length; i < len; i++){
5743                 if(!this.isHidden(i)){
5744                     c++;
5745                 }
5746             }
5747             return c;
5748         }
5749         return this.config.length;
5750     },
5751
5752     /**
5753      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5754      * @param {Function} fn
5755      * @param {Object} scope (optional)
5756      * @return {Array} result
5757      */
5758     getColumnsBy : function(fn, scope){
5759         var r = [];
5760         for(var i = 0, len = this.config.length; i < len; i++){
5761             var c = this.config[i];
5762             if(fn.call(scope||this, c, i) === true){
5763                 r[r.length] = c;
5764             }
5765         }
5766         return r;
5767     },
5768
5769     /**
5770      * Returns true if the specified column is sortable.
5771      * @param {Number} col The column index
5772      * @return {Boolean}
5773      */
5774     isSortable : function(col){
5775         if(typeof this.config[col].sortable == "undefined"){
5776             return this.defaultSortable;
5777         }
5778         return this.config[col].sortable;
5779     },
5780
5781     /**
5782      * Returns the rendering (formatting) function defined for the column.
5783      * @param {Number} col The column index.
5784      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5785      */
5786     getRenderer : function(col){
5787         if(!this.config[col].renderer){
5788             return Roo.grid.ColumnModel.defaultRenderer;
5789         }
5790         return this.config[col].renderer;
5791     },
5792
5793     /**
5794      * Sets the rendering (formatting) function for a column.
5795      * @param {Number} col The column index
5796      * @param {Function} fn The function to use to process the cell's raw data
5797      * to return HTML markup for the grid view. The render function is called with
5798      * the following parameters:<ul>
5799      * <li>Data value.</li>
5800      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5801      * <li>css A CSS style string to apply to the table cell.</li>
5802      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5803      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5804      * <li>Row index</li>
5805      * <li>Column index</li>
5806      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5807      */
5808     setRenderer : function(col, fn){
5809         this.config[col].renderer = fn;
5810     },
5811
5812     /**
5813      * Returns the width for the specified column.
5814      * @param {Number} col The column index
5815      * @return {Number}
5816      */
5817     getColumnWidth : function(col){
5818         return this.config[col].width * 1 || this.defaultWidth;
5819     },
5820
5821     /**
5822      * Sets the width for a column.
5823      * @param {Number} col The column index
5824      * @param {Number} width The new width
5825      */
5826     setColumnWidth : function(col, width, suppressEvent){
5827         this.config[col].width = width;
5828         this.totalWidth = null;
5829         if(!suppressEvent){
5830              this.fireEvent("widthchange", this, col, width);
5831         }
5832     },
5833
5834     /**
5835      * Returns the total width of all columns.
5836      * @param {Boolean} includeHidden True to include hidden column widths
5837      * @return {Number}
5838      */
5839     getTotalWidth : function(includeHidden){
5840         if(!this.totalWidth){
5841             this.totalWidth = 0;
5842             for(var i = 0, len = this.config.length; i < len; i++){
5843                 if(includeHidden || !this.isHidden(i)){
5844                     this.totalWidth += this.getColumnWidth(i);
5845                 }
5846             }
5847         }
5848         return this.totalWidth;
5849     },
5850
5851     /**
5852      * Returns the header for the specified column.
5853      * @param {Number} col The column index
5854      * @return {String}
5855      */
5856     getColumnHeader : function(col){
5857         return this.config[col].header;
5858     },
5859
5860     /**
5861      * Sets the header for a column.
5862      * @param {Number} col The column index
5863      * @param {String} header The new header
5864      */
5865     setColumnHeader : function(col, header){
5866         this.config[col].header = header;
5867         this.fireEvent("headerchange", this, col, header);
5868     },
5869
5870     /**
5871      * Returns the tooltip for the specified column.
5872      * @param {Number} col The column index
5873      * @return {String}
5874      */
5875     getColumnTooltip : function(col){
5876             return this.config[col].tooltip;
5877     },
5878     /**
5879      * Sets the tooltip for a column.
5880      * @param {Number} col The column index
5881      * @param {String} tooltip The new tooltip
5882      */
5883     setColumnTooltip : function(col, tooltip){
5884             this.config[col].tooltip = tooltip;
5885     },
5886
5887     /**
5888      * Returns the dataIndex for the specified column.
5889      * @param {Number} col The column index
5890      * @return {Number}
5891      */
5892     getDataIndex : function(col){
5893         return this.config[col].dataIndex;
5894     },
5895
5896     /**
5897      * Sets the dataIndex for a column.
5898      * @param {Number} col The column index
5899      * @param {Number} dataIndex The new dataIndex
5900      */
5901     setDataIndex : function(col, dataIndex){
5902         this.config[col].dataIndex = dataIndex;
5903     },
5904
5905     
5906     
5907     /**
5908      * Returns true if the cell is editable.
5909      * @param {Number} colIndex The column index
5910      * @param {Number} rowIndex The row index - this is nto actually used..?
5911      * @return {Boolean}
5912      */
5913     isCellEditable : function(colIndex, rowIndex){
5914         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5915     },
5916
5917     /**
5918      * Returns the editor defined for the cell/column.
5919      * return false or null to disable editing.
5920      * @param {Number} colIndex The column index
5921      * @param {Number} rowIndex The row index
5922      * @return {Object}
5923      */
5924     getCellEditor : function(colIndex, rowIndex){
5925         return this.config[colIndex].editor;
5926     },
5927
5928     /**
5929      * Sets if a column is editable.
5930      * @param {Number} col The column index
5931      * @param {Boolean} editable True if the column is editable
5932      */
5933     setEditable : function(col, editable){
5934         this.config[col].editable = editable;
5935     },
5936
5937
5938     /**
5939      * Returns true if the column is hidden.
5940      * @param {Number} colIndex The column index
5941      * @return {Boolean}
5942      */
5943     isHidden : function(colIndex){
5944         return this.config[colIndex].hidden;
5945     },
5946
5947
5948     /**
5949      * Returns true if the column width cannot be changed
5950      */
5951     isFixed : function(colIndex){
5952         return this.config[colIndex].fixed;
5953     },
5954
5955     /**
5956      * Returns true if the column can be resized
5957      * @return {Boolean}
5958      */
5959     isResizable : function(colIndex){
5960         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5961     },
5962     /**
5963      * Sets if a column is hidden.
5964      * @param {Number} colIndex The column index
5965      * @param {Boolean} hidden True if the column is hidden
5966      */
5967     setHidden : function(colIndex, hidden){
5968         this.config[colIndex].hidden = hidden;
5969         this.totalWidth = null;
5970         this.fireEvent("hiddenchange", this, colIndex, hidden);
5971     },
5972
5973     /**
5974      * Sets the editor for a column.
5975      * @param {Number} col The column index
5976      * @param {Object} editor The editor object
5977      */
5978     setEditor : function(col, editor){
5979         this.config[col].editor = editor;
5980     }
5981 });
5982
5983 Roo.grid.ColumnModel.defaultRenderer = function(value)
5984 {
5985     if(typeof value == "object") {
5986         return value;
5987     }
5988         if(typeof value == "string" && value.length < 1){
5989             return "&#160;";
5990         }
5991     
5992         return String.format("{0}", value);
5993 };
5994
5995 // Alias for backwards compatibility
5996 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5997 /*
5998  * Based on:
5999  * Ext JS Library 1.1.1
6000  * Copyright(c) 2006-2007, Ext JS, LLC.
6001  *
6002  * Originally Released Under LGPL - original licence link has changed is not relivant.
6003  *
6004  * Fork - LGPL
6005  * <script type="text/javascript">
6006  */
6007  
6008 /**
6009  * @class Roo.LoadMask
6010  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6011  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6012  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6013  * element's UpdateManager load indicator and will be destroyed after the initial load.
6014  * @constructor
6015  * Create a new LoadMask
6016  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6017  * @param {Object} config The config object
6018  */
6019 Roo.LoadMask = function(el, config){
6020     this.el = Roo.get(el);
6021     Roo.apply(this, config);
6022     if(this.store){
6023         this.store.on('beforeload', this.onBeforeLoad, this);
6024         this.store.on('load', this.onLoad, this);
6025         this.store.on('loadexception', this.onLoadException, this);
6026         this.removeMask = false;
6027     }else{
6028         var um = this.el.getUpdateManager();
6029         um.showLoadIndicator = false; // disable the default indicator
6030         um.on('beforeupdate', this.onBeforeLoad, this);
6031         um.on('update', this.onLoad, this);
6032         um.on('failure', this.onLoad, this);
6033         this.removeMask = true;
6034     }
6035 };
6036
6037 Roo.LoadMask.prototype = {
6038     /**
6039      * @cfg {Boolean} removeMask
6040      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6041      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6042      */
6043     /**
6044      * @cfg {String} msg
6045      * The text to display in a centered loading message box (defaults to 'Loading...')
6046      */
6047     msg : 'Loading...',
6048     /**
6049      * @cfg {String} msgCls
6050      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6051      */
6052     msgCls : 'x-mask-loading',
6053
6054     /**
6055      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6056      * @type Boolean
6057      */
6058     disabled: false,
6059
6060     /**
6061      * Disables the mask to prevent it from being displayed
6062      */
6063     disable : function(){
6064        this.disabled = true;
6065     },
6066
6067     /**
6068      * Enables the mask so that it can be displayed
6069      */
6070     enable : function(){
6071         this.disabled = false;
6072     },
6073     
6074     onLoadException : function()
6075     {
6076         Roo.log(arguments);
6077         
6078         if (typeof(arguments[3]) != 'undefined') {
6079             Roo.MessageBox.alert("Error loading",arguments[3]);
6080         } 
6081         /*
6082         try {
6083             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6084                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6085             }   
6086         } catch(e) {
6087             
6088         }
6089         */
6090     
6091         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6092     },
6093     // private
6094     onLoad : function()
6095     {
6096         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6097     },
6098
6099     // private
6100     onBeforeLoad : function(){
6101         if(!this.disabled){
6102             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6103         }
6104     },
6105
6106     // private
6107     destroy : function(){
6108         if(this.store){
6109             this.store.un('beforeload', this.onBeforeLoad, this);
6110             this.store.un('load', this.onLoad, this);
6111             this.store.un('loadexception', this.onLoadException, this);
6112         }else{
6113             var um = this.el.getUpdateManager();
6114             um.un('beforeupdate', this.onBeforeLoad, this);
6115             um.un('update', this.onLoad, this);
6116             um.un('failure', this.onLoad, this);
6117         }
6118     }
6119 };/*
6120  * - LGPL
6121  *
6122  * table
6123  * 
6124  */
6125
6126 /**
6127  * @class Roo.bootstrap.Table
6128  * @extends Roo.bootstrap.Component
6129  * Bootstrap Table class
6130  * @cfg {String} cls table class
6131  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6132  * @cfg {String} bgcolor Specifies the background color for a table
6133  * @cfg {Number} border Specifies whether the table cells should have borders or not
6134  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6135  * @cfg {Number} cellspacing Specifies the space between cells
6136  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6137  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6138  * @cfg {String} sortable Specifies that the table should be sortable
6139  * @cfg {String} summary Specifies a summary of the content of a table
6140  * @cfg {Number} width Specifies the width of a table
6141  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6142  * 
6143  * @cfg {boolean} striped Should the rows be alternative striped
6144  * @cfg {boolean} bordered Add borders to the table
6145  * @cfg {boolean} hover Add hover highlighting
6146  * @cfg {boolean} condensed Format condensed
6147  * @cfg {boolean} responsive Format condensed
6148  * @cfg {Boolean} loadMask (true|false) default false
6149  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6150  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6151  * @cfg {Boolean} rowSelection (true|false) default false
6152  * @cfg {Boolean} cellSelection (true|false) default false
6153  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6154  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6155  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6156  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6157  
6158  * 
6159  * @constructor
6160  * Create a new Table
6161  * @param {Object} config The config object
6162  */
6163
6164 Roo.bootstrap.Table = function(config){
6165     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6166     
6167   
6168     
6169     // BC...
6170     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6171     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6172     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6173     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6174     
6175     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6176     if (this.sm) {
6177         this.sm.grid = this;
6178         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6179         this.sm = this.selModel;
6180         this.sm.xmodule = this.xmodule || false;
6181     }
6182     
6183     if (this.cm && typeof(this.cm.config) == 'undefined') {
6184         this.colModel = new Roo.grid.ColumnModel(this.cm);
6185         this.cm = this.colModel;
6186         this.cm.xmodule = this.xmodule || false;
6187     }
6188     if (this.store) {
6189         this.store= Roo.factory(this.store, Roo.data);
6190         this.ds = this.store;
6191         this.ds.xmodule = this.xmodule || false;
6192          
6193     }
6194     if (this.footer && this.store) {
6195         this.footer.dataSource = this.ds;
6196         this.footer = Roo.factory(this.footer);
6197     }
6198     
6199     /** @private */
6200     this.addEvents({
6201         /**
6202          * @event cellclick
6203          * Fires when a cell is clicked
6204          * @param {Roo.bootstrap.Table} this
6205          * @param {Roo.Element} el
6206          * @param {Number} rowIndex
6207          * @param {Number} columnIndex
6208          * @param {Roo.EventObject} e
6209          */
6210         "cellclick" : true,
6211         /**
6212          * @event celldblclick
6213          * Fires when a cell is double clicked
6214          * @param {Roo.bootstrap.Table} this
6215          * @param {Roo.Element} el
6216          * @param {Number} rowIndex
6217          * @param {Number} columnIndex
6218          * @param {Roo.EventObject} e
6219          */
6220         "celldblclick" : true,
6221         /**
6222          * @event rowclick
6223          * Fires when a row is clicked
6224          * @param {Roo.bootstrap.Table} this
6225          * @param {Roo.Element} el
6226          * @param {Number} rowIndex
6227          * @param {Roo.EventObject} e
6228          */
6229         "rowclick" : true,
6230         /**
6231          * @event rowdblclick
6232          * Fires when a row is double clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Roo.Element} el
6235          * @param {Number} rowIndex
6236          * @param {Roo.EventObject} e
6237          */
6238         "rowdblclick" : true,
6239         /**
6240          * @event mouseover
6241          * Fires when a mouseover occur
6242          * @param {Roo.bootstrap.Table} this
6243          * @param {Roo.Element} el
6244          * @param {Number} rowIndex
6245          * @param {Number} columnIndex
6246          * @param {Roo.EventObject} e
6247          */
6248         "mouseover" : true,
6249         /**
6250          * @event mouseout
6251          * Fires when a mouseout occur
6252          * @param {Roo.bootstrap.Table} this
6253          * @param {Roo.Element} el
6254          * @param {Number} rowIndex
6255          * @param {Number} columnIndex
6256          * @param {Roo.EventObject} e
6257          */
6258         "mouseout" : true,
6259         /**
6260          * @event rowclass
6261          * Fires when a row is rendered, so you can change add a style to it.
6262          * @param {Roo.bootstrap.Table} this
6263          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6264          */
6265         'rowclass' : true,
6266           /**
6267          * @event rowsrendered
6268          * Fires when all the  rows have been rendered
6269          * @param {Roo.bootstrap.Table} this
6270          */
6271         'rowsrendered' : true,
6272         /**
6273          * @event contextmenu
6274          * The raw contextmenu event for the entire grid.
6275          * @param {Roo.EventObject} e
6276          */
6277         "contextmenu" : true,
6278         /**
6279          * @event rowcontextmenu
6280          * Fires when a row is right clicked
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Number} rowIndex
6283          * @param {Roo.EventObject} e
6284          */
6285         "rowcontextmenu" : true,
6286         /**
6287          * @event cellcontextmenu
6288          * Fires when a cell is right clicked
6289          * @param {Roo.bootstrap.Table} this
6290          * @param {Number} rowIndex
6291          * @param {Number} cellIndex
6292          * @param {Roo.EventObject} e
6293          */
6294          "cellcontextmenu" : true,
6295          /**
6296          * @event headercontextmenu
6297          * Fires when a header is right clicked
6298          * @param {Roo.bootstrap.Table} this
6299          * @param {Number} columnIndex
6300          * @param {Roo.EventObject} e
6301          */
6302         "headercontextmenu" : true
6303     });
6304 };
6305
6306 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6307     
6308     cls: false,
6309     align: false,
6310     bgcolor: false,
6311     border: false,
6312     cellpadding: false,
6313     cellspacing: false,
6314     frame: false,
6315     rules: false,
6316     sortable: false,
6317     summary: false,
6318     width: false,
6319     striped : false,
6320     scrollBody : false,
6321     bordered: false,
6322     hover:  false,
6323     condensed : false,
6324     responsive : false,
6325     sm : false,
6326     cm : false,
6327     store : false,
6328     loadMask : false,
6329     footerShow : true,
6330     headerShow : true,
6331   
6332     rowSelection : false,
6333     cellSelection : false,
6334     layout : false,
6335     
6336     // Roo.Element - the tbody
6337     mainBody: false,
6338     // Roo.Element - thead element
6339     mainHead: false,
6340     
6341     container: false, // used by gridpanel...
6342     
6343     lazyLoad : false,
6344     
6345     CSS : Roo.util.CSS,
6346     
6347     auto_hide_footer : false,
6348     
6349     getAutoCreate : function()
6350     {
6351         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6352         
6353         cfg = {
6354             tag: 'table',
6355             cls : 'table',
6356             cn : []
6357         };
6358         if (this.scrollBody) {
6359             cfg.cls += ' table-body-fixed';
6360         }    
6361         if (this.striped) {
6362             cfg.cls += ' table-striped';
6363         }
6364         
6365         if (this.hover) {
6366             cfg.cls += ' table-hover';
6367         }
6368         if (this.bordered) {
6369             cfg.cls += ' table-bordered';
6370         }
6371         if (this.condensed) {
6372             cfg.cls += ' table-condensed';
6373         }
6374         if (this.responsive) {
6375             cfg.cls += ' table-responsive';
6376         }
6377         
6378         if (this.cls) {
6379             cfg.cls+=  ' ' +this.cls;
6380         }
6381         
6382         // this lot should be simplifed...
6383         var _t = this;
6384         var cp = [
6385             'align',
6386             'bgcolor',
6387             'border',
6388             'cellpadding',
6389             'cellspacing',
6390             'frame',
6391             'rules',
6392             'sortable',
6393             'summary',
6394             'width'
6395         ].forEach(function(k) {
6396             if (_t[k]) {
6397                 cfg[k] = _t[k];
6398             }
6399         });
6400         
6401         
6402         if (this.layout) {
6403             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6404         }
6405         
6406         if(this.store || this.cm){
6407             if(this.headerShow){
6408                 cfg.cn.push(this.renderHeader());
6409             }
6410             
6411             cfg.cn.push(this.renderBody());
6412             
6413             if(this.footerShow){
6414                 cfg.cn.push(this.renderFooter());
6415             }
6416             // where does this come from?
6417             //cfg.cls+=  ' TableGrid';
6418         }
6419         
6420         return { cn : [ cfg ] };
6421     },
6422     
6423     initEvents : function()
6424     {   
6425         if(!this.store || !this.cm){
6426             return;
6427         }
6428         if (this.selModel) {
6429             this.selModel.initEvents();
6430         }
6431         
6432         
6433         //Roo.log('initEvents with ds!!!!');
6434         
6435         this.mainBody = this.el.select('tbody', true).first();
6436         this.mainHead = this.el.select('thead', true).first();
6437         this.mainFoot = this.el.select('tfoot', true).first();
6438         
6439         
6440         
6441         var _this = this;
6442         
6443         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6444             e.on('click', _this.sort, _this);
6445         });
6446         
6447         this.mainBody.on("click", this.onClick, this);
6448         this.mainBody.on("dblclick", this.onDblClick, this);
6449         
6450         // why is this done????? = it breaks dialogs??
6451         //this.parent().el.setStyle('position', 'relative');
6452         
6453         
6454         if (this.footer) {
6455             this.footer.parentId = this.id;
6456             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6457             
6458             if(this.lazyLoad){
6459                 this.el.select('tfoot tr td').first().addClass('hide');
6460             }
6461         } 
6462         
6463         if(this.loadMask) {
6464             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6465         }
6466         
6467         this.store.on('load', this.onLoad, this);
6468         this.store.on('beforeload', this.onBeforeLoad, this);
6469         this.store.on('update', this.onUpdate, this);
6470         this.store.on('add', this.onAdd, this);
6471         this.store.on("clear", this.clear, this);
6472         
6473         this.el.on("contextmenu", this.onContextMenu, this);
6474         
6475         this.mainBody.on('scroll', this.onBodyScroll, this);
6476         
6477         this.cm.on("headerchange", this.onHeaderChange, this);
6478         
6479         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6480         
6481     },
6482     
6483     onContextMenu : function(e, t)
6484     {
6485         this.processEvent("contextmenu", e);
6486     },
6487     
6488     processEvent : function(name, e)
6489     {
6490         if (name != 'touchstart' ) {
6491             this.fireEvent(name, e);    
6492         }
6493         
6494         var t = e.getTarget();
6495         
6496         var cell = Roo.get(t);
6497         
6498         if(!cell){
6499             return;
6500         }
6501         
6502         if(cell.findParent('tfoot', false, true)){
6503             return;
6504         }
6505         
6506         if(cell.findParent('thead', false, true)){
6507             
6508             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6509                 cell = Roo.get(t).findParent('th', false, true);
6510                 if (!cell) {
6511                     Roo.log("failed to find th in thead?");
6512                     Roo.log(e.getTarget());
6513                     return;
6514                 }
6515             }
6516             
6517             var cellIndex = cell.dom.cellIndex;
6518             
6519             var ename = name == 'touchstart' ? 'click' : name;
6520             this.fireEvent("header" + ename, this, cellIndex, e);
6521             
6522             return;
6523         }
6524         
6525         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6526             cell = Roo.get(t).findParent('td', false, true);
6527             if (!cell) {
6528                 Roo.log("failed to find th in tbody?");
6529                 Roo.log(e.getTarget());
6530                 return;
6531             }
6532         }
6533         
6534         var row = cell.findParent('tr', false, true);
6535         var cellIndex = cell.dom.cellIndex;
6536         var rowIndex = row.dom.rowIndex - 1;
6537         
6538         if(row !== false){
6539             
6540             this.fireEvent("row" + name, this, rowIndex, e);
6541             
6542             if(cell !== false){
6543             
6544                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6545             }
6546         }
6547         
6548     },
6549     
6550     onMouseover : function(e, el)
6551     {
6552         var cell = Roo.get(el);
6553         
6554         if(!cell){
6555             return;
6556         }
6557         
6558         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6559             cell = cell.findParent('td', false, true);
6560         }
6561         
6562         var row = cell.findParent('tr', false, true);
6563         var cellIndex = cell.dom.cellIndex;
6564         var rowIndex = row.dom.rowIndex - 1; // start from 0
6565         
6566         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6567         
6568     },
6569     
6570     onMouseout : function(e, el)
6571     {
6572         var cell = Roo.get(el);
6573         
6574         if(!cell){
6575             return;
6576         }
6577         
6578         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6579             cell = cell.findParent('td', false, true);
6580         }
6581         
6582         var row = cell.findParent('tr', false, true);
6583         var cellIndex = cell.dom.cellIndex;
6584         var rowIndex = row.dom.rowIndex - 1; // start from 0
6585         
6586         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6587         
6588     },
6589     
6590     onClick : function(e, el)
6591     {
6592         var cell = Roo.get(el);
6593         
6594         if(!cell || (!this.cellSelection && !this.rowSelection)){
6595             return;
6596         }
6597         
6598         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6599             cell = cell.findParent('td', false, true);
6600         }
6601         
6602         if(!cell || typeof(cell) == 'undefined'){
6603             return;
6604         }
6605         
6606         var row = cell.findParent('tr', false, true);
6607         
6608         if(!row || typeof(row) == 'undefined'){
6609             return;
6610         }
6611         
6612         var cellIndex = cell.dom.cellIndex;
6613         var rowIndex = this.getRowIndex(row);
6614         
6615         // why??? - should these not be based on SelectionModel?
6616         if(this.cellSelection){
6617             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6618         }
6619         
6620         if(this.rowSelection){
6621             this.fireEvent('rowclick', this, row, rowIndex, e);
6622         }
6623         
6624         
6625     },
6626         
6627     onDblClick : function(e,el)
6628     {
6629         var cell = Roo.get(el);
6630         
6631         if(!cell || (!this.cellSelection && !this.rowSelection)){
6632             return;
6633         }
6634         
6635         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6636             cell = cell.findParent('td', false, true);
6637         }
6638         
6639         if(!cell || typeof(cell) == 'undefined'){
6640             return;
6641         }
6642         
6643         var row = cell.findParent('tr', false, true);
6644         
6645         if(!row || typeof(row) == 'undefined'){
6646             return;
6647         }
6648         
6649         var cellIndex = cell.dom.cellIndex;
6650         var rowIndex = this.getRowIndex(row);
6651         
6652         if(this.cellSelection){
6653             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6654         }
6655         
6656         if(this.rowSelection){
6657             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6658         }
6659     },
6660     
6661     sort : function(e,el)
6662     {
6663         var col = Roo.get(el);
6664         
6665         if(!col.hasClass('sortable')){
6666             return;
6667         }
6668         
6669         var sort = col.attr('sort');
6670         var dir = 'ASC';
6671         
6672         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6673             dir = 'DESC';
6674         }
6675         
6676         this.store.sortInfo = {field : sort, direction : dir};
6677         
6678         if (this.footer) {
6679             Roo.log("calling footer first");
6680             this.footer.onClick('first');
6681         } else {
6682         
6683             this.store.load({ params : { start : 0 } });
6684         }
6685     },
6686     
6687     renderHeader : function()
6688     {
6689         var header = {
6690             tag: 'thead',
6691             cn : []
6692         };
6693         
6694         var cm = this.cm;
6695         this.totalWidth = 0;
6696         
6697         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6698             
6699             var config = cm.config[i];
6700             
6701             var c = {
6702                 tag: 'th',
6703                 cls : 'x-hcol-' + i,
6704                 style : '',
6705                 html: cm.getColumnHeader(i)
6706             };
6707             
6708             var hh = '';
6709             
6710             if(typeof(config.sortable) != 'undefined' && config.sortable){
6711                 c.cls = 'sortable';
6712                 c.html = '<i class="glyphicon"></i>' + c.html;
6713             }
6714             
6715             if(typeof(config.lgHeader) != 'undefined'){
6716                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6717             }
6718             
6719             if(typeof(config.mdHeader) != 'undefined'){
6720                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6721             }
6722             
6723             if(typeof(config.smHeader) != 'undefined'){
6724                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6725             }
6726             
6727             if(typeof(config.xsHeader) != 'undefined'){
6728                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6729             }
6730             
6731             if(hh.length){
6732                 c.html = hh;
6733             }
6734             
6735             if(typeof(config.tooltip) != 'undefined'){
6736                 c.tooltip = config.tooltip;
6737             }
6738             
6739             if(typeof(config.colspan) != 'undefined'){
6740                 c.colspan = config.colspan;
6741             }
6742             
6743             if(typeof(config.hidden) != 'undefined' && config.hidden){
6744                 c.style += ' display:none;';
6745             }
6746             
6747             if(typeof(config.dataIndex) != 'undefined'){
6748                 c.sort = config.dataIndex;
6749             }
6750             
6751            
6752             
6753             if(typeof(config.align) != 'undefined' && config.align.length){
6754                 c.style += ' text-align:' + config.align + ';';
6755             }
6756             
6757             if(typeof(config.width) != 'undefined'){
6758                 c.style += ' width:' + config.width + 'px;';
6759                 this.totalWidth += config.width;
6760             } else {
6761                 this.totalWidth += 100; // assume minimum of 100 per column?
6762             }
6763             
6764             if(typeof(config.cls) != 'undefined'){
6765                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6766             }
6767             
6768             ['xs','sm','md','lg'].map(function(size){
6769                 
6770                 if(typeof(config[size]) == 'undefined'){
6771                     return;
6772                 }
6773                 
6774                 if (!config[size]) { // 0 = hidden
6775                     c.cls += ' hidden-' + size;
6776                     return;
6777                 }
6778                 
6779                 c.cls += ' col-' + size + '-' + config[size];
6780
6781             });
6782             
6783             header.cn.push(c)
6784         }
6785         
6786         return header;
6787     },
6788     
6789     renderBody : function()
6790     {
6791         var body = {
6792             tag: 'tbody',
6793             cn : [
6794                 {
6795                     tag: 'tr',
6796                     cn : [
6797                         {
6798                             tag : 'td',
6799                             colspan :  this.cm.getColumnCount()
6800                         }
6801                     ]
6802                 }
6803             ]
6804         };
6805         
6806         return body;
6807     },
6808     
6809     renderFooter : function()
6810     {
6811         var footer = {
6812             tag: 'tfoot',
6813             cn : [
6814                 {
6815                     tag: 'tr',
6816                     cn : [
6817                         {
6818                             tag : 'td',
6819                             colspan :  this.cm.getColumnCount()
6820                         }
6821                     ]
6822                 }
6823             ]
6824         };
6825         
6826         return footer;
6827     },
6828     
6829     
6830     
6831     onLoad : function()
6832     {
6833 //        Roo.log('ds onload');
6834         this.clear();
6835         
6836         var _this = this;
6837         var cm = this.cm;
6838         var ds = this.store;
6839         
6840         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6841             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6842             if (_this.store.sortInfo) {
6843                     
6844                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6845                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6846                 }
6847                 
6848                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6849                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6850                 }
6851             }
6852         });
6853         
6854         var tbody =  this.mainBody;
6855               
6856         if(ds.getCount() > 0){
6857             ds.data.each(function(d,rowIndex){
6858                 var row =  this.renderRow(cm, ds, rowIndex);
6859                 
6860                 tbody.createChild(row);
6861                 
6862                 var _this = this;
6863                 
6864                 if(row.cellObjects.length){
6865                     Roo.each(row.cellObjects, function(r){
6866                         _this.renderCellObject(r);
6867                     })
6868                 }
6869                 
6870             }, this);
6871         }
6872         
6873         var tfoot = this.el.select('tfoot', true).first();
6874         
6875         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6876             
6877             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6878             
6879             var total = this.ds.getTotalCount();
6880             
6881             if(this.footer.pageSize < total){
6882                 this.mainFoot.show();
6883             }
6884         }
6885         
6886         Roo.each(this.el.select('tbody td', true).elements, function(e){
6887             e.on('mouseover', _this.onMouseover, _this);
6888         });
6889         
6890         Roo.each(this.el.select('tbody td', true).elements, function(e){
6891             e.on('mouseout', _this.onMouseout, _this);
6892         });
6893         this.fireEvent('rowsrendered', this);
6894         
6895         this.autoSize();
6896     },
6897     
6898     
6899     onUpdate : function(ds,record)
6900     {
6901         this.refreshRow(record);
6902         this.autoSize();
6903     },
6904     
6905     onRemove : function(ds, record, index, isUpdate){
6906         if(isUpdate !== true){
6907             this.fireEvent("beforerowremoved", this, index, record);
6908         }
6909         var bt = this.mainBody.dom;
6910         
6911         var rows = this.el.select('tbody > tr', true).elements;
6912         
6913         if(typeof(rows[index]) != 'undefined'){
6914             bt.removeChild(rows[index].dom);
6915         }
6916         
6917 //        if(bt.rows[index]){
6918 //            bt.removeChild(bt.rows[index]);
6919 //        }
6920         
6921         if(isUpdate !== true){
6922             //this.stripeRows(index);
6923             //this.syncRowHeights(index, index);
6924             //this.layout();
6925             this.fireEvent("rowremoved", this, index, record);
6926         }
6927     },
6928     
6929     onAdd : function(ds, records, rowIndex)
6930     {
6931         //Roo.log('on Add called');
6932         // - note this does not handle multiple adding very well..
6933         var bt = this.mainBody.dom;
6934         for (var i =0 ; i < records.length;i++) {
6935             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6936             //Roo.log(records[i]);
6937             //Roo.log(this.store.getAt(rowIndex+i));
6938             this.insertRow(this.store, rowIndex + i, false);
6939             return;
6940         }
6941         
6942     },
6943     
6944     
6945     refreshRow : function(record){
6946         var ds = this.store, index;
6947         if(typeof record == 'number'){
6948             index = record;
6949             record = ds.getAt(index);
6950         }else{
6951             index = ds.indexOf(record);
6952         }
6953         this.insertRow(ds, index, true);
6954         this.autoSize();
6955         this.onRemove(ds, record, index+1, true);
6956         this.autoSize();
6957         //this.syncRowHeights(index, index);
6958         //this.layout();
6959         this.fireEvent("rowupdated", this, index, record);
6960     },
6961     
6962     insertRow : function(dm, rowIndex, isUpdate){
6963         
6964         if(!isUpdate){
6965             this.fireEvent("beforerowsinserted", this, rowIndex);
6966         }
6967             //var s = this.getScrollState();
6968         var row = this.renderRow(this.cm, this.store, rowIndex);
6969         // insert before rowIndex..
6970         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6971         
6972         var _this = this;
6973                 
6974         if(row.cellObjects.length){
6975             Roo.each(row.cellObjects, function(r){
6976                 _this.renderCellObject(r);
6977             })
6978         }
6979             
6980         if(!isUpdate){
6981             this.fireEvent("rowsinserted", this, rowIndex);
6982             //this.syncRowHeights(firstRow, lastRow);
6983             //this.stripeRows(firstRow);
6984             //this.layout();
6985         }
6986         
6987     },
6988     
6989     
6990     getRowDom : function(rowIndex)
6991     {
6992         var rows = this.el.select('tbody > tr', true).elements;
6993         
6994         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6995         
6996     },
6997     // returns the object tree for a tr..
6998   
6999     
7000     renderRow : function(cm, ds, rowIndex) 
7001     {
7002         var d = ds.getAt(rowIndex);
7003         
7004         var row = {
7005             tag : 'tr',
7006             cls : 'x-row-' + rowIndex,
7007             cn : []
7008         };
7009             
7010         var cellObjects = [];
7011         
7012         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7013             var config = cm.config[i];
7014             
7015             var renderer = cm.getRenderer(i);
7016             var value = '';
7017             var id = false;
7018             
7019             if(typeof(renderer) !== 'undefined'){
7020                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7021             }
7022             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7023             // and are rendered into the cells after the row is rendered - using the id for the element.
7024             
7025             if(typeof(value) === 'object'){
7026                 id = Roo.id();
7027                 cellObjects.push({
7028                     container : id,
7029                     cfg : value 
7030                 })
7031             }
7032             
7033             var rowcfg = {
7034                 record: d,
7035                 rowIndex : rowIndex,
7036                 colIndex : i,
7037                 rowClass : ''
7038             };
7039
7040             this.fireEvent('rowclass', this, rowcfg);
7041             
7042             var td = {
7043                 tag: 'td',
7044                 cls : rowcfg.rowClass + ' x-col-' + i,
7045                 style: '',
7046                 html: (typeof(value) === 'object') ? '' : value
7047             };
7048             
7049             if (id) {
7050                 td.id = id;
7051             }
7052             
7053             if(typeof(config.colspan) != 'undefined'){
7054                 td.colspan = config.colspan;
7055             }
7056             
7057             if(typeof(config.hidden) != 'undefined' && config.hidden){
7058                 td.style += ' display:none;';
7059             }
7060             
7061             if(typeof(config.align) != 'undefined' && config.align.length){
7062                 td.style += ' text-align:' + config.align + ';';
7063             }
7064             if(typeof(config.valign) != 'undefined' && config.valign.length){
7065                 td.style += ' vertical-align:' + config.valign + ';';
7066             }
7067             
7068             if(typeof(config.width) != 'undefined'){
7069                 td.style += ' width:' +  config.width + 'px;';
7070             }
7071             
7072             if(typeof(config.cursor) != 'undefined'){
7073                 td.style += ' cursor:' +  config.cursor + ';';
7074             }
7075             
7076             if(typeof(config.cls) != 'undefined'){
7077                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7078             }
7079             
7080             ['xs','sm','md','lg'].map(function(size){
7081                 
7082                 if(typeof(config[size]) == 'undefined'){
7083                     return;
7084                 }
7085                 
7086                 if (!config[size]) { // 0 = hidden
7087                     td.cls += ' hidden-' + size;
7088                     return;
7089                 }
7090                 
7091                 td.cls += ' col-' + size + '-' + config[size];
7092
7093             });
7094             
7095             row.cn.push(td);
7096            
7097         }
7098         
7099         row.cellObjects = cellObjects;
7100         
7101         return row;
7102           
7103     },
7104     
7105     
7106     
7107     onBeforeLoad : function()
7108     {
7109         
7110     },
7111      /**
7112      * Remove all rows
7113      */
7114     clear : function()
7115     {
7116         this.el.select('tbody', true).first().dom.innerHTML = '';
7117     },
7118     /**
7119      * Show or hide a row.
7120      * @param {Number} rowIndex to show or hide
7121      * @param {Boolean} state hide
7122      */
7123     setRowVisibility : function(rowIndex, state)
7124     {
7125         var bt = this.mainBody.dom;
7126         
7127         var rows = this.el.select('tbody > tr', true).elements;
7128         
7129         if(typeof(rows[rowIndex]) == 'undefined'){
7130             return;
7131         }
7132         rows[rowIndex].dom.style.display = state ? '' : 'none';
7133     },
7134     
7135     
7136     getSelectionModel : function(){
7137         if(!this.selModel){
7138             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7139         }
7140         return this.selModel;
7141     },
7142     /*
7143      * Render the Roo.bootstrap object from renderder
7144      */
7145     renderCellObject : function(r)
7146     {
7147         var _this = this;
7148         
7149         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7150         
7151         var t = r.cfg.render(r.container);
7152         
7153         if(r.cfg.cn){
7154             Roo.each(r.cfg.cn, function(c){
7155                 var child = {
7156                     container: t.getChildContainer(),
7157                     cfg: c
7158                 };
7159                 _this.renderCellObject(child);
7160             })
7161         }
7162     },
7163     
7164     getRowIndex : function(row)
7165     {
7166         var rowIndex = -1;
7167         
7168         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7169             if(el != row){
7170                 return;
7171             }
7172             
7173             rowIndex = index;
7174         });
7175         
7176         return rowIndex;
7177     },
7178      /**
7179      * Returns the grid's underlying element = used by panel.Grid
7180      * @return {Element} The element
7181      */
7182     getGridEl : function(){
7183         return this.el;
7184     },
7185      /**
7186      * Forces a resize - used by panel.Grid
7187      * @return {Element} The element
7188      */
7189     autoSize : function()
7190     {
7191         //var ctr = Roo.get(this.container.dom.parentElement);
7192         var ctr = Roo.get(this.el.dom);
7193         
7194         var thd = this.getGridEl().select('thead',true).first();
7195         var tbd = this.getGridEl().select('tbody', true).first();
7196         var tfd = this.getGridEl().select('tfoot', true).first();
7197         
7198         var cw = ctr.getWidth();
7199         
7200         if (tbd) {
7201             
7202             tbd.setSize(ctr.getWidth(),
7203                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7204             );
7205             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7206             cw -= barsize;
7207         }
7208         cw = Math.max(cw, this.totalWidth);
7209         this.getGridEl().select('tr',true).setWidth(cw);
7210         // resize 'expandable coloumn?
7211         
7212         return; // we doe not have a view in this design..
7213         
7214     },
7215     onBodyScroll: function()
7216     {
7217         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7218         if(this.mainHead){
7219             this.mainHead.setStyle({
7220                 'position' : 'relative',
7221                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7222             });
7223         }
7224         
7225         if(this.lazyLoad){
7226             
7227             var scrollHeight = this.mainBody.dom.scrollHeight;
7228             
7229             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7230             
7231             var height = this.mainBody.getHeight();
7232             
7233             if(scrollHeight - height == scrollTop) {
7234                 
7235                 var total = this.ds.getTotalCount();
7236                 
7237                 if(this.footer.cursor + this.footer.pageSize < total){
7238                     
7239                     this.footer.ds.load({
7240                         params : {
7241                             start : this.footer.cursor + this.footer.pageSize,
7242                             limit : this.footer.pageSize
7243                         },
7244                         add : true
7245                     });
7246                 }
7247             }
7248             
7249         }
7250     },
7251     
7252     onHeaderChange : function()
7253     {
7254         var header = this.renderHeader();
7255         var table = this.el.select('table', true).first();
7256         
7257         this.mainHead.remove();
7258         this.mainHead = table.createChild(header, this.mainBody, false);
7259     },
7260     
7261     onHiddenChange : function(colModel, colIndex, hidden)
7262     {
7263         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7264         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7265         
7266         this.CSS.updateRule(thSelector, "display", "");
7267         this.CSS.updateRule(tdSelector, "display", "");
7268         
7269         if(hidden){
7270             this.CSS.updateRule(thSelector, "display", "none");
7271             this.CSS.updateRule(tdSelector, "display", "none");
7272         }
7273         
7274         this.onHeaderChange();
7275         this.onLoad();
7276     },
7277     
7278     setColumnWidth: function(col_index, width)
7279     {
7280         // width = "md-2 xs-2..."
7281         if(!this.colModel.config[col_index]) {
7282             return;
7283         }
7284         
7285         var w = width.split(" ");
7286         
7287         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7288         
7289         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7290         
7291         
7292         for(var j = 0; j < w.length; j++) {
7293             
7294             if(!w[j]) {
7295                 continue;
7296             }
7297             
7298             var size_cls = w[j].split("-");
7299             
7300             if(!Number.isInteger(size_cls[1] * 1)) {
7301                 continue;
7302             }
7303             
7304             if(!this.colModel.config[col_index][size_cls[0]]) {
7305                 continue;
7306             }
7307             
7308             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7309                 continue;
7310             }
7311             
7312             h_row[0].classList.replace(
7313                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7314                 "col-"+size_cls[0]+"-"+size_cls[1]
7315             );
7316             
7317             for(var i = 0; i < rows.length; i++) {
7318                 
7319                 var size_cls = w[j].split("-");
7320                 
7321                 if(!Number.isInteger(size_cls[1] * 1)) {
7322                     continue;
7323                 }
7324                 
7325                 if(!this.colModel.config[col_index][size_cls[0]]) {
7326                     continue;
7327                 }
7328                 
7329                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7330                     continue;
7331                 }
7332                 
7333                 rows[i].classList.replace(
7334                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7335                     "col-"+size_cls[0]+"-"+size_cls[1]
7336                 );
7337             }
7338             
7339             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7340         }
7341     }
7342 });
7343
7344  
7345
7346  /*
7347  * - LGPL
7348  *
7349  * table cell
7350  * 
7351  */
7352
7353 /**
7354  * @class Roo.bootstrap.TableCell
7355  * @extends Roo.bootstrap.Component
7356  * Bootstrap TableCell class
7357  * @cfg {String} html cell contain text
7358  * @cfg {String} cls cell class
7359  * @cfg {String} tag cell tag (td|th) default td
7360  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7361  * @cfg {String} align Aligns the content in a cell
7362  * @cfg {String} axis Categorizes cells
7363  * @cfg {String} bgcolor Specifies the background color of a cell
7364  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7365  * @cfg {Number} colspan Specifies the number of columns a cell should span
7366  * @cfg {String} headers Specifies one or more header cells a cell is related to
7367  * @cfg {Number} height Sets the height of a cell
7368  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7369  * @cfg {Number} rowspan Sets the number of rows a cell should span
7370  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7371  * @cfg {String} valign Vertical aligns the content in a cell
7372  * @cfg {Number} width Specifies the width of a cell
7373  * 
7374  * @constructor
7375  * Create a new TableCell
7376  * @param {Object} config The config object
7377  */
7378
7379 Roo.bootstrap.TableCell = function(config){
7380     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7381 };
7382
7383 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7384     
7385     html: false,
7386     cls: false,
7387     tag: false,
7388     abbr: false,
7389     align: false,
7390     axis: false,
7391     bgcolor: false,
7392     charoff: false,
7393     colspan: false,
7394     headers: false,
7395     height: false,
7396     nowrap: false,
7397     rowspan: false,
7398     scope: false,
7399     valign: false,
7400     width: false,
7401     
7402     
7403     getAutoCreate : function(){
7404         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7405         
7406         cfg = {
7407             tag: 'td'
7408         };
7409         
7410         if(this.tag){
7411             cfg.tag = this.tag;
7412         }
7413         
7414         if (this.html) {
7415             cfg.html=this.html
7416         }
7417         if (this.cls) {
7418             cfg.cls=this.cls
7419         }
7420         if (this.abbr) {
7421             cfg.abbr=this.abbr
7422         }
7423         if (this.align) {
7424             cfg.align=this.align
7425         }
7426         if (this.axis) {
7427             cfg.axis=this.axis
7428         }
7429         if (this.bgcolor) {
7430             cfg.bgcolor=this.bgcolor
7431         }
7432         if (this.charoff) {
7433             cfg.charoff=this.charoff
7434         }
7435         if (this.colspan) {
7436             cfg.colspan=this.colspan
7437         }
7438         if (this.headers) {
7439             cfg.headers=this.headers
7440         }
7441         if (this.height) {
7442             cfg.height=this.height
7443         }
7444         if (this.nowrap) {
7445             cfg.nowrap=this.nowrap
7446         }
7447         if (this.rowspan) {
7448             cfg.rowspan=this.rowspan
7449         }
7450         if (this.scope) {
7451             cfg.scope=this.scope
7452         }
7453         if (this.valign) {
7454             cfg.valign=this.valign
7455         }
7456         if (this.width) {
7457             cfg.width=this.width
7458         }
7459         
7460         
7461         return cfg;
7462     }
7463    
7464 });
7465
7466  
7467
7468  /*
7469  * - LGPL
7470  *
7471  * table row
7472  * 
7473  */
7474
7475 /**
7476  * @class Roo.bootstrap.TableRow
7477  * @extends Roo.bootstrap.Component
7478  * Bootstrap TableRow class
7479  * @cfg {String} cls row class
7480  * @cfg {String} align Aligns the content in a table row
7481  * @cfg {String} bgcolor Specifies a background color for a table row
7482  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7483  * @cfg {String} valign Vertical aligns the content in a table row
7484  * 
7485  * @constructor
7486  * Create a new TableRow
7487  * @param {Object} config The config object
7488  */
7489
7490 Roo.bootstrap.TableRow = function(config){
7491     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7492 };
7493
7494 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7495     
7496     cls: false,
7497     align: false,
7498     bgcolor: false,
7499     charoff: false,
7500     valign: false,
7501     
7502     getAutoCreate : function(){
7503         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7504         
7505         cfg = {
7506             tag: 'tr'
7507         };
7508             
7509         if(this.cls){
7510             cfg.cls = this.cls;
7511         }
7512         if(this.align){
7513             cfg.align = this.align;
7514         }
7515         if(this.bgcolor){
7516             cfg.bgcolor = this.bgcolor;
7517         }
7518         if(this.charoff){
7519             cfg.charoff = this.charoff;
7520         }
7521         if(this.valign){
7522             cfg.valign = this.valign;
7523         }
7524         
7525         return cfg;
7526     }
7527    
7528 });
7529
7530  
7531
7532  /*
7533  * - LGPL
7534  *
7535  * table body
7536  * 
7537  */
7538
7539 /**
7540  * @class Roo.bootstrap.TableBody
7541  * @extends Roo.bootstrap.Component
7542  * Bootstrap TableBody class
7543  * @cfg {String} cls element class
7544  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7545  * @cfg {String} align Aligns the content inside the element
7546  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7547  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7548  * 
7549  * @constructor
7550  * Create a new TableBody
7551  * @param {Object} config The config object
7552  */
7553
7554 Roo.bootstrap.TableBody = function(config){
7555     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7556 };
7557
7558 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7559     
7560     cls: false,
7561     tag: false,
7562     align: false,
7563     charoff: false,
7564     valign: false,
7565     
7566     getAutoCreate : function(){
7567         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7568         
7569         cfg = {
7570             tag: 'tbody'
7571         };
7572             
7573         if (this.cls) {
7574             cfg.cls=this.cls
7575         }
7576         if(this.tag){
7577             cfg.tag = this.tag;
7578         }
7579         
7580         if(this.align){
7581             cfg.align = this.align;
7582         }
7583         if(this.charoff){
7584             cfg.charoff = this.charoff;
7585         }
7586         if(this.valign){
7587             cfg.valign = this.valign;
7588         }
7589         
7590         return cfg;
7591     }
7592     
7593     
7594 //    initEvents : function()
7595 //    {
7596 //        
7597 //        if(!this.store){
7598 //            return;
7599 //        }
7600 //        
7601 //        this.store = Roo.factory(this.store, Roo.data);
7602 //        this.store.on('load', this.onLoad, this);
7603 //        
7604 //        this.store.load();
7605 //        
7606 //    },
7607 //    
7608 //    onLoad: function () 
7609 //    {   
7610 //        this.fireEvent('load', this);
7611 //    }
7612 //    
7613 //   
7614 });
7615
7616  
7617
7618  /*
7619  * Based on:
7620  * Ext JS Library 1.1.1
7621  * Copyright(c) 2006-2007, Ext JS, LLC.
7622  *
7623  * Originally Released Under LGPL - original licence link has changed is not relivant.
7624  *
7625  * Fork - LGPL
7626  * <script type="text/javascript">
7627  */
7628
7629 // as we use this in bootstrap.
7630 Roo.namespace('Roo.form');
7631  /**
7632  * @class Roo.form.Action
7633  * Internal Class used to handle form actions
7634  * @constructor
7635  * @param {Roo.form.BasicForm} el The form element or its id
7636  * @param {Object} config Configuration options
7637  */
7638
7639  
7640  
7641 // define the action interface
7642 Roo.form.Action = function(form, options){
7643     this.form = form;
7644     this.options = options || {};
7645 };
7646 /**
7647  * Client Validation Failed
7648  * @const 
7649  */
7650 Roo.form.Action.CLIENT_INVALID = 'client';
7651 /**
7652  * Server Validation Failed
7653  * @const 
7654  */
7655 Roo.form.Action.SERVER_INVALID = 'server';
7656  /**
7657  * Connect to Server Failed
7658  * @const 
7659  */
7660 Roo.form.Action.CONNECT_FAILURE = 'connect';
7661 /**
7662  * Reading Data from Server Failed
7663  * @const 
7664  */
7665 Roo.form.Action.LOAD_FAILURE = 'load';
7666
7667 Roo.form.Action.prototype = {
7668     type : 'default',
7669     failureType : undefined,
7670     response : undefined,
7671     result : undefined,
7672
7673     // interface method
7674     run : function(options){
7675
7676     },
7677
7678     // interface method
7679     success : function(response){
7680
7681     },
7682
7683     // interface method
7684     handleResponse : function(response){
7685
7686     },
7687
7688     // default connection failure
7689     failure : function(response){
7690         
7691         this.response = response;
7692         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7693         this.form.afterAction(this, false);
7694     },
7695
7696     processResponse : function(response){
7697         this.response = response;
7698         if(!response.responseText){
7699             return true;
7700         }
7701         this.result = this.handleResponse(response);
7702         return this.result;
7703     },
7704
7705     // utility functions used internally
7706     getUrl : function(appendParams){
7707         var url = this.options.url || this.form.url || this.form.el.dom.action;
7708         if(appendParams){
7709             var p = this.getParams();
7710             if(p){
7711                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7712             }
7713         }
7714         return url;
7715     },
7716
7717     getMethod : function(){
7718         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7719     },
7720
7721     getParams : function(){
7722         var bp = this.form.baseParams;
7723         var p = this.options.params;
7724         if(p){
7725             if(typeof p == "object"){
7726                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7727             }else if(typeof p == 'string' && bp){
7728                 p += '&' + Roo.urlEncode(bp);
7729             }
7730         }else if(bp){
7731             p = Roo.urlEncode(bp);
7732         }
7733         return p;
7734     },
7735
7736     createCallback : function(){
7737         return {
7738             success: this.success,
7739             failure: this.failure,
7740             scope: this,
7741             timeout: (this.form.timeout*1000),
7742             upload: this.form.fileUpload ? this.success : undefined
7743         };
7744     }
7745 };
7746
7747 Roo.form.Action.Submit = function(form, options){
7748     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7749 };
7750
7751 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7752     type : 'submit',
7753
7754     haveProgress : false,
7755     uploadComplete : false,
7756     
7757     // uploadProgress indicator.
7758     uploadProgress : function()
7759     {
7760         if (!this.form.progressUrl) {
7761             return;
7762         }
7763         
7764         if (!this.haveProgress) {
7765             Roo.MessageBox.progress("Uploading", "Uploading");
7766         }
7767         if (this.uploadComplete) {
7768            Roo.MessageBox.hide();
7769            return;
7770         }
7771         
7772         this.haveProgress = true;
7773    
7774         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7775         
7776         var c = new Roo.data.Connection();
7777         c.request({
7778             url : this.form.progressUrl,
7779             params: {
7780                 id : uid
7781             },
7782             method: 'GET',
7783             success : function(req){
7784                //console.log(data);
7785                 var rdata = false;
7786                 var edata;
7787                 try  {
7788                    rdata = Roo.decode(req.responseText)
7789                 } catch (e) {
7790                     Roo.log("Invalid data from server..");
7791                     Roo.log(edata);
7792                     return;
7793                 }
7794                 if (!rdata || !rdata.success) {
7795                     Roo.log(rdata);
7796                     Roo.MessageBox.alert(Roo.encode(rdata));
7797                     return;
7798                 }
7799                 var data = rdata.data;
7800                 
7801                 if (this.uploadComplete) {
7802                    Roo.MessageBox.hide();
7803                    return;
7804                 }
7805                    
7806                 if (data){
7807                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7808                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7809                     );
7810                 }
7811                 this.uploadProgress.defer(2000,this);
7812             },
7813        
7814             failure: function(data) {
7815                 Roo.log('progress url failed ');
7816                 Roo.log(data);
7817             },
7818             scope : this
7819         });
7820            
7821     },
7822     
7823     
7824     run : function()
7825     {
7826         // run get Values on the form, so it syncs any secondary forms.
7827         this.form.getValues();
7828         
7829         var o = this.options;
7830         var method = this.getMethod();
7831         var isPost = method == 'POST';
7832         if(o.clientValidation === false || this.form.isValid()){
7833             
7834             if (this.form.progressUrl) {
7835                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7836                     (new Date() * 1) + '' + Math.random());
7837                     
7838             } 
7839             
7840             
7841             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7842                 form:this.form.el.dom,
7843                 url:this.getUrl(!isPost),
7844                 method: method,
7845                 params:isPost ? this.getParams() : null,
7846                 isUpload: this.form.fileUpload
7847             }));
7848             
7849             this.uploadProgress();
7850
7851         }else if (o.clientValidation !== false){ // client validation failed
7852             this.failureType = Roo.form.Action.CLIENT_INVALID;
7853             this.form.afterAction(this, false);
7854         }
7855     },
7856
7857     success : function(response)
7858     {
7859         this.uploadComplete= true;
7860         if (this.haveProgress) {
7861             Roo.MessageBox.hide();
7862         }
7863         
7864         
7865         var result = this.processResponse(response);
7866         if(result === true || result.success){
7867             this.form.afterAction(this, true);
7868             return;
7869         }
7870         if(result.errors){
7871             this.form.markInvalid(result.errors);
7872             this.failureType = Roo.form.Action.SERVER_INVALID;
7873         }
7874         this.form.afterAction(this, false);
7875     },
7876     failure : function(response)
7877     {
7878         this.uploadComplete= true;
7879         if (this.haveProgress) {
7880             Roo.MessageBox.hide();
7881         }
7882         
7883         this.response = response;
7884         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7885         this.form.afterAction(this, false);
7886     },
7887     
7888     handleResponse : function(response){
7889         if(this.form.errorReader){
7890             var rs = this.form.errorReader.read(response);
7891             var errors = [];
7892             if(rs.records){
7893                 for(var i = 0, len = rs.records.length; i < len; i++) {
7894                     var r = rs.records[i];
7895                     errors[i] = r.data;
7896                 }
7897             }
7898             if(errors.length < 1){
7899                 errors = null;
7900             }
7901             return {
7902                 success : rs.success,
7903                 errors : errors
7904             };
7905         }
7906         var ret = false;
7907         try {
7908             ret = Roo.decode(response.responseText);
7909         } catch (e) {
7910             ret = {
7911                 success: false,
7912                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7913                 errors : []
7914             };
7915         }
7916         return ret;
7917         
7918     }
7919 });
7920
7921
7922 Roo.form.Action.Load = function(form, options){
7923     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7924     this.reader = this.form.reader;
7925 };
7926
7927 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7928     type : 'load',
7929
7930     run : function(){
7931         
7932         Roo.Ajax.request(Roo.apply(
7933                 this.createCallback(), {
7934                     method:this.getMethod(),
7935                     url:this.getUrl(false),
7936                     params:this.getParams()
7937         }));
7938     },
7939
7940     success : function(response){
7941         
7942         var result = this.processResponse(response);
7943         if(result === true || !result.success || !result.data){
7944             this.failureType = Roo.form.Action.LOAD_FAILURE;
7945             this.form.afterAction(this, false);
7946             return;
7947         }
7948         this.form.clearInvalid();
7949         this.form.setValues(result.data);
7950         this.form.afterAction(this, true);
7951     },
7952
7953     handleResponse : function(response){
7954         if(this.form.reader){
7955             var rs = this.form.reader.read(response);
7956             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7957             return {
7958                 success : rs.success,
7959                 data : data
7960             };
7961         }
7962         return Roo.decode(response.responseText);
7963     }
7964 });
7965
7966 Roo.form.Action.ACTION_TYPES = {
7967     'load' : Roo.form.Action.Load,
7968     'submit' : Roo.form.Action.Submit
7969 };/*
7970  * - LGPL
7971  *
7972  * form
7973  *
7974  */
7975
7976 /**
7977  * @class Roo.bootstrap.Form
7978  * @extends Roo.bootstrap.Component
7979  * Bootstrap Form class
7980  * @cfg {String} method  GET | POST (default POST)
7981  * @cfg {String} labelAlign top | left (default top)
7982  * @cfg {String} align left  | right - for navbars
7983  * @cfg {Boolean} loadMask load mask when submit (default true)
7984
7985  *
7986  * @constructor
7987  * Create a new Form
7988  * @param {Object} config The config object
7989  */
7990
7991
7992 Roo.bootstrap.Form = function(config){
7993     
7994     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7995     
7996     Roo.bootstrap.Form.popover.apply();
7997     
7998     this.addEvents({
7999         /**
8000          * @event clientvalidation
8001          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8002          * @param {Form} this
8003          * @param {Boolean} valid true if the form has passed client-side validation
8004          */
8005         clientvalidation: true,
8006         /**
8007          * @event beforeaction
8008          * Fires before any action is performed. Return false to cancel the action.
8009          * @param {Form} this
8010          * @param {Action} action The action to be performed
8011          */
8012         beforeaction: true,
8013         /**
8014          * @event actionfailed
8015          * Fires when an action fails.
8016          * @param {Form} this
8017          * @param {Action} action The action that failed
8018          */
8019         actionfailed : true,
8020         /**
8021          * @event actioncomplete
8022          * Fires when an action is completed.
8023          * @param {Form} this
8024          * @param {Action} action The action that completed
8025          */
8026         actioncomplete : true
8027     });
8028 };
8029
8030 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8031
8032      /**
8033      * @cfg {String} method
8034      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8035      */
8036     method : 'POST',
8037     /**
8038      * @cfg {String} url
8039      * The URL to use for form actions if one isn't supplied in the action options.
8040      */
8041     /**
8042      * @cfg {Boolean} fileUpload
8043      * Set to true if this form is a file upload.
8044      */
8045
8046     /**
8047      * @cfg {Object} baseParams
8048      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8049      */
8050
8051     /**
8052      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8053      */
8054     timeout: 30,
8055     /**
8056      * @cfg {Sting} align (left|right) for navbar forms
8057      */
8058     align : 'left',
8059
8060     // private
8061     activeAction : null,
8062
8063     /**
8064      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8065      * element by passing it or its id or mask the form itself by passing in true.
8066      * @type Mixed
8067      */
8068     waitMsgTarget : false,
8069
8070     loadMask : true,
8071     
8072     /**
8073      * @cfg {Boolean} errorMask (true|false) default false
8074      */
8075     errorMask : false,
8076     
8077     /**
8078      * @cfg {Number} maskOffset Default 100
8079      */
8080     maskOffset : 100,
8081     
8082     /**
8083      * @cfg {Boolean} maskBody
8084      */
8085     maskBody : false,
8086
8087     getAutoCreate : function(){
8088
8089         var cfg = {
8090             tag: 'form',
8091             method : this.method || 'POST',
8092             id : this.id || Roo.id(),
8093             cls : ''
8094         };
8095         if (this.parent().xtype.match(/^Nav/)) {
8096             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8097
8098         }
8099
8100         if (this.labelAlign == 'left' ) {
8101             cfg.cls += ' form-horizontal';
8102         }
8103
8104
8105         return cfg;
8106     },
8107     initEvents : function()
8108     {
8109         this.el.on('submit', this.onSubmit, this);
8110         // this was added as random key presses on the form where triggering form submit.
8111         this.el.on('keypress', function(e) {
8112             if (e.getCharCode() != 13) {
8113                 return true;
8114             }
8115             // we might need to allow it for textareas.. and some other items.
8116             // check e.getTarget().
8117
8118             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8119                 return true;
8120             }
8121
8122             Roo.log("keypress blocked");
8123
8124             e.preventDefault();
8125             return false;
8126         });
8127         
8128     },
8129     // private
8130     onSubmit : function(e){
8131         e.stopEvent();
8132     },
8133
8134      /**
8135      * Returns true if client-side validation on the form is successful.
8136      * @return Boolean
8137      */
8138     isValid : function(){
8139         var items = this.getItems();
8140         var valid = true;
8141         var target = false;
8142         
8143         items.each(function(f){
8144             
8145             if(f.validate()){
8146                 return;
8147             }
8148             
8149             Roo.log('invalid field: ' + f.name);
8150             
8151             valid = false;
8152
8153             if(!target && f.el.isVisible(true)){
8154                 target = f;
8155             }
8156            
8157         });
8158         
8159         if(this.errorMask && !valid){
8160             Roo.bootstrap.Form.popover.mask(this, target);
8161         }
8162         
8163         return valid;
8164     },
8165     
8166     /**
8167      * Returns true if any fields in this form have changed since their original load.
8168      * @return Boolean
8169      */
8170     isDirty : function(){
8171         var dirty = false;
8172         var items = this.getItems();
8173         items.each(function(f){
8174            if(f.isDirty()){
8175                dirty = true;
8176                return false;
8177            }
8178            return true;
8179         });
8180         return dirty;
8181     },
8182      /**
8183      * Performs a predefined action (submit or load) or custom actions you define on this form.
8184      * @param {String} actionName The name of the action type
8185      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8186      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8187      * accept other config options):
8188      * <pre>
8189 Property          Type             Description
8190 ----------------  ---------------  ----------------------------------------------------------------------------------
8191 url               String           The url for the action (defaults to the form's url)
8192 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8193 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8194 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8195                                    validate the form on the client (defaults to false)
8196      * </pre>
8197      * @return {BasicForm} this
8198      */
8199     doAction : function(action, options){
8200         if(typeof action == 'string'){
8201             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8202         }
8203         if(this.fireEvent('beforeaction', this, action) !== false){
8204             this.beforeAction(action);
8205             action.run.defer(100, action);
8206         }
8207         return this;
8208     },
8209
8210     // private
8211     beforeAction : function(action){
8212         var o = action.options;
8213         
8214         if(this.loadMask){
8215             
8216             if(this.maskBody){
8217                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8218             } else {
8219                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8220             }
8221         }
8222         // not really supported yet.. ??
8223
8224         //if(this.waitMsgTarget === true){
8225         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8226         //}else if(this.waitMsgTarget){
8227         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8228         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8229         //}else {
8230         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8231        // }
8232
8233     },
8234
8235     // private
8236     afterAction : function(action, success){
8237         this.activeAction = null;
8238         var o = action.options;
8239
8240         if(this.loadMask){
8241             
8242             if(this.maskBody){
8243                 Roo.get(document.body).unmask();
8244             } else {
8245                 this.el.unmask();
8246             }
8247         }
8248         
8249         //if(this.waitMsgTarget === true){
8250 //            this.el.unmask();
8251         //}else if(this.waitMsgTarget){
8252         //    this.waitMsgTarget.unmask();
8253         //}else{
8254         //    Roo.MessageBox.updateProgress(1);
8255         //    Roo.MessageBox.hide();
8256        // }
8257         //
8258         if(success){
8259             if(o.reset){
8260                 this.reset();
8261             }
8262             Roo.callback(o.success, o.scope, [this, action]);
8263             this.fireEvent('actioncomplete', this, action);
8264
8265         }else{
8266
8267             // failure condition..
8268             // we have a scenario where updates need confirming.
8269             // eg. if a locking scenario exists..
8270             // we look for { errors : { needs_confirm : true }} in the response.
8271             if (
8272                 (typeof(action.result) != 'undefined')  &&
8273                 (typeof(action.result.errors) != 'undefined')  &&
8274                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8275            ){
8276                 var _t = this;
8277                 Roo.log("not supported yet");
8278                  /*
8279
8280                 Roo.MessageBox.confirm(
8281                     "Change requires confirmation",
8282                     action.result.errorMsg,
8283                     function(r) {
8284                         if (r != 'yes') {
8285                             return;
8286                         }
8287                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8288                     }
8289
8290                 );
8291                 */
8292
8293
8294                 return;
8295             }
8296
8297             Roo.callback(o.failure, o.scope, [this, action]);
8298             // show an error message if no failed handler is set..
8299             if (!this.hasListener('actionfailed')) {
8300                 Roo.log("need to add dialog support");
8301                 /*
8302                 Roo.MessageBox.alert("Error",
8303                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8304                         action.result.errorMsg :
8305                         "Saving Failed, please check your entries or try again"
8306                 );
8307                 */
8308             }
8309
8310             this.fireEvent('actionfailed', this, action);
8311         }
8312
8313     },
8314     /**
8315      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8316      * @param {String} id The value to search for
8317      * @return Field
8318      */
8319     findField : function(id){
8320         var items = this.getItems();
8321         var field = items.get(id);
8322         if(!field){
8323              items.each(function(f){
8324                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8325                     field = f;
8326                     return false;
8327                 }
8328                 return true;
8329             });
8330         }
8331         return field || null;
8332     },
8333      /**
8334      * Mark fields in this form invalid in bulk.
8335      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8336      * @return {BasicForm} this
8337      */
8338     markInvalid : function(errors){
8339         if(errors instanceof Array){
8340             for(var i = 0, len = errors.length; i < len; i++){
8341                 var fieldError = errors[i];
8342                 var f = this.findField(fieldError.id);
8343                 if(f){
8344                     f.markInvalid(fieldError.msg);
8345                 }
8346             }
8347         }else{
8348             var field, id;
8349             for(id in errors){
8350                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8351                     field.markInvalid(errors[id]);
8352                 }
8353             }
8354         }
8355         //Roo.each(this.childForms || [], function (f) {
8356         //    f.markInvalid(errors);
8357         //});
8358
8359         return this;
8360     },
8361
8362     /**
8363      * Set values for fields in this form in bulk.
8364      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8365      * @return {BasicForm} this
8366      */
8367     setValues : function(values){
8368         if(values instanceof Array){ // array of objects
8369             for(var i = 0, len = values.length; i < len; i++){
8370                 var v = values[i];
8371                 var f = this.findField(v.id);
8372                 if(f){
8373                     f.setValue(v.value);
8374                     if(this.trackResetOnLoad){
8375                         f.originalValue = f.getValue();
8376                     }
8377                 }
8378             }
8379         }else{ // object hash
8380             var field, id;
8381             for(id in values){
8382                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8383
8384                     if (field.setFromData &&
8385                         field.valueField &&
8386                         field.displayField &&
8387                         // combos' with local stores can
8388                         // be queried via setValue()
8389                         // to set their value..
8390                         (field.store && !field.store.isLocal)
8391                         ) {
8392                         // it's a combo
8393                         var sd = { };
8394                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8395                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8396                         field.setFromData(sd);
8397
8398                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8399                         
8400                         field.setFromData(values);
8401                         
8402                     } else {
8403                         field.setValue(values[id]);
8404                     }
8405
8406
8407                     if(this.trackResetOnLoad){
8408                         field.originalValue = field.getValue();
8409                     }
8410                 }
8411             }
8412         }
8413
8414         //Roo.each(this.childForms || [], function (f) {
8415         //    f.setValues(values);
8416         //});
8417
8418         return this;
8419     },
8420
8421     /**
8422      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8423      * they are returned as an array.
8424      * @param {Boolean} asString
8425      * @return {Object}
8426      */
8427     getValues : function(asString){
8428         //if (this.childForms) {
8429             // copy values from the child forms
8430         //    Roo.each(this.childForms, function (f) {
8431         //        this.setValues(f.getValues());
8432         //    }, this);
8433         //}
8434
8435
8436
8437         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8438         if(asString === true){
8439             return fs;
8440         }
8441         return Roo.urlDecode(fs);
8442     },
8443
8444     /**
8445      * Returns the fields in this form as an object with key/value pairs.
8446      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8447      * @return {Object}
8448      */
8449     getFieldValues : function(with_hidden)
8450     {
8451         var items = this.getItems();
8452         var ret = {};
8453         items.each(function(f){
8454             
8455             if (!f.getName()) {
8456                 return;
8457             }
8458             
8459             var v = f.getValue();
8460             
8461             if (f.inputType =='radio') {
8462                 if (typeof(ret[f.getName()]) == 'undefined') {
8463                     ret[f.getName()] = ''; // empty..
8464                 }
8465
8466                 if (!f.el.dom.checked) {
8467                     return;
8468
8469                 }
8470                 v = f.el.dom.value;
8471
8472             }
8473             
8474             if(f.xtype == 'MoneyField'){
8475                 ret[f.currencyName] = f.getCurrency();
8476             }
8477
8478             // not sure if this supported any more..
8479             if ((typeof(v) == 'object') && f.getRawValue) {
8480                 v = f.getRawValue() ; // dates..
8481             }
8482             // combo boxes where name != hiddenName...
8483             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8484                 ret[f.name] = f.getRawValue();
8485             }
8486             ret[f.getName()] = v;
8487         });
8488
8489         return ret;
8490     },
8491
8492     /**
8493      * Clears all invalid messages in this form.
8494      * @return {BasicForm} this
8495      */
8496     clearInvalid : function(){
8497         var items = this.getItems();
8498
8499         items.each(function(f){
8500            f.clearInvalid();
8501         });
8502
8503         return this;
8504     },
8505
8506     /**
8507      * Resets this form.
8508      * @return {BasicForm} this
8509      */
8510     reset : function(){
8511         var items = this.getItems();
8512         items.each(function(f){
8513             f.reset();
8514         });
8515
8516         Roo.each(this.childForms || [], function (f) {
8517             f.reset();
8518         });
8519
8520
8521         return this;
8522     },
8523     
8524     getItems : function()
8525     {
8526         var r=new Roo.util.MixedCollection(false, function(o){
8527             return o.id || (o.id = Roo.id());
8528         });
8529         var iter = function(el) {
8530             if (el.inputEl) {
8531                 r.add(el);
8532             }
8533             if (!el.items) {
8534                 return;
8535             }
8536             Roo.each(el.items,function(e) {
8537                 iter(e);
8538             });
8539         };
8540
8541         iter(this);
8542         return r;
8543     },
8544     
8545     hideFields : function(items)
8546     {
8547         Roo.each(items, function(i){
8548             
8549             var f = this.findField(i);
8550             
8551             if(!f){
8552                 return;
8553             }
8554             
8555             f.hide();
8556             
8557         }, this);
8558     },
8559     
8560     showFields : function(items)
8561     {
8562         Roo.each(items, function(i){
8563             
8564             var f = this.findField(i);
8565             
8566             if(!f){
8567                 return;
8568             }
8569             
8570             f.show();
8571             
8572         }, this);
8573     }
8574
8575 });
8576
8577 Roo.apply(Roo.bootstrap.Form, {
8578     
8579     popover : {
8580         
8581         padding : 5,
8582         
8583         isApplied : false,
8584         
8585         isMasked : false,
8586         
8587         form : false,
8588         
8589         target : false,
8590         
8591         toolTip : false,
8592         
8593         intervalID : false,
8594         
8595         maskEl : false,
8596         
8597         apply : function()
8598         {
8599             if(this.isApplied){
8600                 return;
8601             }
8602             
8603             this.maskEl = {
8604                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8605                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8606                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8607                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8608             };
8609             
8610             this.maskEl.top.enableDisplayMode("block");
8611             this.maskEl.left.enableDisplayMode("block");
8612             this.maskEl.bottom.enableDisplayMode("block");
8613             this.maskEl.right.enableDisplayMode("block");
8614             
8615             this.toolTip = new Roo.bootstrap.Tooltip({
8616                 cls : 'roo-form-error-popover',
8617                 alignment : {
8618                     'left' : ['r-l', [-2,0], 'right'],
8619                     'right' : ['l-r', [2,0], 'left'],
8620                     'bottom' : ['tl-bl', [0,2], 'top'],
8621                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8622                 }
8623             });
8624             
8625             this.toolTip.render(Roo.get(document.body));
8626
8627             this.toolTip.el.enableDisplayMode("block");
8628             
8629             Roo.get(document.body).on('click', function(){
8630                 this.unmask();
8631             }, this);
8632             
8633             Roo.get(document.body).on('touchstart', function(){
8634                 this.unmask();
8635             }, this);
8636             
8637             this.isApplied = true
8638         },
8639         
8640         mask : function(form, target)
8641         {
8642             this.form = form;
8643             
8644             this.target = target;
8645             
8646             if(!this.form.errorMask || !target.el){
8647                 return;
8648             }
8649             
8650             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8651             
8652             Roo.log(scrollable);
8653             
8654             var ot = this.target.el.calcOffsetsTo(scrollable);
8655             
8656             var scrollTo = ot[1] - this.form.maskOffset;
8657             
8658             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8659             
8660             scrollable.scrollTo('top', scrollTo);
8661             
8662             var box = this.target.el.getBox();
8663             Roo.log(box);
8664             var zIndex = Roo.bootstrap.Modal.zIndex++;
8665
8666             
8667             this.maskEl.top.setStyle('position', 'absolute');
8668             this.maskEl.top.setStyle('z-index', zIndex);
8669             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8670             this.maskEl.top.setLeft(0);
8671             this.maskEl.top.setTop(0);
8672             this.maskEl.top.show();
8673             
8674             this.maskEl.left.setStyle('position', 'absolute');
8675             this.maskEl.left.setStyle('z-index', zIndex);
8676             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8677             this.maskEl.left.setLeft(0);
8678             this.maskEl.left.setTop(box.y - this.padding);
8679             this.maskEl.left.show();
8680
8681             this.maskEl.bottom.setStyle('position', 'absolute');
8682             this.maskEl.bottom.setStyle('z-index', zIndex);
8683             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8684             this.maskEl.bottom.setLeft(0);
8685             this.maskEl.bottom.setTop(box.bottom + this.padding);
8686             this.maskEl.bottom.show();
8687
8688             this.maskEl.right.setStyle('position', 'absolute');
8689             this.maskEl.right.setStyle('z-index', zIndex);
8690             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8691             this.maskEl.right.setLeft(box.right + this.padding);
8692             this.maskEl.right.setTop(box.y - this.padding);
8693             this.maskEl.right.show();
8694
8695             this.toolTip.bindEl = this.target.el;
8696
8697             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8698
8699             var tip = this.target.blankText;
8700
8701             if(this.target.getValue() !== '' ) {
8702                 
8703                 if (this.target.invalidText.length) {
8704                     tip = this.target.invalidText;
8705                 } else if (this.target.regexText.length){
8706                     tip = this.target.regexText;
8707                 }
8708             }
8709
8710             this.toolTip.show(tip);
8711
8712             this.intervalID = window.setInterval(function() {
8713                 Roo.bootstrap.Form.popover.unmask();
8714             }, 10000);
8715
8716             window.onwheel = function(){ return false;};
8717             
8718             (function(){ this.isMasked = true; }).defer(500, this);
8719             
8720         },
8721         
8722         unmask : function()
8723         {
8724             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8725                 return;
8726             }
8727             
8728             this.maskEl.top.setStyle('position', 'absolute');
8729             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8730             this.maskEl.top.hide();
8731
8732             this.maskEl.left.setStyle('position', 'absolute');
8733             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8734             this.maskEl.left.hide();
8735
8736             this.maskEl.bottom.setStyle('position', 'absolute');
8737             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8738             this.maskEl.bottom.hide();
8739
8740             this.maskEl.right.setStyle('position', 'absolute');
8741             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8742             this.maskEl.right.hide();
8743             
8744             this.toolTip.hide();
8745             
8746             this.toolTip.el.hide();
8747             
8748             window.onwheel = function(){ return true;};
8749             
8750             if(this.intervalID){
8751                 window.clearInterval(this.intervalID);
8752                 this.intervalID = false;
8753             }
8754             
8755             this.isMasked = false;
8756             
8757         }
8758         
8759     }
8760     
8761 });
8762
8763 /*
8764  * Based on:
8765  * Ext JS Library 1.1.1
8766  * Copyright(c) 2006-2007, Ext JS, LLC.
8767  *
8768  * Originally Released Under LGPL - original licence link has changed is not relivant.
8769  *
8770  * Fork - LGPL
8771  * <script type="text/javascript">
8772  */
8773 /**
8774  * @class Roo.form.VTypes
8775  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8776  * @singleton
8777  */
8778 Roo.form.VTypes = function(){
8779     // closure these in so they are only created once.
8780     var alpha = /^[a-zA-Z_]+$/;
8781     var alphanum = /^[a-zA-Z0-9_]+$/;
8782     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8783     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8784
8785     // All these messages and functions are configurable
8786     return {
8787         /**
8788          * The function used to validate email addresses
8789          * @param {String} value The email address
8790          */
8791         'email' : function(v){
8792             return email.test(v);
8793         },
8794         /**
8795          * The error text to display when the email validation function returns false
8796          * @type String
8797          */
8798         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8799         /**
8800          * The keystroke filter mask to be applied on email input
8801          * @type RegExp
8802          */
8803         'emailMask' : /[a-z0-9_\.\-@]/i,
8804
8805         /**
8806          * The function used to validate URLs
8807          * @param {String} value The URL
8808          */
8809         'url' : function(v){
8810             return url.test(v);
8811         },
8812         /**
8813          * The error text to display when the url validation function returns false
8814          * @type String
8815          */
8816         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8817         
8818         /**
8819          * The function used to validate alpha values
8820          * @param {String} value The value
8821          */
8822         'alpha' : function(v){
8823             return alpha.test(v);
8824         },
8825         /**
8826          * The error text to display when the alpha validation function returns false
8827          * @type String
8828          */
8829         'alphaText' : 'This field should only contain letters and _',
8830         /**
8831          * The keystroke filter mask to be applied on alpha input
8832          * @type RegExp
8833          */
8834         'alphaMask' : /[a-z_]/i,
8835
8836         /**
8837          * The function used to validate alphanumeric values
8838          * @param {String} value The value
8839          */
8840         'alphanum' : function(v){
8841             return alphanum.test(v);
8842         },
8843         /**
8844          * The error text to display when the alphanumeric validation function returns false
8845          * @type String
8846          */
8847         'alphanumText' : 'This field should only contain letters, numbers and _',
8848         /**
8849          * The keystroke filter mask to be applied on alphanumeric input
8850          * @type RegExp
8851          */
8852         'alphanumMask' : /[a-z0-9_]/i
8853     };
8854 }();/*
8855  * - LGPL
8856  *
8857  * Input
8858  * 
8859  */
8860
8861 /**
8862  * @class Roo.bootstrap.Input
8863  * @extends Roo.bootstrap.Component
8864  * Bootstrap Input class
8865  * @cfg {Boolean} disabled is it disabled
8866  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8867  * @cfg {String} name name of the input
8868  * @cfg {string} fieldLabel - the label associated
8869  * @cfg {string} placeholder - placeholder to put in text.
8870  * @cfg {string}  before - input group add on before
8871  * @cfg {string} after - input group add on after
8872  * @cfg {string} size - (lg|sm) or leave empty..
8873  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8874  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8875  * @cfg {Number} md colspan out of 12 for computer-sized screens
8876  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8877  * @cfg {string} value default value of the input
8878  * @cfg {Number} labelWidth set the width of label 
8879  * @cfg {Number} labellg set the width of label (1-12)
8880  * @cfg {Number} labelmd set the width of label (1-12)
8881  * @cfg {Number} labelsm set the width of label (1-12)
8882  * @cfg {Number} labelxs set the width of label (1-12)
8883  * @cfg {String} labelAlign (top|left)
8884  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8885  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8886  * @cfg {String} indicatorpos (left|right) default left
8887  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8888  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8889
8890  * @cfg {String} align (left|center|right) Default left
8891  * @cfg {Boolean} forceFeedback (true|false) Default false
8892  * 
8893  * @constructor
8894  * Create a new Input
8895  * @param {Object} config The config object
8896  */
8897
8898 Roo.bootstrap.Input = function(config){
8899     
8900     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8901     
8902     this.addEvents({
8903         /**
8904          * @event focus
8905          * Fires when this field receives input focus.
8906          * @param {Roo.form.Field} this
8907          */
8908         focus : true,
8909         /**
8910          * @event blur
8911          * Fires when this field loses input focus.
8912          * @param {Roo.form.Field} this
8913          */
8914         blur : true,
8915         /**
8916          * @event specialkey
8917          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8918          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8919          * @param {Roo.form.Field} this
8920          * @param {Roo.EventObject} e The event object
8921          */
8922         specialkey : true,
8923         /**
8924          * @event change
8925          * Fires just before the field blurs if the field value has changed.
8926          * @param {Roo.form.Field} this
8927          * @param {Mixed} newValue The new value
8928          * @param {Mixed} oldValue The original value
8929          */
8930         change : true,
8931         /**
8932          * @event invalid
8933          * Fires after the field has been marked as invalid.
8934          * @param {Roo.form.Field} this
8935          * @param {String} msg The validation message
8936          */
8937         invalid : true,
8938         /**
8939          * @event valid
8940          * Fires after the field has been validated with no errors.
8941          * @param {Roo.form.Field} this
8942          */
8943         valid : true,
8944          /**
8945          * @event keyup
8946          * Fires after the key up
8947          * @param {Roo.form.Field} this
8948          * @param {Roo.EventObject}  e The event Object
8949          */
8950         keyup : true
8951     });
8952 };
8953
8954 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8955      /**
8956      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8957       automatic validation (defaults to "keyup").
8958      */
8959     validationEvent : "keyup",
8960      /**
8961      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8962      */
8963     validateOnBlur : true,
8964     /**
8965      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8966      */
8967     validationDelay : 250,
8968      /**
8969      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8970      */
8971     focusClass : "x-form-focus",  // not needed???
8972     
8973        
8974     /**
8975      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8976      */
8977     invalidClass : "has-warning",
8978     
8979     /**
8980      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8981      */
8982     validClass : "has-success",
8983     
8984     /**
8985      * @cfg {Boolean} hasFeedback (true|false) default true
8986      */
8987     hasFeedback : true,
8988     
8989     /**
8990      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8991      */
8992     invalidFeedbackClass : "glyphicon-warning-sign",
8993     
8994     /**
8995      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8996      */
8997     validFeedbackClass : "glyphicon-ok",
8998     
8999     /**
9000      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9001      */
9002     selectOnFocus : false,
9003     
9004      /**
9005      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9006      */
9007     maskRe : null,
9008        /**
9009      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9010      */
9011     vtype : null,
9012     
9013       /**
9014      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9015      */
9016     disableKeyFilter : false,
9017     
9018        /**
9019      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9020      */
9021     disabled : false,
9022      /**
9023      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9024      */
9025     allowBlank : true,
9026     /**
9027      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9028      */
9029     blankText : "Please complete this mandatory field",
9030     
9031      /**
9032      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9033      */
9034     minLength : 0,
9035     /**
9036      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9037      */
9038     maxLength : Number.MAX_VALUE,
9039     /**
9040      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9041      */
9042     minLengthText : "The minimum length for this field is {0}",
9043     /**
9044      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9045      */
9046     maxLengthText : "The maximum length for this field is {0}",
9047   
9048     
9049     /**
9050      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9051      * If available, this function will be called only after the basic validators all return true, and will be passed the
9052      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9053      */
9054     validator : null,
9055     /**
9056      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9057      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9058      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9059      */
9060     regex : null,
9061     /**
9062      * @cfg {String} regexText -- Depricated - use Invalid Text
9063      */
9064     regexText : "",
9065     
9066     /**
9067      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9068      */
9069     invalidText : "",
9070     
9071     
9072     
9073     autocomplete: false,
9074     
9075     
9076     fieldLabel : '',
9077     inputType : 'text',
9078     
9079     name : false,
9080     placeholder: false,
9081     before : false,
9082     after : false,
9083     size : false,
9084     hasFocus : false,
9085     preventMark: false,
9086     isFormField : true,
9087     value : '',
9088     labelWidth : 2,
9089     labelAlign : false,
9090     readOnly : false,
9091     align : false,
9092     formatedValue : false,
9093     forceFeedback : false,
9094     
9095     indicatorpos : 'left',
9096     
9097     labellg : 0,
9098     labelmd : 0,
9099     labelsm : 0,
9100     labelxs : 0,
9101     
9102     capture : '',
9103     accept : '',
9104     
9105     parentLabelAlign : function()
9106     {
9107         var parent = this;
9108         while (parent.parent()) {
9109             parent = parent.parent();
9110             if (typeof(parent.labelAlign) !='undefined') {
9111                 return parent.labelAlign;
9112             }
9113         }
9114         return 'left';
9115         
9116     },
9117     
9118     getAutoCreate : function()
9119     {
9120         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9121         
9122         var id = Roo.id();
9123         
9124         var cfg = {};
9125         
9126         if(this.inputType != 'hidden'){
9127             cfg.cls = 'form-group' //input-group
9128         }
9129         
9130         var input =  {
9131             tag: 'input',
9132             id : id,
9133             type : this.inputType,
9134             value : this.value,
9135             cls : 'form-control',
9136             placeholder : this.placeholder || '',
9137             autocomplete : this.autocomplete || 'new-password'
9138         };
9139         
9140         if(this.capture.length){
9141             input.capture = this.capture;
9142         }
9143         
9144         if(this.accept.length){
9145             input.accept = this.accept + "/*";
9146         }
9147         
9148         if(this.align){
9149             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9150         }
9151         
9152         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9153             input.maxLength = this.maxLength;
9154         }
9155         
9156         if (this.disabled) {
9157             input.disabled=true;
9158         }
9159         
9160         if (this.readOnly) {
9161             input.readonly=true;
9162         }
9163         
9164         if (this.name) {
9165             input.name = this.name;
9166         }
9167         
9168         if (this.size) {
9169             input.cls += ' input-' + this.size;
9170         }
9171         
9172         var settings=this;
9173         ['xs','sm','md','lg'].map(function(size){
9174             if (settings[size]) {
9175                 cfg.cls += ' col-' + size + '-' + settings[size];
9176             }
9177         });
9178         
9179         var inputblock = input;
9180         
9181         var feedback = {
9182             tag: 'span',
9183             cls: 'glyphicon form-control-feedback'
9184         };
9185             
9186         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9187             
9188             inputblock = {
9189                 cls : 'has-feedback',
9190                 cn :  [
9191                     input,
9192                     feedback
9193                 ] 
9194             };  
9195         }
9196         
9197         if (this.before || this.after) {
9198             
9199             inputblock = {
9200                 cls : 'input-group',
9201                 cn :  [] 
9202             };
9203             
9204             if (this.before && typeof(this.before) == 'string') {
9205                 
9206                 inputblock.cn.push({
9207                     tag :'span',
9208                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9209                     html : this.before
9210                 });
9211             }
9212             if (this.before && typeof(this.before) == 'object') {
9213                 this.before = Roo.factory(this.before);
9214                 
9215                 inputblock.cn.push({
9216                     tag :'span',
9217                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9218                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9219                 });
9220             }
9221             
9222             inputblock.cn.push(input);
9223             
9224             if (this.after && typeof(this.after) == 'string') {
9225                 inputblock.cn.push({
9226                     tag :'span',
9227                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9228                     html : this.after
9229                 });
9230             }
9231             if (this.after && typeof(this.after) == 'object') {
9232                 this.after = Roo.factory(this.after);
9233                 
9234                 inputblock.cn.push({
9235                     tag :'span',
9236                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9237                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9238                 });
9239             }
9240             
9241             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9242                 inputblock.cls += ' has-feedback';
9243                 inputblock.cn.push(feedback);
9244             }
9245         };
9246         var indicator = {
9247             tag : 'i',
9248             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9249             tooltip : 'This field is required'
9250         };
9251         if (Roo.bootstrap.version == 4) {
9252             indicator = {
9253                 tag : 'i',
9254                 style : 'display-none'
9255             };
9256         }
9257         if (align ==='left' && this.fieldLabel.length) {
9258             
9259             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9260             
9261             cfg.cn = [
9262                 indicator,
9263                 {
9264                     tag: 'label',
9265                     'for' :  id,
9266                     cls : 'control-label col-form-label',
9267                     html : this.fieldLabel
9268
9269                 },
9270                 {
9271                     cls : "", 
9272                     cn: [
9273                         inputblock
9274                     ]
9275                 }
9276             ];
9277             
9278             var labelCfg = cfg.cn[1];
9279             var contentCfg = cfg.cn[2];
9280             
9281             if(this.indicatorpos == 'right'){
9282                 cfg.cn = [
9283                     {
9284                         tag: 'label',
9285                         'for' :  id,
9286                         cls : 'control-label col-form-label',
9287                         cn : [
9288                             {
9289                                 tag : 'span',
9290                                 html : this.fieldLabel
9291                             },
9292                             indicator
9293                         ]
9294                     },
9295                     {
9296                         cls : "",
9297                         cn: [
9298                             inputblock
9299                         ]
9300                     }
9301
9302                 ];
9303                 
9304                 labelCfg = cfg.cn[0];
9305                 contentCfg = cfg.cn[1];
9306             
9307             }
9308             
9309             if(this.labelWidth > 12){
9310                 labelCfg.style = "width: " + this.labelWidth + 'px';
9311             }
9312             
9313             if(this.labelWidth < 13 && this.labelmd == 0){
9314                 this.labelmd = this.labelWidth;
9315             }
9316             
9317             if(this.labellg > 0){
9318                 labelCfg.cls += ' col-lg-' + this.labellg;
9319                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9320             }
9321             
9322             if(this.labelmd > 0){
9323                 labelCfg.cls += ' col-md-' + this.labelmd;
9324                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9325             }
9326             
9327             if(this.labelsm > 0){
9328                 labelCfg.cls += ' col-sm-' + this.labelsm;
9329                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9330             }
9331             
9332             if(this.labelxs > 0){
9333                 labelCfg.cls += ' col-xs-' + this.labelxs;
9334                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9335             }
9336             
9337             
9338         } else if ( this.fieldLabel.length) {
9339                 
9340             cfg.cn = [
9341                 {
9342                     tag : 'i',
9343                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9344                     tooltip : 'This field is required'
9345                 },
9346                 {
9347                     tag: 'label',
9348                    //cls : 'input-group-addon',
9349                     html : this.fieldLabel
9350
9351                 },
9352
9353                inputblock
9354
9355            ];
9356            
9357            if(this.indicatorpos == 'right'){
9358                 
9359                 cfg.cn = [
9360                     {
9361                         tag: 'label',
9362                        //cls : 'input-group-addon',
9363                         html : this.fieldLabel
9364
9365                     },
9366                     {
9367                         tag : 'i',
9368                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9369                         tooltip : 'This field is required'
9370                     },
9371
9372                    inputblock
9373
9374                ];
9375
9376             }
9377
9378         } else {
9379             
9380             cfg.cn = [
9381
9382                     inputblock
9383
9384             ];
9385                 
9386                 
9387         };
9388         
9389         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9390            cfg.cls += ' navbar-form';
9391         }
9392         
9393         if (this.parentType === 'NavGroup') {
9394            cfg.cls += ' navbar-form';
9395            cfg.tag = 'li';
9396         }
9397         
9398         return cfg;
9399         
9400     },
9401     /**
9402      * return the real input element.
9403      */
9404     inputEl: function ()
9405     {
9406         return this.el.select('input.form-control',true).first();
9407     },
9408     
9409     tooltipEl : function()
9410     {
9411         return this.inputEl();
9412     },
9413     
9414     indicatorEl : function()
9415     {
9416         if (Roo.bootstrap.version == 4) {
9417             return false; // not enabled in v4 yet.
9418         }
9419         
9420         var indicator = this.el.select('i.roo-required-indicator',true).first();
9421         
9422         if(!indicator){
9423             return false;
9424         }
9425         
9426         return indicator;
9427         
9428     },
9429     
9430     setDisabled : function(v)
9431     {
9432         var i  = this.inputEl().dom;
9433         if (!v) {
9434             i.removeAttribute('disabled');
9435             return;
9436             
9437         }
9438         i.setAttribute('disabled','true');
9439     },
9440     initEvents : function()
9441     {
9442           
9443         this.inputEl().on("keydown" , this.fireKey,  this);
9444         this.inputEl().on("focus", this.onFocus,  this);
9445         this.inputEl().on("blur", this.onBlur,  this);
9446         
9447         this.inputEl().relayEvent('keyup', this);
9448         
9449         this.indicator = this.indicatorEl();
9450         
9451         if(this.indicator){
9452             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9453         }
9454  
9455         // reference to original value for reset
9456         this.originalValue = this.getValue();
9457         //Roo.form.TextField.superclass.initEvents.call(this);
9458         if(this.validationEvent == 'keyup'){
9459             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9460             this.inputEl().on('keyup', this.filterValidation, this);
9461         }
9462         else if(this.validationEvent !== false){
9463             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9464         }
9465         
9466         if(this.selectOnFocus){
9467             this.on("focus", this.preFocus, this);
9468             
9469         }
9470         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9471             this.inputEl().on("keypress", this.filterKeys, this);
9472         } else {
9473             this.inputEl().relayEvent('keypress', this);
9474         }
9475        /* if(this.grow){
9476             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9477             this.el.on("click", this.autoSize,  this);
9478         }
9479         */
9480         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9481             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9482         }
9483         
9484         if (typeof(this.before) == 'object') {
9485             this.before.render(this.el.select('.roo-input-before',true).first());
9486         }
9487         if (typeof(this.after) == 'object') {
9488             this.after.render(this.el.select('.roo-input-after',true).first());
9489         }
9490         
9491         this.inputEl().on('change', this.onChange, this);
9492         
9493     },
9494     filterValidation : function(e){
9495         if(!e.isNavKeyPress()){
9496             this.validationTask.delay(this.validationDelay);
9497         }
9498     },
9499      /**
9500      * Validates the field value
9501      * @return {Boolean} True if the value is valid, else false
9502      */
9503     validate : function(){
9504         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9505         if(this.disabled || this.validateValue(this.getRawValue())){
9506             this.markValid();
9507             return true;
9508         }
9509         
9510         this.markInvalid();
9511         return false;
9512     },
9513     
9514     
9515     /**
9516      * Validates a value according to the field's validation rules and marks the field as invalid
9517      * if the validation fails
9518      * @param {Mixed} value The value to validate
9519      * @return {Boolean} True if the value is valid, else false
9520      */
9521     validateValue : function(value)
9522     {
9523         if(this.getVisibilityEl().hasClass('hidden')){
9524             return true;
9525         }
9526         
9527         if(value.length < 1)  { // if it's blank
9528             if(this.allowBlank){
9529                 return true;
9530             }
9531             return false;
9532         }
9533         
9534         if(value.length < this.minLength){
9535             return false;
9536         }
9537         if(value.length > this.maxLength){
9538             return false;
9539         }
9540         if(this.vtype){
9541             var vt = Roo.form.VTypes;
9542             if(!vt[this.vtype](value, this)){
9543                 return false;
9544             }
9545         }
9546         if(typeof this.validator == "function"){
9547             var msg = this.validator(value);
9548             if(msg !== true){
9549                 return false;
9550             }
9551             if (typeof(msg) == 'string') {
9552                 this.invalidText = msg;
9553             }
9554         }
9555         
9556         if(this.regex && !this.regex.test(value)){
9557             return false;
9558         }
9559         
9560         return true;
9561     },
9562     
9563      // private
9564     fireKey : function(e){
9565         //Roo.log('field ' + e.getKey());
9566         if(e.isNavKeyPress()){
9567             this.fireEvent("specialkey", this, e);
9568         }
9569     },
9570     focus : function (selectText){
9571         if(this.rendered){
9572             this.inputEl().focus();
9573             if(selectText === true){
9574                 this.inputEl().dom.select();
9575             }
9576         }
9577         return this;
9578     } ,
9579     
9580     onFocus : function(){
9581         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9582            // this.el.addClass(this.focusClass);
9583         }
9584         if(!this.hasFocus){
9585             this.hasFocus = true;
9586             this.startValue = this.getValue();
9587             this.fireEvent("focus", this);
9588         }
9589     },
9590     
9591     beforeBlur : Roo.emptyFn,
9592
9593     
9594     // private
9595     onBlur : function(){
9596         this.beforeBlur();
9597         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9598             //this.el.removeClass(this.focusClass);
9599         }
9600         this.hasFocus = false;
9601         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9602             this.validate();
9603         }
9604         var v = this.getValue();
9605         if(String(v) !== String(this.startValue)){
9606             this.fireEvent('change', this, v, this.startValue);
9607         }
9608         this.fireEvent("blur", this);
9609     },
9610     
9611     onChange : function(e)
9612     {
9613         var v = this.getValue();
9614         if(String(v) !== String(this.startValue)){
9615             this.fireEvent('change', this, v, this.startValue);
9616         }
9617         
9618     },
9619     
9620     /**
9621      * Resets the current field value to the originally loaded value and clears any validation messages
9622      */
9623     reset : function(){
9624         this.setValue(this.originalValue);
9625         this.validate();
9626     },
9627      /**
9628      * Returns the name of the field
9629      * @return {Mixed} name The name field
9630      */
9631     getName: function(){
9632         return this.name;
9633     },
9634      /**
9635      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9636      * @return {Mixed} value The field value
9637      */
9638     getValue : function(){
9639         
9640         var v = this.inputEl().getValue();
9641         
9642         return v;
9643     },
9644     /**
9645      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9646      * @return {Mixed} value The field value
9647      */
9648     getRawValue : function(){
9649         var v = this.inputEl().getValue();
9650         
9651         return v;
9652     },
9653     
9654     /**
9655      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9656      * @param {Mixed} value The value to set
9657      */
9658     setRawValue : function(v){
9659         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9660     },
9661     
9662     selectText : function(start, end){
9663         var v = this.getRawValue();
9664         if(v.length > 0){
9665             start = start === undefined ? 0 : start;
9666             end = end === undefined ? v.length : end;
9667             var d = this.inputEl().dom;
9668             if(d.setSelectionRange){
9669                 d.setSelectionRange(start, end);
9670             }else if(d.createTextRange){
9671                 var range = d.createTextRange();
9672                 range.moveStart("character", start);
9673                 range.moveEnd("character", v.length-end);
9674                 range.select();
9675             }
9676         }
9677     },
9678     
9679     /**
9680      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9681      * @param {Mixed} value The value to set
9682      */
9683     setValue : function(v){
9684         this.value = v;
9685         if(this.rendered){
9686             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9687             this.validate();
9688         }
9689     },
9690     
9691     /*
9692     processValue : function(value){
9693         if(this.stripCharsRe){
9694             var newValue = value.replace(this.stripCharsRe, '');
9695             if(newValue !== value){
9696                 this.setRawValue(newValue);
9697                 return newValue;
9698             }
9699         }
9700         return value;
9701     },
9702   */
9703     preFocus : function(){
9704         
9705         if(this.selectOnFocus){
9706             this.inputEl().dom.select();
9707         }
9708     },
9709     filterKeys : function(e){
9710         var k = e.getKey();
9711         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9712             return;
9713         }
9714         var c = e.getCharCode(), cc = String.fromCharCode(c);
9715         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9716             return;
9717         }
9718         if(!this.maskRe.test(cc)){
9719             e.stopEvent();
9720         }
9721     },
9722      /**
9723      * Clear any invalid styles/messages for this field
9724      */
9725     clearInvalid : function(){
9726         
9727         if(!this.el || this.preventMark){ // not rendered
9728             return;
9729         }
9730         
9731      
9732         this.el.removeClass(this.invalidClass);
9733         
9734         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9735             
9736             var feedback = this.el.select('.form-control-feedback', true).first();
9737             
9738             if(feedback){
9739                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9740             }
9741             
9742         }
9743         
9744         if(this.indicator){
9745             this.indicator.removeClass('visible');
9746             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9747         }
9748         
9749         this.fireEvent('valid', this);
9750     },
9751     
9752      /**
9753      * Mark this field as valid
9754      */
9755     markValid : function()
9756     {
9757         if(!this.el  || this.preventMark){ // not rendered...
9758             return;
9759         }
9760         
9761         this.el.removeClass([this.invalidClass, this.validClass]);
9762         
9763         var feedback = this.el.select('.form-control-feedback', true).first();
9764             
9765         if(feedback){
9766             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9767         }
9768         
9769         if(this.indicator){
9770             this.indicator.removeClass('visible');
9771             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9772         }
9773         
9774         if(this.disabled){
9775             return;
9776         }
9777         
9778         if(this.allowBlank && !this.getRawValue().length){
9779             return;
9780         }
9781         
9782         this.el.addClass(this.validClass);
9783         
9784         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9785             
9786             var feedback = this.el.select('.form-control-feedback', true).first();
9787             
9788             if(feedback){
9789                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9790                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9791             }
9792             
9793         }
9794         
9795         this.fireEvent('valid', this);
9796     },
9797     
9798      /**
9799      * Mark this field as invalid
9800      * @param {String} msg The validation message
9801      */
9802     markInvalid : function(msg)
9803     {
9804         if(!this.el  || this.preventMark){ // not rendered
9805             return;
9806         }
9807         
9808         this.el.removeClass([this.invalidClass, this.validClass]);
9809         
9810         var feedback = this.el.select('.form-control-feedback', true).first();
9811             
9812         if(feedback){
9813             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9814         }
9815
9816         if(this.disabled){
9817             return;
9818         }
9819         
9820         if(this.allowBlank && !this.getRawValue().length){
9821             return;
9822         }
9823         
9824         if(this.indicator){
9825             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9826             this.indicator.addClass('visible');
9827         }
9828         
9829         this.el.addClass(this.invalidClass);
9830         
9831         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9832             
9833             var feedback = this.el.select('.form-control-feedback', true).first();
9834             
9835             if(feedback){
9836                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9837                 
9838                 if(this.getValue().length || this.forceFeedback){
9839                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9840                 }
9841                 
9842             }
9843             
9844         }
9845         
9846         this.fireEvent('invalid', this, msg);
9847     },
9848     // private
9849     SafariOnKeyDown : function(event)
9850     {
9851         // this is a workaround for a password hang bug on chrome/ webkit.
9852         if (this.inputEl().dom.type != 'password') {
9853             return;
9854         }
9855         
9856         var isSelectAll = false;
9857         
9858         if(this.inputEl().dom.selectionEnd > 0){
9859             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9860         }
9861         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9862             event.preventDefault();
9863             this.setValue('');
9864             return;
9865         }
9866         
9867         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9868             
9869             event.preventDefault();
9870             // this is very hacky as keydown always get's upper case.
9871             //
9872             var cc = String.fromCharCode(event.getCharCode());
9873             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9874             
9875         }
9876     },
9877     adjustWidth : function(tag, w){
9878         tag = tag.toLowerCase();
9879         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9880             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9881                 if(tag == 'input'){
9882                     return w + 2;
9883                 }
9884                 if(tag == 'textarea'){
9885                     return w-2;
9886                 }
9887             }else if(Roo.isOpera){
9888                 if(tag == 'input'){
9889                     return w + 2;
9890                 }
9891                 if(tag == 'textarea'){
9892                     return w-2;
9893                 }
9894             }
9895         }
9896         return w;
9897     },
9898     
9899     setFieldLabel : function(v)
9900     {
9901         if(!this.rendered){
9902             return;
9903         }
9904         
9905         if(this.indicatorEl()){
9906             var ar = this.el.select('label > span',true);
9907             
9908             if (ar.elements.length) {
9909                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9910                 this.fieldLabel = v;
9911                 return;
9912             }
9913             
9914             var br = this.el.select('label',true);
9915             
9916             if(br.elements.length) {
9917                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9918                 this.fieldLabel = v;
9919                 return;
9920             }
9921             
9922             Roo.log('Cannot Found any of label > span || label in input');
9923             return;
9924         }
9925         
9926         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9927         this.fieldLabel = v;
9928         
9929         
9930     }
9931 });
9932
9933  
9934 /*
9935  * - LGPL
9936  *
9937  * Input
9938  * 
9939  */
9940
9941 /**
9942  * @class Roo.bootstrap.TextArea
9943  * @extends Roo.bootstrap.Input
9944  * Bootstrap TextArea class
9945  * @cfg {Number} cols Specifies the visible width of a text area
9946  * @cfg {Number} rows Specifies the visible number of lines in a text area
9947  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9948  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9949  * @cfg {string} html text
9950  * 
9951  * @constructor
9952  * Create a new TextArea
9953  * @param {Object} config The config object
9954  */
9955
9956 Roo.bootstrap.TextArea = function(config){
9957     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9958    
9959 };
9960
9961 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9962      
9963     cols : false,
9964     rows : 5,
9965     readOnly : false,
9966     warp : 'soft',
9967     resize : false,
9968     value: false,
9969     html: false,
9970     
9971     getAutoCreate : function(){
9972         
9973         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9974         
9975         var id = Roo.id();
9976         
9977         var cfg = {};
9978         
9979         if(this.inputType != 'hidden'){
9980             cfg.cls = 'form-group' //input-group
9981         }
9982         
9983         var input =  {
9984             tag: 'textarea',
9985             id : id,
9986             warp : this.warp,
9987             rows : this.rows,
9988             value : this.value || '',
9989             html: this.html || '',
9990             cls : 'form-control',
9991             placeholder : this.placeholder || '' 
9992             
9993         };
9994         
9995         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9996             input.maxLength = this.maxLength;
9997         }
9998         
9999         if(this.resize){
10000             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10001         }
10002         
10003         if(this.cols){
10004             input.cols = this.cols;
10005         }
10006         
10007         if (this.readOnly) {
10008             input.readonly = true;
10009         }
10010         
10011         if (this.name) {
10012             input.name = this.name;
10013         }
10014         
10015         if (this.size) {
10016             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10017         }
10018         
10019         var settings=this;
10020         ['xs','sm','md','lg'].map(function(size){
10021             if (settings[size]) {
10022                 cfg.cls += ' col-' + size + '-' + settings[size];
10023             }
10024         });
10025         
10026         var inputblock = input;
10027         
10028         if(this.hasFeedback && !this.allowBlank){
10029             
10030             var feedback = {
10031                 tag: 'span',
10032                 cls: 'glyphicon form-control-feedback'
10033             };
10034
10035             inputblock = {
10036                 cls : 'has-feedback',
10037                 cn :  [
10038                     input,
10039                     feedback
10040                 ] 
10041             };  
10042         }
10043         
10044         
10045         if (this.before || this.after) {
10046             
10047             inputblock = {
10048                 cls : 'input-group',
10049                 cn :  [] 
10050             };
10051             if (this.before) {
10052                 inputblock.cn.push({
10053                     tag :'span',
10054                     cls : 'input-group-addon',
10055                     html : this.before
10056                 });
10057             }
10058             
10059             inputblock.cn.push(input);
10060             
10061             if(this.hasFeedback && !this.allowBlank){
10062                 inputblock.cls += ' has-feedback';
10063                 inputblock.cn.push(feedback);
10064             }
10065             
10066             if (this.after) {
10067                 inputblock.cn.push({
10068                     tag :'span',
10069                     cls : 'input-group-addon',
10070                     html : this.after
10071                 });
10072             }
10073             
10074         }
10075         
10076         if (align ==='left' && this.fieldLabel.length) {
10077             cfg.cn = [
10078                 {
10079                     tag: 'label',
10080                     'for' :  id,
10081                     cls : 'control-label',
10082                     html : this.fieldLabel
10083                 },
10084                 {
10085                     cls : "",
10086                     cn: [
10087                         inputblock
10088                     ]
10089                 }
10090
10091             ];
10092             
10093             if(this.labelWidth > 12){
10094                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10095             }
10096
10097             if(this.labelWidth < 13 && this.labelmd == 0){
10098                 this.labelmd = this.labelWidth;
10099             }
10100
10101             if(this.labellg > 0){
10102                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10103                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10104             }
10105
10106             if(this.labelmd > 0){
10107                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10108                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10109             }
10110
10111             if(this.labelsm > 0){
10112                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10113                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10114             }
10115
10116             if(this.labelxs > 0){
10117                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10118                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10119             }
10120             
10121         } else if ( this.fieldLabel.length) {
10122             cfg.cn = [
10123
10124                {
10125                    tag: 'label',
10126                    //cls : 'input-group-addon',
10127                    html : this.fieldLabel
10128
10129                },
10130
10131                inputblock
10132
10133            ];
10134
10135         } else {
10136
10137             cfg.cn = [
10138
10139                 inputblock
10140
10141             ];
10142                 
10143         }
10144         
10145         if (this.disabled) {
10146             input.disabled=true;
10147         }
10148         
10149         return cfg;
10150         
10151     },
10152     /**
10153      * return the real textarea element.
10154      */
10155     inputEl: function ()
10156     {
10157         return this.el.select('textarea.form-control',true).first();
10158     },
10159     
10160     /**
10161      * Clear any invalid styles/messages for this field
10162      */
10163     clearInvalid : function()
10164     {
10165         
10166         if(!this.el || this.preventMark){ // not rendered
10167             return;
10168         }
10169         
10170         var label = this.el.select('label', true).first();
10171         var icon = this.el.select('i.fa-star', true).first();
10172         
10173         if(label && icon){
10174             icon.remove();
10175         }
10176         
10177         this.el.removeClass(this.invalidClass);
10178         
10179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10180             
10181             var feedback = this.el.select('.form-control-feedback', true).first();
10182             
10183             if(feedback){
10184                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10185             }
10186             
10187         }
10188         
10189         this.fireEvent('valid', this);
10190     },
10191     
10192      /**
10193      * Mark this field as valid
10194      */
10195     markValid : function()
10196     {
10197         if(!this.el  || this.preventMark){ // not rendered
10198             return;
10199         }
10200         
10201         this.el.removeClass([this.invalidClass, this.validClass]);
10202         
10203         var feedback = this.el.select('.form-control-feedback', true).first();
10204             
10205         if(feedback){
10206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10207         }
10208
10209         if(this.disabled || this.allowBlank){
10210             return;
10211         }
10212         
10213         var label = this.el.select('label', true).first();
10214         var icon = this.el.select('i.fa-star', true).first();
10215         
10216         if(label && icon){
10217             icon.remove();
10218         }
10219         
10220         this.el.addClass(this.validClass);
10221         
10222         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10223             
10224             var feedback = this.el.select('.form-control-feedback', true).first();
10225             
10226             if(feedback){
10227                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10228                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10229             }
10230             
10231         }
10232         
10233         this.fireEvent('valid', this);
10234     },
10235     
10236      /**
10237      * Mark this field as invalid
10238      * @param {String} msg The validation message
10239      */
10240     markInvalid : function(msg)
10241     {
10242         if(!this.el  || this.preventMark){ // not rendered
10243             return;
10244         }
10245         
10246         this.el.removeClass([this.invalidClass, this.validClass]);
10247         
10248         var feedback = this.el.select('.form-control-feedback', true).first();
10249             
10250         if(feedback){
10251             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10252         }
10253
10254         if(this.disabled || this.allowBlank){
10255             return;
10256         }
10257         
10258         var label = this.el.select('label', true).first();
10259         var icon = this.el.select('i.fa-star', true).first();
10260         
10261         if(!this.getValue().length && label && !icon){
10262             this.el.createChild({
10263                 tag : 'i',
10264                 cls : 'text-danger fa fa-lg fa-star',
10265                 tooltip : 'This field is required',
10266                 style : 'margin-right:5px;'
10267             }, label, true);
10268         }
10269
10270         this.el.addClass(this.invalidClass);
10271         
10272         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10273             
10274             var feedback = this.el.select('.form-control-feedback', true).first();
10275             
10276             if(feedback){
10277                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10278                 
10279                 if(this.getValue().length || this.forceFeedback){
10280                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10281                 }
10282                 
10283             }
10284             
10285         }
10286         
10287         this.fireEvent('invalid', this, msg);
10288     }
10289 });
10290
10291  
10292 /*
10293  * - LGPL
10294  *
10295  * trigger field - base class for combo..
10296  * 
10297  */
10298  
10299 /**
10300  * @class Roo.bootstrap.TriggerField
10301  * @extends Roo.bootstrap.Input
10302  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10303  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10304  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10305  * for which you can provide a custom implementation.  For example:
10306  * <pre><code>
10307 var trigger = new Roo.bootstrap.TriggerField();
10308 trigger.onTriggerClick = myTriggerFn;
10309 trigger.applyTo('my-field');
10310 </code></pre>
10311  *
10312  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10313  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10314  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10315  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10316  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10317
10318  * @constructor
10319  * Create a new TriggerField.
10320  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10321  * to the base TextField)
10322  */
10323 Roo.bootstrap.TriggerField = function(config){
10324     this.mimicing = false;
10325     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10326 };
10327
10328 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10329     /**
10330      * @cfg {String} triggerClass A CSS class to apply to the trigger
10331      */
10332      /**
10333      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10334      */
10335     hideTrigger:false,
10336
10337     /**
10338      * @cfg {Boolean} removable (true|false) special filter default false
10339      */
10340     removable : false,
10341     
10342     /** @cfg {Boolean} grow @hide */
10343     /** @cfg {Number} growMin @hide */
10344     /** @cfg {Number} growMax @hide */
10345
10346     /**
10347      * @hide 
10348      * @method
10349      */
10350     autoSize: Roo.emptyFn,
10351     // private
10352     monitorTab : true,
10353     // private
10354     deferHeight : true,
10355
10356     
10357     actionMode : 'wrap',
10358     
10359     caret : false,
10360     
10361     
10362     getAutoCreate : function(){
10363        
10364         var align = this.labelAlign || this.parentLabelAlign();
10365         
10366         var id = Roo.id();
10367         
10368         var cfg = {
10369             cls: 'form-group' //input-group
10370         };
10371         
10372         
10373         var input =  {
10374             tag: 'input',
10375             id : id,
10376             type : this.inputType,
10377             cls : 'form-control',
10378             autocomplete: 'new-password',
10379             placeholder : this.placeholder || '' 
10380             
10381         };
10382         if (this.name) {
10383             input.name = this.name;
10384         }
10385         if (this.size) {
10386             input.cls += ' input-' + this.size;
10387         }
10388         
10389         if (this.disabled) {
10390             input.disabled=true;
10391         }
10392         
10393         var inputblock = input;
10394         
10395         if(this.hasFeedback && !this.allowBlank){
10396             
10397             var feedback = {
10398                 tag: 'span',
10399                 cls: 'glyphicon form-control-feedback'
10400             };
10401             
10402             if(this.removable && !this.editable && !this.tickable){
10403                 inputblock = {
10404                     cls : 'has-feedback',
10405                     cn :  [
10406                         inputblock,
10407                         {
10408                             tag: 'button',
10409                             html : 'x',
10410                             cls : 'roo-combo-removable-btn close'
10411                         },
10412                         feedback
10413                     ] 
10414                 };
10415             } else {
10416                 inputblock = {
10417                     cls : 'has-feedback',
10418                     cn :  [
10419                         inputblock,
10420                         feedback
10421                     ] 
10422                 };
10423             }
10424
10425         } else {
10426             if(this.removable && !this.editable && !this.tickable){
10427                 inputblock = {
10428                     cls : 'roo-removable',
10429                     cn :  [
10430                         inputblock,
10431                         {
10432                             tag: 'button',
10433                             html : 'x',
10434                             cls : 'roo-combo-removable-btn close'
10435                         }
10436                     ] 
10437                 };
10438             }
10439         }
10440         
10441         if (this.before || this.after) {
10442             
10443             inputblock = {
10444                 cls : 'input-group',
10445                 cn :  [] 
10446             };
10447             if (this.before) {
10448                 inputblock.cn.push({
10449                     tag :'span',
10450                     cls : 'input-group-addon input-group-prepend input-group-text',
10451                     html : this.before
10452                 });
10453             }
10454             
10455             inputblock.cn.push(input);
10456             
10457             if(this.hasFeedback && !this.allowBlank){
10458                 inputblock.cls += ' has-feedback';
10459                 inputblock.cn.push(feedback);
10460             }
10461             
10462             if (this.after) {
10463                 inputblock.cn.push({
10464                     tag :'span',
10465                     cls : 'input-group-addon input-group-append input-group-text',
10466                     html : this.after
10467                 });
10468             }
10469             
10470         };
10471         
10472       
10473         
10474         var ibwrap = inputblock;
10475         
10476         if(this.multiple){
10477             ibwrap = {
10478                 tag: 'ul',
10479                 cls: 'roo-select2-choices',
10480                 cn:[
10481                     {
10482                         tag: 'li',
10483                         cls: 'roo-select2-search-field',
10484                         cn: [
10485
10486                             inputblock
10487                         ]
10488                     }
10489                 ]
10490             };
10491                 
10492         }
10493         
10494         var combobox = {
10495             cls: 'roo-select2-container input-group',
10496             cn: [
10497                  {
10498                     tag: 'input',
10499                     type : 'hidden',
10500                     cls: 'form-hidden-field'
10501                 },
10502                 ibwrap
10503             ]
10504         };
10505         
10506         if(!this.multiple && this.showToggleBtn){
10507             
10508             var caret = {
10509                         tag: 'span',
10510                         cls: 'caret'
10511              };
10512             if (this.caret != false) {
10513                 caret = {
10514                      tag: 'i',
10515                      cls: 'fa fa-' + this.caret
10516                 };
10517                 
10518             }
10519             
10520             combobox.cn.push({
10521                 tag :'span',
10522                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10523                 cn : [
10524                     caret,
10525                     {
10526                         tag: 'span',
10527                         cls: 'combobox-clear',
10528                         cn  : [
10529                             {
10530                                 tag : 'i',
10531                                 cls: 'icon-remove'
10532                             }
10533                         ]
10534                     }
10535                 ]
10536
10537             })
10538         }
10539         
10540         if(this.multiple){
10541             combobox.cls += ' roo-select2-container-multi';
10542         }
10543          var indicator = {
10544             tag : 'i',
10545             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10546             tooltip : 'This field is required'
10547         };
10548         if (Roo.bootstrap.version == 4) {
10549             indicator = {
10550                 tag : 'i',
10551                 style : 'display:none'
10552             };
10553         }
10554         
10555         
10556         if (align ==='left' && this.fieldLabel.length) {
10557             
10558             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10559
10560             cfg.cn = [
10561                 indicator,
10562                 {
10563                     tag: 'label',
10564                     'for' :  id,
10565                     cls : 'control-label',
10566                     html : this.fieldLabel
10567
10568                 },
10569                 {
10570                     cls : "", 
10571                     cn: [
10572                         combobox
10573                     ]
10574                 }
10575
10576             ];
10577             
10578             var labelCfg = cfg.cn[1];
10579             var contentCfg = cfg.cn[2];
10580             
10581             if(this.indicatorpos == 'right'){
10582                 cfg.cn = [
10583                     {
10584                         tag: 'label',
10585                         'for' :  id,
10586                         cls : 'control-label',
10587                         cn : [
10588                             {
10589                                 tag : 'span',
10590                                 html : this.fieldLabel
10591                             },
10592                             indicator
10593                         ]
10594                     },
10595                     {
10596                         cls : "", 
10597                         cn: [
10598                             combobox
10599                         ]
10600                     }
10601
10602                 ];
10603                 
10604                 labelCfg = cfg.cn[0];
10605                 contentCfg = cfg.cn[1];
10606             }
10607             
10608             if(this.labelWidth > 12){
10609                 labelCfg.style = "width: " + this.labelWidth + 'px';
10610             }
10611             
10612             if(this.labelWidth < 13 && this.labelmd == 0){
10613                 this.labelmd = this.labelWidth;
10614             }
10615             
10616             if(this.labellg > 0){
10617                 labelCfg.cls += ' col-lg-' + this.labellg;
10618                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10619             }
10620             
10621             if(this.labelmd > 0){
10622                 labelCfg.cls += ' col-md-' + this.labelmd;
10623                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10624             }
10625             
10626             if(this.labelsm > 0){
10627                 labelCfg.cls += ' col-sm-' + this.labelsm;
10628                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10629             }
10630             
10631             if(this.labelxs > 0){
10632                 labelCfg.cls += ' col-xs-' + this.labelxs;
10633                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10634             }
10635             
10636         } else if ( this.fieldLabel.length) {
10637 //                Roo.log(" label");
10638             cfg.cn = [
10639                 indicator,
10640                {
10641                    tag: 'label',
10642                    //cls : 'input-group-addon',
10643                    html : this.fieldLabel
10644
10645                },
10646
10647                combobox
10648
10649             ];
10650             
10651             if(this.indicatorpos == 'right'){
10652                 
10653                 cfg.cn = [
10654                     {
10655                        tag: 'label',
10656                        cn : [
10657                            {
10658                                tag : 'span',
10659                                html : this.fieldLabel
10660                            },
10661                            indicator
10662                        ]
10663
10664                     },
10665                     combobox
10666
10667                 ];
10668
10669             }
10670
10671         } else {
10672             
10673 //                Roo.log(" no label && no align");
10674                 cfg = combobox
10675                      
10676                 
10677         }
10678         
10679         var settings=this;
10680         ['xs','sm','md','lg'].map(function(size){
10681             if (settings[size]) {
10682                 cfg.cls += ' col-' + size + '-' + settings[size];
10683             }
10684         });
10685         
10686         return cfg;
10687         
10688     },
10689     
10690     
10691     
10692     // private
10693     onResize : function(w, h){
10694 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10695 //        if(typeof w == 'number'){
10696 //            var x = w - this.trigger.getWidth();
10697 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10698 //            this.trigger.setStyle('left', x+'px');
10699 //        }
10700     },
10701
10702     // private
10703     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10704
10705     // private
10706     getResizeEl : function(){
10707         return this.inputEl();
10708     },
10709
10710     // private
10711     getPositionEl : function(){
10712         return this.inputEl();
10713     },
10714
10715     // private
10716     alignErrorIcon : function(){
10717         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10718     },
10719
10720     // private
10721     initEvents : function(){
10722         
10723         this.createList();
10724         
10725         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10726         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10727         if(!this.multiple && this.showToggleBtn){
10728             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10729             if(this.hideTrigger){
10730                 this.trigger.setDisplayed(false);
10731             }
10732             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10733         }
10734         
10735         if(this.multiple){
10736             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10737         }
10738         
10739         if(this.removable && !this.editable && !this.tickable){
10740             var close = this.closeTriggerEl();
10741             
10742             if(close){
10743                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10744                 close.on('click', this.removeBtnClick, this, close);
10745             }
10746         }
10747         
10748         //this.trigger.addClassOnOver('x-form-trigger-over');
10749         //this.trigger.addClassOnClick('x-form-trigger-click');
10750         
10751         //if(!this.width){
10752         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10753         //}
10754     },
10755     
10756     closeTriggerEl : function()
10757     {
10758         var close = this.el.select('.roo-combo-removable-btn', true).first();
10759         return close ? close : false;
10760     },
10761     
10762     removeBtnClick : function(e, h, el)
10763     {
10764         e.preventDefault();
10765         
10766         if(this.fireEvent("remove", this) !== false){
10767             this.reset();
10768             this.fireEvent("afterremove", this)
10769         }
10770     },
10771     
10772     createList : function()
10773     {
10774         this.list = Roo.get(document.body).createChild({
10775             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10776             cls: 'typeahead typeahead-long dropdown-menu',
10777             style: 'display:none'
10778         });
10779         
10780         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10781         
10782     },
10783
10784     // private
10785     initTrigger : function(){
10786        
10787     },
10788
10789     // private
10790     onDestroy : function(){
10791         if(this.trigger){
10792             this.trigger.removeAllListeners();
10793           //  this.trigger.remove();
10794         }
10795         //if(this.wrap){
10796         //    this.wrap.remove();
10797         //}
10798         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10799     },
10800
10801     // private
10802     onFocus : function(){
10803         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10804         /*
10805         if(!this.mimicing){
10806             this.wrap.addClass('x-trigger-wrap-focus');
10807             this.mimicing = true;
10808             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10809             if(this.monitorTab){
10810                 this.el.on("keydown", this.checkTab, this);
10811             }
10812         }
10813         */
10814     },
10815
10816     // private
10817     checkTab : function(e){
10818         if(e.getKey() == e.TAB){
10819             this.triggerBlur();
10820         }
10821     },
10822
10823     // private
10824     onBlur : function(){
10825         // do nothing
10826     },
10827
10828     // private
10829     mimicBlur : function(e, t){
10830         /*
10831         if(!this.wrap.contains(t) && this.validateBlur()){
10832             this.triggerBlur();
10833         }
10834         */
10835     },
10836
10837     // private
10838     triggerBlur : function(){
10839         this.mimicing = false;
10840         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10841         if(this.monitorTab){
10842             this.el.un("keydown", this.checkTab, this);
10843         }
10844         //this.wrap.removeClass('x-trigger-wrap-focus');
10845         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10846     },
10847
10848     // private
10849     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10850     validateBlur : function(e, t){
10851         return true;
10852     },
10853
10854     // private
10855     onDisable : function(){
10856         this.inputEl().dom.disabled = true;
10857         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10858         //if(this.wrap){
10859         //    this.wrap.addClass('x-item-disabled');
10860         //}
10861     },
10862
10863     // private
10864     onEnable : function(){
10865         this.inputEl().dom.disabled = false;
10866         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10867         //if(this.wrap){
10868         //    this.el.removeClass('x-item-disabled');
10869         //}
10870     },
10871
10872     // private
10873     onShow : function(){
10874         var ae = this.getActionEl();
10875         
10876         if(ae){
10877             ae.dom.style.display = '';
10878             ae.dom.style.visibility = 'visible';
10879         }
10880     },
10881
10882     // private
10883     
10884     onHide : function(){
10885         var ae = this.getActionEl();
10886         ae.dom.style.display = 'none';
10887     },
10888
10889     /**
10890      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10891      * by an implementing function.
10892      * @method
10893      * @param {EventObject} e
10894      */
10895     onTriggerClick : Roo.emptyFn
10896 });
10897  /*
10898  * Based on:
10899  * Ext JS Library 1.1.1
10900  * Copyright(c) 2006-2007, Ext JS, LLC.
10901  *
10902  * Originally Released Under LGPL - original licence link has changed is not relivant.
10903  *
10904  * Fork - LGPL
10905  * <script type="text/javascript">
10906  */
10907
10908
10909 /**
10910  * @class Roo.data.SortTypes
10911  * @singleton
10912  * Defines the default sorting (casting?) comparison functions used when sorting data.
10913  */
10914 Roo.data.SortTypes = {
10915     /**
10916      * Default sort that does nothing
10917      * @param {Mixed} s The value being converted
10918      * @return {Mixed} The comparison value
10919      */
10920     none : function(s){
10921         return s;
10922     },
10923     
10924     /**
10925      * The regular expression used to strip tags
10926      * @type {RegExp}
10927      * @property
10928      */
10929     stripTagsRE : /<\/?[^>]+>/gi,
10930     
10931     /**
10932      * Strips all HTML tags to sort on text only
10933      * @param {Mixed} s The value being converted
10934      * @return {String} The comparison value
10935      */
10936     asText : function(s){
10937         return String(s).replace(this.stripTagsRE, "");
10938     },
10939     
10940     /**
10941      * Strips all HTML tags to sort on text only - Case insensitive
10942      * @param {Mixed} s The value being converted
10943      * @return {String} The comparison value
10944      */
10945     asUCText : function(s){
10946         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10947     },
10948     
10949     /**
10950      * Case insensitive string
10951      * @param {Mixed} s The value being converted
10952      * @return {String} The comparison value
10953      */
10954     asUCString : function(s) {
10955         return String(s).toUpperCase();
10956     },
10957     
10958     /**
10959      * Date sorting
10960      * @param {Mixed} s The value being converted
10961      * @return {Number} The comparison value
10962      */
10963     asDate : function(s) {
10964         if(!s){
10965             return 0;
10966         }
10967         if(s instanceof Date){
10968             return s.getTime();
10969         }
10970         return Date.parse(String(s));
10971     },
10972     
10973     /**
10974      * Float sorting
10975      * @param {Mixed} s The value being converted
10976      * @return {Float} The comparison value
10977      */
10978     asFloat : function(s) {
10979         var val = parseFloat(String(s).replace(/,/g, ""));
10980         if(isNaN(val)) {
10981             val = 0;
10982         }
10983         return val;
10984     },
10985     
10986     /**
10987      * Integer sorting
10988      * @param {Mixed} s The value being converted
10989      * @return {Number} The comparison value
10990      */
10991     asInt : function(s) {
10992         var val = parseInt(String(s).replace(/,/g, ""));
10993         if(isNaN(val)) {
10994             val = 0;
10995         }
10996         return val;
10997     }
10998 };/*
10999  * Based on:
11000  * Ext JS Library 1.1.1
11001  * Copyright(c) 2006-2007, Ext JS, LLC.
11002  *
11003  * Originally Released Under LGPL - original licence link has changed is not relivant.
11004  *
11005  * Fork - LGPL
11006  * <script type="text/javascript">
11007  */
11008
11009 /**
11010 * @class Roo.data.Record
11011  * Instances of this class encapsulate both record <em>definition</em> information, and record
11012  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11013  * to access Records cached in an {@link Roo.data.Store} object.<br>
11014  * <p>
11015  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11016  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11017  * objects.<br>
11018  * <p>
11019  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11020  * @constructor
11021  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11022  * {@link #create}. The parameters are the same.
11023  * @param {Array} data An associative Array of data values keyed by the field name.
11024  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11025  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11026  * not specified an integer id is generated.
11027  */
11028 Roo.data.Record = function(data, id){
11029     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11030     this.data = data;
11031 };
11032
11033 /**
11034  * Generate a constructor for a specific record layout.
11035  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11036  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11037  * Each field definition object may contain the following properties: <ul>
11038  * <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,
11039  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11040  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11041  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11042  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11043  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11044  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11045  * this may be omitted.</p></li>
11046  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11047  * <ul><li>auto (Default, implies no conversion)</li>
11048  * <li>string</li>
11049  * <li>int</li>
11050  * <li>float</li>
11051  * <li>boolean</li>
11052  * <li>date</li></ul></p></li>
11053  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11054  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11055  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11056  * by the Reader into an object that will be stored in the Record. It is passed the
11057  * following parameters:<ul>
11058  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11059  * </ul></p></li>
11060  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11061  * </ul>
11062  * <br>usage:<br><pre><code>
11063 var TopicRecord = Roo.data.Record.create(
11064     {name: 'title', mapping: 'topic_title'},
11065     {name: 'author', mapping: 'username'},
11066     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11067     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11068     {name: 'lastPoster', mapping: 'user2'},
11069     {name: 'excerpt', mapping: 'post_text'}
11070 );
11071
11072 var myNewRecord = new TopicRecord({
11073     title: 'Do my job please',
11074     author: 'noobie',
11075     totalPosts: 1,
11076     lastPost: new Date(),
11077     lastPoster: 'Animal',
11078     excerpt: 'No way dude!'
11079 });
11080 myStore.add(myNewRecord);
11081 </code></pre>
11082  * @method create
11083  * @static
11084  */
11085 Roo.data.Record.create = function(o){
11086     var f = function(){
11087         f.superclass.constructor.apply(this, arguments);
11088     };
11089     Roo.extend(f, Roo.data.Record);
11090     var p = f.prototype;
11091     p.fields = new Roo.util.MixedCollection(false, function(field){
11092         return field.name;
11093     });
11094     for(var i = 0, len = o.length; i < len; i++){
11095         p.fields.add(new Roo.data.Field(o[i]));
11096     }
11097     f.getField = function(name){
11098         return p.fields.get(name);  
11099     };
11100     return f;
11101 };
11102
11103 Roo.data.Record.AUTO_ID = 1000;
11104 Roo.data.Record.EDIT = 'edit';
11105 Roo.data.Record.REJECT = 'reject';
11106 Roo.data.Record.COMMIT = 'commit';
11107
11108 Roo.data.Record.prototype = {
11109     /**
11110      * Readonly flag - true if this record has been modified.
11111      * @type Boolean
11112      */
11113     dirty : false,
11114     editing : false,
11115     error: null,
11116     modified: null,
11117
11118     // private
11119     join : function(store){
11120         this.store = store;
11121     },
11122
11123     /**
11124      * Set the named field to the specified value.
11125      * @param {String} name The name of the field to set.
11126      * @param {Object} value The value to set the field to.
11127      */
11128     set : function(name, value){
11129         if(this.data[name] == value){
11130             return;
11131         }
11132         this.dirty = true;
11133         if(!this.modified){
11134             this.modified = {};
11135         }
11136         if(typeof this.modified[name] == 'undefined'){
11137             this.modified[name] = this.data[name];
11138         }
11139         this.data[name] = value;
11140         if(!this.editing && this.store){
11141             this.store.afterEdit(this);
11142         }       
11143     },
11144
11145     /**
11146      * Get the value of the named field.
11147      * @param {String} name The name of the field to get the value of.
11148      * @return {Object} The value of the field.
11149      */
11150     get : function(name){
11151         return this.data[name]; 
11152     },
11153
11154     // private
11155     beginEdit : function(){
11156         this.editing = true;
11157         this.modified = {}; 
11158     },
11159
11160     // private
11161     cancelEdit : function(){
11162         this.editing = false;
11163         delete this.modified;
11164     },
11165
11166     // private
11167     endEdit : function(){
11168         this.editing = false;
11169         if(this.dirty && this.store){
11170             this.store.afterEdit(this);
11171         }
11172     },
11173
11174     /**
11175      * Usually called by the {@link Roo.data.Store} which owns the Record.
11176      * Rejects all changes made to the Record since either creation, or the last commit operation.
11177      * Modified fields are reverted to their original values.
11178      * <p>
11179      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11180      * of reject operations.
11181      */
11182     reject : function(){
11183         var m = this.modified;
11184         for(var n in m){
11185             if(typeof m[n] != "function"){
11186                 this.data[n] = m[n];
11187             }
11188         }
11189         this.dirty = false;
11190         delete this.modified;
11191         this.editing = false;
11192         if(this.store){
11193             this.store.afterReject(this);
11194         }
11195     },
11196
11197     /**
11198      * Usually called by the {@link Roo.data.Store} which owns the Record.
11199      * Commits all changes made to the Record since either creation, or the last commit operation.
11200      * <p>
11201      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11202      * of commit operations.
11203      */
11204     commit : function(){
11205         this.dirty = false;
11206         delete this.modified;
11207         this.editing = false;
11208         if(this.store){
11209             this.store.afterCommit(this);
11210         }
11211     },
11212
11213     // private
11214     hasError : function(){
11215         return this.error != null;
11216     },
11217
11218     // private
11219     clearError : function(){
11220         this.error = null;
11221     },
11222
11223     /**
11224      * Creates a copy of this record.
11225      * @param {String} id (optional) A new record id if you don't want to use this record's id
11226      * @return {Record}
11227      */
11228     copy : function(newId) {
11229         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11230     }
11231 };/*
11232  * Based on:
11233  * Ext JS Library 1.1.1
11234  * Copyright(c) 2006-2007, Ext JS, LLC.
11235  *
11236  * Originally Released Under LGPL - original licence link has changed is not relivant.
11237  *
11238  * Fork - LGPL
11239  * <script type="text/javascript">
11240  */
11241
11242
11243
11244 /**
11245  * @class Roo.data.Store
11246  * @extends Roo.util.Observable
11247  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11248  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11249  * <p>
11250  * 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
11251  * has no knowledge of the format of the data returned by the Proxy.<br>
11252  * <p>
11253  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11254  * instances from the data object. These records are cached and made available through accessor functions.
11255  * @constructor
11256  * Creates a new Store.
11257  * @param {Object} config A config object containing the objects needed for the Store to access data,
11258  * and read the data into Records.
11259  */
11260 Roo.data.Store = function(config){
11261     this.data = new Roo.util.MixedCollection(false);
11262     this.data.getKey = function(o){
11263         return o.id;
11264     };
11265     this.baseParams = {};
11266     // private
11267     this.paramNames = {
11268         "start" : "start",
11269         "limit" : "limit",
11270         "sort" : "sort",
11271         "dir" : "dir",
11272         "multisort" : "_multisort"
11273     };
11274
11275     if(config && config.data){
11276         this.inlineData = config.data;
11277         delete config.data;
11278     }
11279
11280     Roo.apply(this, config);
11281     
11282     if(this.reader){ // reader passed
11283         this.reader = Roo.factory(this.reader, Roo.data);
11284         this.reader.xmodule = this.xmodule || false;
11285         if(!this.recordType){
11286             this.recordType = this.reader.recordType;
11287         }
11288         if(this.reader.onMetaChange){
11289             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11290         }
11291     }
11292
11293     if(this.recordType){
11294         this.fields = this.recordType.prototype.fields;
11295     }
11296     this.modified = [];
11297
11298     this.addEvents({
11299         /**
11300          * @event datachanged
11301          * Fires when the data cache has changed, and a widget which is using this Store
11302          * as a Record cache should refresh its view.
11303          * @param {Store} this
11304          */
11305         datachanged : true,
11306         /**
11307          * @event metachange
11308          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11309          * @param {Store} this
11310          * @param {Object} meta The JSON metadata
11311          */
11312         metachange : true,
11313         /**
11314          * @event add
11315          * Fires when Records have been added to the Store
11316          * @param {Store} this
11317          * @param {Roo.data.Record[]} records The array of Records added
11318          * @param {Number} index The index at which the record(s) were added
11319          */
11320         add : true,
11321         /**
11322          * @event remove
11323          * Fires when a Record has been removed from the Store
11324          * @param {Store} this
11325          * @param {Roo.data.Record} record The Record that was removed
11326          * @param {Number} index The index at which the record was removed
11327          */
11328         remove : true,
11329         /**
11330          * @event update
11331          * Fires when a Record has been updated
11332          * @param {Store} this
11333          * @param {Roo.data.Record} record The Record that was updated
11334          * @param {String} operation The update operation being performed.  Value may be one of:
11335          * <pre><code>
11336  Roo.data.Record.EDIT
11337  Roo.data.Record.REJECT
11338  Roo.data.Record.COMMIT
11339          * </code></pre>
11340          */
11341         update : true,
11342         /**
11343          * @event clear
11344          * Fires when the data cache has been cleared.
11345          * @param {Store} this
11346          */
11347         clear : true,
11348         /**
11349          * @event beforeload
11350          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11351          * the load action will be canceled.
11352          * @param {Store} this
11353          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11354          */
11355         beforeload : true,
11356         /**
11357          * @event beforeloadadd
11358          * Fires after a new set of Records has been loaded.
11359          * @param {Store} this
11360          * @param {Roo.data.Record[]} records The Records that were loaded
11361          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11362          */
11363         beforeloadadd : true,
11364         /**
11365          * @event load
11366          * Fires after a new set of Records has been loaded, before they are added to the store.
11367          * @param {Store} this
11368          * @param {Roo.data.Record[]} records The Records that were loaded
11369          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11370          * @params {Object} return from reader
11371          */
11372         load : true,
11373         /**
11374          * @event loadexception
11375          * Fires if an exception occurs in the Proxy during loading.
11376          * Called with the signature of the Proxy's "loadexception" event.
11377          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11378          * 
11379          * @param {Proxy} 
11380          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11381          * @param {Object} load options 
11382          * @param {Object} jsonData from your request (normally this contains the Exception)
11383          */
11384         loadexception : true
11385     });
11386     
11387     if(this.proxy){
11388         this.proxy = Roo.factory(this.proxy, Roo.data);
11389         this.proxy.xmodule = this.xmodule || false;
11390         this.relayEvents(this.proxy,  ["loadexception"]);
11391     }
11392     this.sortToggle = {};
11393     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11394
11395     Roo.data.Store.superclass.constructor.call(this);
11396
11397     if(this.inlineData){
11398         this.loadData(this.inlineData);
11399         delete this.inlineData;
11400     }
11401 };
11402
11403 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11404      /**
11405     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11406     * without a remote query - used by combo/forms at present.
11407     */
11408     
11409     /**
11410     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11411     */
11412     /**
11413     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11414     */
11415     /**
11416     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11417     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11418     */
11419     /**
11420     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11421     * on any HTTP request
11422     */
11423     /**
11424     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11425     */
11426     /**
11427     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11428     */
11429     multiSort: false,
11430     /**
11431     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11432     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11433     */
11434     remoteSort : false,
11435
11436     /**
11437     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11438      * loaded or when a record is removed. (defaults to false).
11439     */
11440     pruneModifiedRecords : false,
11441
11442     // private
11443     lastOptions : null,
11444
11445     /**
11446      * Add Records to the Store and fires the add event.
11447      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11448      */
11449     add : function(records){
11450         records = [].concat(records);
11451         for(var i = 0, len = records.length; i < len; i++){
11452             records[i].join(this);
11453         }
11454         var index = this.data.length;
11455         this.data.addAll(records);
11456         this.fireEvent("add", this, records, index);
11457     },
11458
11459     /**
11460      * Remove a Record from the Store and fires the remove event.
11461      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11462      */
11463     remove : function(record){
11464         var index = this.data.indexOf(record);
11465         this.data.removeAt(index);
11466  
11467         if(this.pruneModifiedRecords){
11468             this.modified.remove(record);
11469         }
11470         this.fireEvent("remove", this, record, index);
11471     },
11472
11473     /**
11474      * Remove all Records from the Store and fires the clear event.
11475      */
11476     removeAll : function(){
11477         this.data.clear();
11478         if(this.pruneModifiedRecords){
11479             this.modified = [];
11480         }
11481         this.fireEvent("clear", this);
11482     },
11483
11484     /**
11485      * Inserts Records to the Store at the given index and fires the add event.
11486      * @param {Number} index The start index at which to insert the passed Records.
11487      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11488      */
11489     insert : function(index, records){
11490         records = [].concat(records);
11491         for(var i = 0, len = records.length; i < len; i++){
11492             this.data.insert(index, records[i]);
11493             records[i].join(this);
11494         }
11495         this.fireEvent("add", this, records, index);
11496     },
11497
11498     /**
11499      * Get the index within the cache of the passed Record.
11500      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11501      * @return {Number} The index of the passed Record. Returns -1 if not found.
11502      */
11503     indexOf : function(record){
11504         return this.data.indexOf(record);
11505     },
11506
11507     /**
11508      * Get the index within the cache of the Record with the passed id.
11509      * @param {String} id The id of the Record to find.
11510      * @return {Number} The index of the Record. Returns -1 if not found.
11511      */
11512     indexOfId : function(id){
11513         return this.data.indexOfKey(id);
11514     },
11515
11516     /**
11517      * Get the Record with the specified id.
11518      * @param {String} id The id of the Record to find.
11519      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11520      */
11521     getById : function(id){
11522         return this.data.key(id);
11523     },
11524
11525     /**
11526      * Get the Record at the specified index.
11527      * @param {Number} index The index of the Record to find.
11528      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11529      */
11530     getAt : function(index){
11531         return this.data.itemAt(index);
11532     },
11533
11534     /**
11535      * Returns a range of Records between specified indices.
11536      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11537      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11538      * @return {Roo.data.Record[]} An array of Records
11539      */
11540     getRange : function(start, end){
11541         return this.data.getRange(start, end);
11542     },
11543
11544     // private
11545     storeOptions : function(o){
11546         o = Roo.apply({}, o);
11547         delete o.callback;
11548         delete o.scope;
11549         this.lastOptions = o;
11550     },
11551
11552     /**
11553      * Loads the Record cache from the configured Proxy using the configured Reader.
11554      * <p>
11555      * If using remote paging, then the first load call must specify the <em>start</em>
11556      * and <em>limit</em> properties in the options.params property to establish the initial
11557      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11558      * <p>
11559      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11560      * and this call will return before the new data has been loaded. Perform any post-processing
11561      * in a callback function, or in a "load" event handler.</strong>
11562      * <p>
11563      * @param {Object} options An object containing properties which control loading options:<ul>
11564      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11565      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11566      * passed the following arguments:<ul>
11567      * <li>r : Roo.data.Record[]</li>
11568      * <li>options: Options object from the load call</li>
11569      * <li>success: Boolean success indicator</li></ul></li>
11570      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11571      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11572      * </ul>
11573      */
11574     load : function(options){
11575         options = options || {};
11576         if(this.fireEvent("beforeload", this, options) !== false){
11577             this.storeOptions(options);
11578             var p = Roo.apply(options.params || {}, this.baseParams);
11579             // if meta was not loaded from remote source.. try requesting it.
11580             if (!this.reader.metaFromRemote) {
11581                 p._requestMeta = 1;
11582             }
11583             if(this.sortInfo && this.remoteSort){
11584                 var pn = this.paramNames;
11585                 p[pn["sort"]] = this.sortInfo.field;
11586                 p[pn["dir"]] = this.sortInfo.direction;
11587             }
11588             if (this.multiSort) {
11589                 var pn = this.paramNames;
11590                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11591             }
11592             
11593             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11594         }
11595     },
11596
11597     /**
11598      * Reloads the Record cache from the configured Proxy using the configured Reader and
11599      * the options from the last load operation performed.
11600      * @param {Object} options (optional) An object containing properties which may override the options
11601      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11602      * the most recently used options are reused).
11603      */
11604     reload : function(options){
11605         this.load(Roo.applyIf(options||{}, this.lastOptions));
11606     },
11607
11608     // private
11609     // Called as a callback by the Reader during a load operation.
11610     loadRecords : function(o, options, success){
11611         if(!o || success === false){
11612             if(success !== false){
11613                 this.fireEvent("load", this, [], options, o);
11614             }
11615             if(options.callback){
11616                 options.callback.call(options.scope || this, [], options, false);
11617             }
11618             return;
11619         }
11620         // if data returned failure - throw an exception.
11621         if (o.success === false) {
11622             // show a message if no listener is registered.
11623             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11624                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11625             }
11626             // loadmask wil be hooked into this..
11627             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11628             return;
11629         }
11630         var r = o.records, t = o.totalRecords || r.length;
11631         
11632         this.fireEvent("beforeloadadd", this, r, options, o);
11633         
11634         if(!options || options.add !== true){
11635             if(this.pruneModifiedRecords){
11636                 this.modified = [];
11637             }
11638             for(var i = 0, len = r.length; i < len; i++){
11639                 r[i].join(this);
11640             }
11641             if(this.snapshot){
11642                 this.data = this.snapshot;
11643                 delete this.snapshot;
11644             }
11645             this.data.clear();
11646             this.data.addAll(r);
11647             this.totalLength = t;
11648             this.applySort();
11649             this.fireEvent("datachanged", this);
11650         }else{
11651             this.totalLength = Math.max(t, this.data.length+r.length);
11652             this.add(r);
11653         }
11654         
11655         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11656                 
11657             var e = new Roo.data.Record({});
11658
11659             e.set(this.parent.displayField, this.parent.emptyTitle);
11660             e.set(this.parent.valueField, '');
11661
11662             this.insert(0, e);
11663         }
11664             
11665         this.fireEvent("load", this, r, options, o);
11666         if(options.callback){
11667             options.callback.call(options.scope || this, r, options, true);
11668         }
11669     },
11670
11671
11672     /**
11673      * Loads data from a passed data block. A Reader which understands the format of the data
11674      * must have been configured in the constructor.
11675      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11676      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11677      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11678      */
11679     loadData : function(o, append){
11680         var r = this.reader.readRecords(o);
11681         this.loadRecords(r, {add: append}, true);
11682     },
11683
11684     /**
11685      * Gets the number of cached records.
11686      * <p>
11687      * <em>If using paging, this may not be the total size of the dataset. If the data object
11688      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11689      * the data set size</em>
11690      */
11691     getCount : function(){
11692         return this.data.length || 0;
11693     },
11694
11695     /**
11696      * Gets the total number of records in the dataset as returned by the server.
11697      * <p>
11698      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11699      * the dataset size</em>
11700      */
11701     getTotalCount : function(){
11702         return this.totalLength || 0;
11703     },
11704
11705     /**
11706      * Returns the sort state of the Store as an object with two properties:
11707      * <pre><code>
11708  field {String} The name of the field by which the Records are sorted
11709  direction {String} The sort order, "ASC" or "DESC"
11710      * </code></pre>
11711      */
11712     getSortState : function(){
11713         return this.sortInfo;
11714     },
11715
11716     // private
11717     applySort : function(){
11718         if(this.sortInfo && !this.remoteSort){
11719             var s = this.sortInfo, f = s.field;
11720             var st = this.fields.get(f).sortType;
11721             var fn = function(r1, r2){
11722                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11723                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11724             };
11725             this.data.sort(s.direction, fn);
11726             if(this.snapshot && this.snapshot != this.data){
11727                 this.snapshot.sort(s.direction, fn);
11728             }
11729         }
11730     },
11731
11732     /**
11733      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
11738         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11739     },
11740
11741     /**
11742      * Sort the Records.
11743      * If remote sorting is used, the sort is performed on the server, and the cache is
11744      * reloaded. If local sorting is used, the cache is sorted internally.
11745      * @param {String} fieldName The name of the field to sort by.
11746      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11747      */
11748     sort : function(fieldName, dir){
11749         var f = this.fields.get(fieldName);
11750         if(!dir){
11751             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11752             
11753             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11754                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11755             }else{
11756                 dir = f.sortDir;
11757             }
11758         }
11759         this.sortToggle[f.name] = dir;
11760         this.sortInfo = {field: f.name, direction: dir};
11761         if(!this.remoteSort){
11762             this.applySort();
11763             this.fireEvent("datachanged", this);
11764         }else{
11765             this.load(this.lastOptions);
11766         }
11767     },
11768
11769     /**
11770      * Calls the specified function for each of the Records in the cache.
11771      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11772      * Returning <em>false</em> aborts and exits the iteration.
11773      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11774      */
11775     each : function(fn, scope){
11776         this.data.each(fn, scope);
11777     },
11778
11779     /**
11780      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11781      * (e.g., during paging).
11782      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11783      */
11784     getModifiedRecords : function(){
11785         return this.modified;
11786     },
11787
11788     // private
11789     createFilterFn : function(property, value, anyMatch){
11790         if(!value.exec){ // not a regex
11791             value = String(value);
11792             if(value.length == 0){
11793                 return false;
11794             }
11795             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11796         }
11797         return function(r){
11798             return value.test(r.data[property]);
11799         };
11800     },
11801
11802     /**
11803      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11804      * @param {String} property A field on your records
11805      * @param {Number} start The record index to start at (defaults to 0)
11806      * @param {Number} end The last record index to include (defaults to length - 1)
11807      * @return {Number} The sum
11808      */
11809     sum : function(property, start, end){
11810         var rs = this.data.items, v = 0;
11811         start = start || 0;
11812         end = (end || end === 0) ? end : rs.length-1;
11813
11814         for(var i = start; i <= end; i++){
11815             v += (rs[i].data[property] || 0);
11816         }
11817         return v;
11818     },
11819
11820     /**
11821      * Filter the records by a specified property.
11822      * @param {String} field A field on your records
11823      * @param {String/RegExp} value Either a string that the field
11824      * should start with or a RegExp to test against the field
11825      * @param {Boolean} anyMatch True to match any part not just the beginning
11826      */
11827     filter : function(property, value, anyMatch){
11828         var fn = this.createFilterFn(property, value, anyMatch);
11829         return fn ? this.filterBy(fn) : this.clearFilter();
11830     },
11831
11832     /**
11833      * Filter by a function. The specified function will be called with each
11834      * record in this data source. If the function returns true the record is included,
11835      * otherwise it is filtered.
11836      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11837      * @param {Object} scope (optional) The scope of the function (defaults to this)
11838      */
11839     filterBy : function(fn, scope){
11840         this.snapshot = this.snapshot || this.data;
11841         this.data = this.queryBy(fn, scope||this);
11842         this.fireEvent("datachanged", this);
11843     },
11844
11845     /**
11846      * Query the records by a specified property.
11847      * @param {String} field A field on your records
11848      * @param {String/RegExp} value Either a string that the field
11849      * should start with or a RegExp to test against the field
11850      * @param {Boolean} anyMatch True to match any part not just the beginning
11851      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11852      */
11853     query : function(property, value, anyMatch){
11854         var fn = this.createFilterFn(property, value, anyMatch);
11855         return fn ? this.queryBy(fn) : this.data.clone();
11856     },
11857
11858     /**
11859      * Query by a function. The specified function will be called with each
11860      * record in this data source. If the function returns true the record is included
11861      * in the results.
11862      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11863      * @param {Object} scope (optional) The scope of the function (defaults to this)
11864       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11865      **/
11866     queryBy : function(fn, scope){
11867         var data = this.snapshot || this.data;
11868         return data.filterBy(fn, scope||this);
11869     },
11870
11871     /**
11872      * Collects unique values for a particular dataIndex from this store.
11873      * @param {String} dataIndex The property to collect
11874      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11875      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11876      * @return {Array} An array of the unique values
11877      **/
11878     collect : function(dataIndex, allowNull, bypassFilter){
11879         var d = (bypassFilter === true && this.snapshot) ?
11880                 this.snapshot.items : this.data.items;
11881         var v, sv, r = [], l = {};
11882         for(var i = 0, len = d.length; i < len; i++){
11883             v = d[i].data[dataIndex];
11884             sv = String(v);
11885             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11886                 l[sv] = true;
11887                 r[r.length] = v;
11888             }
11889         }
11890         return r;
11891     },
11892
11893     /**
11894      * Revert to a view of the Record cache with no filtering applied.
11895      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11896      */
11897     clearFilter : function(suppressEvent){
11898         if(this.snapshot && this.snapshot != this.data){
11899             this.data = this.snapshot;
11900             delete this.snapshot;
11901             if(suppressEvent !== true){
11902                 this.fireEvent("datachanged", this);
11903             }
11904         }
11905     },
11906
11907     // private
11908     afterEdit : function(record){
11909         if(this.modified.indexOf(record) == -1){
11910             this.modified.push(record);
11911         }
11912         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11913     },
11914     
11915     // private
11916     afterReject : function(record){
11917         this.modified.remove(record);
11918         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11919     },
11920
11921     // private
11922     afterCommit : function(record){
11923         this.modified.remove(record);
11924         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11925     },
11926
11927     /**
11928      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11929      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11930      */
11931     commitChanges : 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].commit();
11936         }
11937     },
11938
11939     /**
11940      * Cancel outstanding changes on all changed records.
11941      */
11942     rejectChanges : function(){
11943         var m = this.modified.slice(0);
11944         this.modified = [];
11945         for(var i = 0, len = m.length; i < len; i++){
11946             m[i].reject();
11947         }
11948     },
11949
11950     onMetaChange : function(meta, rtype, o){
11951         this.recordType = rtype;
11952         this.fields = rtype.prototype.fields;
11953         delete this.snapshot;
11954         this.sortInfo = meta.sortInfo || this.sortInfo;
11955         this.modified = [];
11956         this.fireEvent('metachange', this, this.reader.meta);
11957     },
11958     
11959     moveIndex : function(data, type)
11960     {
11961         var index = this.indexOf(data);
11962         
11963         var newIndex = index + type;
11964         
11965         this.remove(data);
11966         
11967         this.insert(newIndex, data);
11968         
11969     }
11970 });/*
11971  * Based on:
11972  * Ext JS Library 1.1.1
11973  * Copyright(c) 2006-2007, Ext JS, LLC.
11974  *
11975  * Originally Released Under LGPL - original licence link has changed is not relivant.
11976  *
11977  * Fork - LGPL
11978  * <script type="text/javascript">
11979  */
11980
11981 /**
11982  * @class Roo.data.SimpleStore
11983  * @extends Roo.data.Store
11984  * Small helper class to make creating Stores from Array data easier.
11985  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11986  * @cfg {Array} fields An array of field definition objects, or field name strings.
11987  * @cfg {Array} data The multi-dimensional array of data
11988  * @constructor
11989  * @param {Object} config
11990  */
11991 Roo.data.SimpleStore = function(config){
11992     Roo.data.SimpleStore.superclass.constructor.call(this, {
11993         isLocal : true,
11994         reader: new Roo.data.ArrayReader({
11995                 id: config.id
11996             },
11997             Roo.data.Record.create(config.fields)
11998         ),
11999         proxy : new Roo.data.MemoryProxy(config.data)
12000     });
12001     this.load();
12002 };
12003 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12004  * Based on:
12005  * Ext JS Library 1.1.1
12006  * Copyright(c) 2006-2007, Ext JS, LLC.
12007  *
12008  * Originally Released Under LGPL - original licence link has changed is not relivant.
12009  *
12010  * Fork - LGPL
12011  * <script type="text/javascript">
12012  */
12013
12014 /**
12015 /**
12016  * @extends Roo.data.Store
12017  * @class Roo.data.JsonStore
12018  * Small helper class to make creating Stores for JSON data easier. <br/>
12019 <pre><code>
12020 var store = new Roo.data.JsonStore({
12021     url: 'get-images.php',
12022     root: 'images',
12023     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12024 });
12025 </code></pre>
12026  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12027  * JsonReader and HttpProxy (unless inline data is provided).</b>
12028  * @cfg {Array} fields An array of field definition objects, or field name strings.
12029  * @constructor
12030  * @param {Object} config
12031  */
12032 Roo.data.JsonStore = function(c){
12033     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12034         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12035         reader: new Roo.data.JsonReader(c, c.fields)
12036     }));
12037 };
12038 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12039  * Based on:
12040  * Ext JS Library 1.1.1
12041  * Copyright(c) 2006-2007, Ext JS, LLC.
12042  *
12043  * Originally Released Under LGPL - original licence link has changed is not relivant.
12044  *
12045  * Fork - LGPL
12046  * <script type="text/javascript">
12047  */
12048
12049  
12050 Roo.data.Field = function(config){
12051     if(typeof config == "string"){
12052         config = {name: config};
12053     }
12054     Roo.apply(this, config);
12055     
12056     if(!this.type){
12057         this.type = "auto";
12058     }
12059     
12060     var st = Roo.data.SortTypes;
12061     // named sortTypes are supported, here we look them up
12062     if(typeof this.sortType == "string"){
12063         this.sortType = st[this.sortType];
12064     }
12065     
12066     // set default sortType for strings and dates
12067     if(!this.sortType){
12068         switch(this.type){
12069             case "string":
12070                 this.sortType = st.asUCString;
12071                 break;
12072             case "date":
12073                 this.sortType = st.asDate;
12074                 break;
12075             default:
12076                 this.sortType = st.none;
12077         }
12078     }
12079
12080     // define once
12081     var stripRe = /[\$,%]/g;
12082
12083     // prebuilt conversion function for this field, instead of
12084     // switching every time we're reading a value
12085     if(!this.convert){
12086         var cv, dateFormat = this.dateFormat;
12087         switch(this.type){
12088             case "":
12089             case "auto":
12090             case undefined:
12091                 cv = function(v){ return v; };
12092                 break;
12093             case "string":
12094                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12095                 break;
12096             case "int":
12097                 cv = function(v){
12098                     return v !== undefined && v !== null && v !== '' ?
12099                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12100                     };
12101                 break;
12102             case "float":
12103                 cv = function(v){
12104                     return v !== undefined && v !== null && v !== '' ?
12105                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12106                     };
12107                 break;
12108             case "bool":
12109             case "boolean":
12110                 cv = function(v){ return v === true || v === "true" || v == 1; };
12111                 break;
12112             case "date":
12113                 cv = function(v){
12114                     if(!v){
12115                         return '';
12116                     }
12117                     if(v instanceof Date){
12118                         return v;
12119                     }
12120                     if(dateFormat){
12121                         if(dateFormat == "timestamp"){
12122                             return new Date(v*1000);
12123                         }
12124                         return Date.parseDate(v, dateFormat);
12125                     }
12126                     var parsed = Date.parse(v);
12127                     return parsed ? new Date(parsed) : null;
12128                 };
12129              break;
12130             
12131         }
12132         this.convert = cv;
12133     }
12134 };
12135
12136 Roo.data.Field.prototype = {
12137     dateFormat: null,
12138     defaultValue: "",
12139     mapping: null,
12140     sortType : null,
12141     sortDir : "ASC"
12142 };/*
12143  * Based on:
12144  * Ext JS Library 1.1.1
12145  * Copyright(c) 2006-2007, Ext JS, LLC.
12146  *
12147  * Originally Released Under LGPL - original licence link has changed is not relivant.
12148  *
12149  * Fork - LGPL
12150  * <script type="text/javascript">
12151  */
12152  
12153 // Base class for reading structured data from a data source.  This class is intended to be
12154 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12155
12156 /**
12157  * @class Roo.data.DataReader
12158  * Base class for reading structured data from a data source.  This class is intended to be
12159  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12160  */
12161
12162 Roo.data.DataReader = function(meta, recordType){
12163     
12164     this.meta = meta;
12165     
12166     this.recordType = recordType instanceof Array ? 
12167         Roo.data.Record.create(recordType) : recordType;
12168 };
12169
12170 Roo.data.DataReader.prototype = {
12171      /**
12172      * Create an empty record
12173      * @param {Object} data (optional) - overlay some values
12174      * @return {Roo.data.Record} record created.
12175      */
12176     newRow :  function(d) {
12177         var da =  {};
12178         this.recordType.prototype.fields.each(function(c) {
12179             switch( c.type) {
12180                 case 'int' : da[c.name] = 0; break;
12181                 case 'date' : da[c.name] = new Date(); break;
12182                 case 'float' : da[c.name] = 0.0; break;
12183                 case 'boolean' : da[c.name] = false; break;
12184                 default : da[c.name] = ""; break;
12185             }
12186             
12187         });
12188         return new this.recordType(Roo.apply(da, d));
12189     }
12190     
12191 };/*
12192  * Based on:
12193  * Ext JS Library 1.1.1
12194  * Copyright(c) 2006-2007, Ext JS, LLC.
12195  *
12196  * Originally Released Under LGPL - original licence link has changed is not relivant.
12197  *
12198  * Fork - LGPL
12199  * <script type="text/javascript">
12200  */
12201
12202 /**
12203  * @class Roo.data.DataProxy
12204  * @extends Roo.data.Observable
12205  * This class is an abstract base class for implementations which provide retrieval of
12206  * unformatted data objects.<br>
12207  * <p>
12208  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12209  * (of the appropriate type which knows how to parse the data object) to provide a block of
12210  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12211  * <p>
12212  * Custom implementations must implement the load method as described in
12213  * {@link Roo.data.HttpProxy#load}.
12214  */
12215 Roo.data.DataProxy = function(){
12216     this.addEvents({
12217         /**
12218          * @event beforeload
12219          * Fires before a network request is made to retrieve a data object.
12220          * @param {Object} This DataProxy object.
12221          * @param {Object} params The params parameter to the load function.
12222          */
12223         beforeload : true,
12224         /**
12225          * @event load
12226          * Fires before the load method's callback is called.
12227          * @param {Object} This DataProxy object.
12228          * @param {Object} o The data object.
12229          * @param {Object} arg The callback argument object passed to the load function.
12230          */
12231         load : true,
12232         /**
12233          * @event loadexception
12234          * Fires if an Exception occurs during data retrieval.
12235          * @param {Object} This DataProxy object.
12236          * @param {Object} o The data object.
12237          * @param {Object} arg The callback argument object passed to the load function.
12238          * @param {Object} e The Exception.
12239          */
12240         loadexception : true
12241     });
12242     Roo.data.DataProxy.superclass.constructor.call(this);
12243 };
12244
12245 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12246
12247     /**
12248      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12249      */
12250 /*
12251  * Based on:
12252  * Ext JS Library 1.1.1
12253  * Copyright(c) 2006-2007, Ext JS, LLC.
12254  *
12255  * Originally Released Under LGPL - original licence link has changed is not relivant.
12256  *
12257  * Fork - LGPL
12258  * <script type="text/javascript">
12259  */
12260 /**
12261  * @class Roo.data.MemoryProxy
12262  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12263  * to the Reader when its load method is called.
12264  * @constructor
12265  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12266  */
12267 Roo.data.MemoryProxy = function(data){
12268     if (data.data) {
12269         data = data.data;
12270     }
12271     Roo.data.MemoryProxy.superclass.constructor.call(this);
12272     this.data = data;
12273 };
12274
12275 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12276     
12277     /**
12278      * Load data from the requested source (in this case an in-memory
12279      * data object passed to the constructor), read the data object into
12280      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12281      * process that block using the passed callback.
12282      * @param {Object} params This parameter is not used by the MemoryProxy class.
12283      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12284      * object into a block of Roo.data.Records.
12285      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12286      * The function must be passed <ul>
12287      * <li>The Record block object</li>
12288      * <li>The "arg" argument from the load function</li>
12289      * <li>A boolean success indicator</li>
12290      * </ul>
12291      * @param {Object} scope The scope in which to call the callback
12292      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12293      */
12294     load : function(params, reader, callback, scope, arg){
12295         params = params || {};
12296         var result;
12297         try {
12298             result = reader.readRecords(this.data);
12299         }catch(e){
12300             this.fireEvent("loadexception", this, arg, null, e);
12301             callback.call(scope, null, arg, false);
12302             return;
12303         }
12304         callback.call(scope, result, arg, true);
12305     },
12306     
12307     // private
12308     update : function(params, records){
12309         
12310     }
12311 });/*
12312  * Based on:
12313  * Ext JS Library 1.1.1
12314  * Copyright(c) 2006-2007, Ext JS, LLC.
12315  *
12316  * Originally Released Under LGPL - original licence link has changed is not relivant.
12317  *
12318  * Fork - LGPL
12319  * <script type="text/javascript">
12320  */
12321 /**
12322  * @class Roo.data.HttpProxy
12323  * @extends Roo.data.DataProxy
12324  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12325  * configured to reference a certain URL.<br><br>
12326  * <p>
12327  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12328  * from which the running page was served.<br><br>
12329  * <p>
12330  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12331  * <p>
12332  * Be aware that to enable the browser to parse an XML document, the server must set
12333  * the Content-Type header in the HTTP response to "text/xml".
12334  * @constructor
12335  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12336  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12337  * will be used to make the request.
12338  */
12339 Roo.data.HttpProxy = function(conn){
12340     Roo.data.HttpProxy.superclass.constructor.call(this);
12341     // is conn a conn config or a real conn?
12342     this.conn = conn;
12343     this.useAjax = !conn || !conn.events;
12344   
12345 };
12346
12347 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12348     // thse are take from connection...
12349     
12350     /**
12351      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12352      */
12353     /**
12354      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12355      * extra parameters to each request made by this object. (defaults to undefined)
12356      */
12357     /**
12358      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12359      *  to each request made by this object. (defaults to undefined)
12360      */
12361     /**
12362      * @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)
12363      */
12364     /**
12365      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12366      */
12367      /**
12368      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12369      * @type Boolean
12370      */
12371   
12372
12373     /**
12374      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12375      * @type Boolean
12376      */
12377     /**
12378      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12379      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12380      * a finer-grained basis than the DataProxy events.
12381      */
12382     getConnection : function(){
12383         return this.useAjax ? Roo.Ajax : this.conn;
12384     },
12385
12386     /**
12387      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12388      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12389      * process that block using the passed callback.
12390      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12391      * for the request to the remote server.
12392      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12393      * object into a block of Roo.data.Records.
12394      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12395      * The function must be passed <ul>
12396      * <li>The Record block object</li>
12397      * <li>The "arg" argument from the load function</li>
12398      * <li>A boolean success indicator</li>
12399      * </ul>
12400      * @param {Object} scope The scope in which to call the callback
12401      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12402      */
12403     load : function(params, reader, callback, scope, arg){
12404         if(this.fireEvent("beforeload", this, params) !== false){
12405             var  o = {
12406                 params : params || {},
12407                 request: {
12408                     callback : callback,
12409                     scope : scope,
12410                     arg : arg
12411                 },
12412                 reader: reader,
12413                 callback : this.loadResponse,
12414                 scope: this
12415             };
12416             if(this.useAjax){
12417                 Roo.applyIf(o, this.conn);
12418                 if(this.activeRequest){
12419                     Roo.Ajax.abort(this.activeRequest);
12420                 }
12421                 this.activeRequest = Roo.Ajax.request(o);
12422             }else{
12423                 this.conn.request(o);
12424             }
12425         }else{
12426             callback.call(scope||this, null, arg, false);
12427         }
12428     },
12429
12430     // private
12431     loadResponse : function(o, success, response){
12432         delete this.activeRequest;
12433         if(!success){
12434             this.fireEvent("loadexception", this, o, response);
12435             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12436             return;
12437         }
12438         var result;
12439         try {
12440             result = o.reader.read(response);
12441         }catch(e){
12442             this.fireEvent("loadexception", this, o, response, e);
12443             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12444             return;
12445         }
12446         
12447         this.fireEvent("load", this, o, o.request.arg);
12448         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12449     },
12450
12451     // private
12452     update : function(dataSet){
12453
12454     },
12455
12456     // private
12457     updateResponse : function(dataSet){
12458
12459     }
12460 });/*
12461  * Based on:
12462  * Ext JS Library 1.1.1
12463  * Copyright(c) 2006-2007, Ext JS, LLC.
12464  *
12465  * Originally Released Under LGPL - original licence link has changed is not relivant.
12466  *
12467  * Fork - LGPL
12468  * <script type="text/javascript">
12469  */
12470
12471 /**
12472  * @class Roo.data.ScriptTagProxy
12473  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12474  * other than the originating domain of the running page.<br><br>
12475  * <p>
12476  * <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
12477  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12478  * <p>
12479  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12480  * source code that is used as the source inside a &lt;script> tag.<br><br>
12481  * <p>
12482  * In order for the browser to process the returned data, the server must wrap the data object
12483  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12484  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12485  * depending on whether the callback name was passed:
12486  * <p>
12487  * <pre><code>
12488 boolean scriptTag = false;
12489 String cb = request.getParameter("callback");
12490 if (cb != null) {
12491     scriptTag = true;
12492     response.setContentType("text/javascript");
12493 } else {
12494     response.setContentType("application/x-json");
12495 }
12496 Writer out = response.getWriter();
12497 if (scriptTag) {
12498     out.write(cb + "(");
12499 }
12500 out.print(dataBlock.toJsonString());
12501 if (scriptTag) {
12502     out.write(");");
12503 }
12504 </pre></code>
12505  *
12506  * @constructor
12507  * @param {Object} config A configuration object.
12508  */
12509 Roo.data.ScriptTagProxy = function(config){
12510     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12511     Roo.apply(this, config);
12512     this.head = document.getElementsByTagName("head")[0];
12513 };
12514
12515 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12516
12517 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12518     /**
12519      * @cfg {String} url The URL from which to request the data object.
12520      */
12521     /**
12522      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12523      */
12524     timeout : 30000,
12525     /**
12526      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12527      * the server the name of the callback function set up by the load call to process the returned data object.
12528      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12529      * javascript output which calls this named function passing the data object as its only parameter.
12530      */
12531     callbackParam : "callback",
12532     /**
12533      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12534      * name to the request.
12535      */
12536     nocache : true,
12537
12538     /**
12539      * Load data from the configured URL, read the data object into
12540      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12541      * process that block using the passed callback.
12542      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12543      * for the request to the remote server.
12544      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12545      * object into a block of Roo.data.Records.
12546      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12547      * The function must be passed <ul>
12548      * <li>The Record block object</li>
12549      * <li>The "arg" argument from the load function</li>
12550      * <li>A boolean success indicator</li>
12551      * </ul>
12552      * @param {Object} scope The scope in which to call the callback
12553      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12554      */
12555     load : function(params, reader, callback, scope, arg){
12556         if(this.fireEvent("beforeload", this, params) !== false){
12557
12558             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12559
12560             var url = this.url;
12561             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12562             if(this.nocache){
12563                 url += "&_dc=" + (new Date().getTime());
12564             }
12565             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12566             var trans = {
12567                 id : transId,
12568                 cb : "stcCallback"+transId,
12569                 scriptId : "stcScript"+transId,
12570                 params : params,
12571                 arg : arg,
12572                 url : url,
12573                 callback : callback,
12574                 scope : scope,
12575                 reader : reader
12576             };
12577             var conn = this;
12578
12579             window[trans.cb] = function(o){
12580                 conn.handleResponse(o, trans);
12581             };
12582
12583             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12584
12585             if(this.autoAbort !== false){
12586                 this.abort();
12587             }
12588
12589             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12590
12591             var script = document.createElement("script");
12592             script.setAttribute("src", url);
12593             script.setAttribute("type", "text/javascript");
12594             script.setAttribute("id", trans.scriptId);
12595             this.head.appendChild(script);
12596
12597             this.trans = trans;
12598         }else{
12599             callback.call(scope||this, null, arg, false);
12600         }
12601     },
12602
12603     // private
12604     isLoading : function(){
12605         return this.trans ? true : false;
12606     },
12607
12608     /**
12609      * Abort the current server request.
12610      */
12611     abort : function(){
12612         if(this.isLoading()){
12613             this.destroyTrans(this.trans);
12614         }
12615     },
12616
12617     // private
12618     destroyTrans : function(trans, isLoaded){
12619         this.head.removeChild(document.getElementById(trans.scriptId));
12620         clearTimeout(trans.timeoutId);
12621         if(isLoaded){
12622             window[trans.cb] = undefined;
12623             try{
12624                 delete window[trans.cb];
12625             }catch(e){}
12626         }else{
12627             // if hasn't been loaded, wait for load to remove it to prevent script error
12628             window[trans.cb] = function(){
12629                 window[trans.cb] = undefined;
12630                 try{
12631                     delete window[trans.cb];
12632                 }catch(e){}
12633             };
12634         }
12635     },
12636
12637     // private
12638     handleResponse : function(o, trans){
12639         this.trans = false;
12640         this.destroyTrans(trans, true);
12641         var result;
12642         try {
12643             result = trans.reader.readRecords(o);
12644         }catch(e){
12645             this.fireEvent("loadexception", this, o, trans.arg, e);
12646             trans.callback.call(trans.scope||window, null, trans.arg, false);
12647             return;
12648         }
12649         this.fireEvent("load", this, o, trans.arg);
12650         trans.callback.call(trans.scope||window, result, trans.arg, true);
12651     },
12652
12653     // private
12654     handleFailure : function(trans){
12655         this.trans = false;
12656         this.destroyTrans(trans, false);
12657         this.fireEvent("loadexception", this, null, trans.arg);
12658         trans.callback.call(trans.scope||window, null, trans.arg, false);
12659     }
12660 });/*
12661  * Based on:
12662  * Ext JS Library 1.1.1
12663  * Copyright(c) 2006-2007, Ext JS, LLC.
12664  *
12665  * Originally Released Under LGPL - original licence link has changed is not relivant.
12666  *
12667  * Fork - LGPL
12668  * <script type="text/javascript">
12669  */
12670
12671 /**
12672  * @class Roo.data.JsonReader
12673  * @extends Roo.data.DataReader
12674  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12675  * based on mappings in a provided Roo.data.Record constructor.
12676  * 
12677  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12678  * in the reply previously. 
12679  * 
12680  * <p>
12681  * Example code:
12682  * <pre><code>
12683 var RecordDef = Roo.data.Record.create([
12684     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12685     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12686 ]);
12687 var myReader = new Roo.data.JsonReader({
12688     totalProperty: "results",    // The property which contains the total dataset size (optional)
12689     root: "rows",                // The property which contains an Array of row objects
12690     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12691 }, RecordDef);
12692 </code></pre>
12693  * <p>
12694  * This would consume a JSON file like this:
12695  * <pre><code>
12696 { 'results': 2, 'rows': [
12697     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12698     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12699 }
12700 </code></pre>
12701  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12702  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12703  * paged from the remote server.
12704  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12705  * @cfg {String} root name of the property which contains the Array of row objects.
12706  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12707  * @cfg {Array} fields Array of field definition objects
12708  * @constructor
12709  * Create a new JsonReader
12710  * @param {Object} meta Metadata configuration options
12711  * @param {Object} recordType Either an Array of field definition objects,
12712  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12713  */
12714 Roo.data.JsonReader = function(meta, recordType){
12715     
12716     meta = meta || {};
12717     // set some defaults:
12718     Roo.applyIf(meta, {
12719         totalProperty: 'total',
12720         successProperty : 'success',
12721         root : 'data',
12722         id : 'id'
12723     });
12724     
12725     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12726 };
12727 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12728     
12729     /**
12730      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12731      * Used by Store query builder to append _requestMeta to params.
12732      * 
12733      */
12734     metaFromRemote : false,
12735     /**
12736      * This method is only used by a DataProxy which has retrieved data from a remote server.
12737      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12738      * @return {Object} data A data block which is used by an Roo.data.Store object as
12739      * a cache of Roo.data.Records.
12740      */
12741     read : function(response){
12742         var json = response.responseText;
12743        
12744         var o = /* eval:var:o */ eval("("+json+")");
12745         if(!o) {
12746             throw {message: "JsonReader.read: Json object not found"};
12747         }
12748         
12749         if(o.metaData){
12750             
12751             delete this.ef;
12752             this.metaFromRemote = true;
12753             this.meta = o.metaData;
12754             this.recordType = Roo.data.Record.create(o.metaData.fields);
12755             this.onMetaChange(this.meta, this.recordType, o);
12756         }
12757         return this.readRecords(o);
12758     },
12759
12760     // private function a store will implement
12761     onMetaChange : function(meta, recordType, o){
12762
12763     },
12764
12765     /**
12766          * @ignore
12767          */
12768     simpleAccess: function(obj, subsc) {
12769         return obj[subsc];
12770     },
12771
12772         /**
12773          * @ignore
12774          */
12775     getJsonAccessor: function(){
12776         var re = /[\[\.]/;
12777         return function(expr) {
12778             try {
12779                 return(re.test(expr))
12780                     ? new Function("obj", "return obj." + expr)
12781                     : function(obj){
12782                         return obj[expr];
12783                     };
12784             } catch(e){}
12785             return Roo.emptyFn;
12786         };
12787     }(),
12788
12789     /**
12790      * Create a data block containing Roo.data.Records from an XML document.
12791      * @param {Object} o An object which contains an Array of row objects in the property specified
12792      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12793      * which contains the total size of the dataset.
12794      * @return {Object} data A data block which is used by an Roo.data.Store object as
12795      * a cache of Roo.data.Records.
12796      */
12797     readRecords : function(o){
12798         /**
12799          * After any data loads, the raw JSON data is available for further custom processing.
12800          * @type Object
12801          */
12802         this.o = o;
12803         var s = this.meta, Record = this.recordType,
12804             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12805
12806 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12807         if (!this.ef) {
12808             if(s.totalProperty) {
12809                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12810                 }
12811                 if(s.successProperty) {
12812                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12813                 }
12814                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12815                 if (s.id) {
12816                         var g = this.getJsonAccessor(s.id);
12817                         this.getId = function(rec) {
12818                                 var r = g(rec);  
12819                                 return (r === undefined || r === "") ? null : r;
12820                         };
12821                 } else {
12822                         this.getId = function(){return null;};
12823                 }
12824             this.ef = [];
12825             for(var jj = 0; jj < fl; jj++){
12826                 f = fi[jj];
12827                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12828                 this.ef[jj] = this.getJsonAccessor(map);
12829             }
12830         }
12831
12832         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12833         if(s.totalProperty){
12834             var vt = parseInt(this.getTotal(o), 10);
12835             if(!isNaN(vt)){
12836                 totalRecords = vt;
12837             }
12838         }
12839         if(s.successProperty){
12840             var vs = this.getSuccess(o);
12841             if(vs === false || vs === 'false'){
12842                 success = false;
12843             }
12844         }
12845         var records = [];
12846         for(var i = 0; i < c; i++){
12847                 var n = root[i];
12848             var values = {};
12849             var id = this.getId(n);
12850             for(var j = 0; j < fl; j++){
12851                 f = fi[j];
12852             var v = this.ef[j](n);
12853             if (!f.convert) {
12854                 Roo.log('missing convert for ' + f.name);
12855                 Roo.log(f);
12856                 continue;
12857             }
12858             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12859             }
12860             var record = new Record(values, id);
12861             record.json = n;
12862             records[i] = record;
12863         }
12864         return {
12865             raw : o,
12866             success : success,
12867             records : records,
12868             totalRecords : totalRecords
12869         };
12870     }
12871 });/*
12872  * Based on:
12873  * Ext JS Library 1.1.1
12874  * Copyright(c) 2006-2007, Ext JS, LLC.
12875  *
12876  * Originally Released Under LGPL - original licence link has changed is not relivant.
12877  *
12878  * Fork - LGPL
12879  * <script type="text/javascript">
12880  */
12881
12882 /**
12883  * @class Roo.data.ArrayReader
12884  * @extends Roo.data.DataReader
12885  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12886  * Each element of that Array represents a row of data fields. The
12887  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12888  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12889  * <p>
12890  * Example code:.
12891  * <pre><code>
12892 var RecordDef = Roo.data.Record.create([
12893     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12894     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12895 ]);
12896 var myReader = new Roo.data.ArrayReader({
12897     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12898 }, RecordDef);
12899 </code></pre>
12900  * <p>
12901  * This would consume an Array like this:
12902  * <pre><code>
12903 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12904   </code></pre>
12905  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12906  * @constructor
12907  * Create a new JsonReader
12908  * @param {Object} meta Metadata configuration options.
12909  * @param {Object} recordType Either an Array of field definition objects
12910  * as specified to {@link Roo.data.Record#create},
12911  * or an {@link Roo.data.Record} object
12912  * created using {@link Roo.data.Record#create}.
12913  */
12914 Roo.data.ArrayReader = function(meta, recordType){
12915     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12916 };
12917
12918 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12919     /**
12920      * Create a data block containing Roo.data.Records from an XML document.
12921      * @param {Object} o An Array of row objects which represents the dataset.
12922      * @return {Object} data A data block which is used by an Roo.data.Store object as
12923      * a cache of Roo.data.Records.
12924      */
12925     readRecords : function(o){
12926         var sid = this.meta ? this.meta.id : null;
12927         var recordType = this.recordType, fields = recordType.prototype.fields;
12928         var records = [];
12929         var root = o;
12930             for(var i = 0; i < root.length; i++){
12931                     var n = root[i];
12932                 var values = {};
12933                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12934                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12935                 var f = fields.items[j];
12936                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12937                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12938                 v = f.convert(v);
12939                 values[f.name] = v;
12940             }
12941                 var record = new recordType(values, id);
12942                 record.json = n;
12943                 records[records.length] = record;
12944             }
12945             return {
12946                 records : records,
12947                 totalRecords : records.length
12948             };
12949     }
12950 });/*
12951  * - LGPL
12952  * * 
12953  */
12954
12955 /**
12956  * @class Roo.bootstrap.ComboBox
12957  * @extends Roo.bootstrap.TriggerField
12958  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12959  * @cfg {Boolean} append (true|false) default false
12960  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12961  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12962  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12963  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12964  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12965  * @cfg {Boolean} animate default true
12966  * @cfg {Boolean} emptyResultText only for touch device
12967  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12968  * @cfg {String} emptyTitle default ''
12969  * @constructor
12970  * Create a new ComboBox.
12971  * @param {Object} config Configuration options
12972  */
12973 Roo.bootstrap.ComboBox = function(config){
12974     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12975     this.addEvents({
12976         /**
12977          * @event expand
12978          * Fires when the dropdown list is expanded
12979         * @param {Roo.bootstrap.ComboBox} combo This combo box
12980         */
12981         'expand' : true,
12982         /**
12983          * @event collapse
12984          * Fires when the dropdown list is collapsed
12985         * @param {Roo.bootstrap.ComboBox} combo This combo box
12986         */
12987         'collapse' : true,
12988         /**
12989          * @event beforeselect
12990          * Fires before a list item is selected. Return false to cancel the selection.
12991         * @param {Roo.bootstrap.ComboBox} combo This combo box
12992         * @param {Roo.data.Record} record The data record returned from the underlying store
12993         * @param {Number} index The index of the selected item in the dropdown list
12994         */
12995         'beforeselect' : true,
12996         /**
12997          * @event select
12998          * Fires when a list item is selected
12999         * @param {Roo.bootstrap.ComboBox} combo This combo box
13000         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13001         * @param {Number} index The index of the selected item in the dropdown list
13002         */
13003         'select' : true,
13004         /**
13005          * @event beforequery
13006          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13007          * The event object passed has these properties:
13008         * @param {Roo.bootstrap.ComboBox} combo This combo box
13009         * @param {String} query The query
13010         * @param {Boolean} forceAll true to force "all" query
13011         * @param {Boolean} cancel true to cancel the query
13012         * @param {Object} e The query event object
13013         */
13014         'beforequery': true,
13015          /**
13016          * @event add
13017          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13018         * @param {Roo.bootstrap.ComboBox} combo This combo box
13019         */
13020         'add' : true,
13021         /**
13022          * @event edit
13023          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13024         * @param {Roo.bootstrap.ComboBox} combo This combo box
13025         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13026         */
13027         'edit' : true,
13028         /**
13029          * @event remove
13030          * Fires when the remove value from the combobox array
13031         * @param {Roo.bootstrap.ComboBox} combo This combo box
13032         */
13033         'remove' : true,
13034         /**
13035          * @event afterremove
13036          * Fires when the remove value from the combobox array
13037         * @param {Roo.bootstrap.ComboBox} combo This combo box
13038         */
13039         'afterremove' : true,
13040         /**
13041          * @event specialfilter
13042          * Fires when specialfilter
13043             * @param {Roo.bootstrap.ComboBox} combo This combo box
13044             */
13045         'specialfilter' : true,
13046         /**
13047          * @event tick
13048          * Fires when tick the element
13049             * @param {Roo.bootstrap.ComboBox} combo This combo box
13050             */
13051         'tick' : true,
13052         /**
13053          * @event touchviewdisplay
13054          * Fires when touch view require special display (default is using displayField)
13055             * @param {Roo.bootstrap.ComboBox} combo This combo box
13056             * @param {Object} cfg set html .
13057             */
13058         'touchviewdisplay' : true
13059         
13060     });
13061     
13062     this.item = [];
13063     this.tickItems = [];
13064     
13065     this.selectedIndex = -1;
13066     if(this.mode == 'local'){
13067         if(config.queryDelay === undefined){
13068             this.queryDelay = 10;
13069         }
13070         if(config.minChars === undefined){
13071             this.minChars = 0;
13072         }
13073     }
13074 };
13075
13076 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13077      
13078     /**
13079      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13080      * rendering into an Roo.Editor, defaults to false)
13081      */
13082     /**
13083      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13084      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13085      */
13086     /**
13087      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13088      */
13089     /**
13090      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13091      * the dropdown list (defaults to undefined, with no header element)
13092      */
13093
13094      /**
13095      * @cfg {String/Roo.Template} tpl The template to use to render the output
13096      */
13097      
13098      /**
13099      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13100      */
13101     listWidth: undefined,
13102     /**
13103      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13104      * mode = 'remote' or 'text' if mode = 'local')
13105      */
13106     displayField: undefined,
13107     
13108     /**
13109      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13110      * mode = 'remote' or 'value' if mode = 'local'). 
13111      * Note: use of a valueField requires the user make a selection
13112      * in order for a value to be mapped.
13113      */
13114     valueField: undefined,
13115     /**
13116      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13117      */
13118     modalTitle : '',
13119     
13120     /**
13121      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13122      * field's data value (defaults to the underlying DOM element's name)
13123      */
13124     hiddenName: undefined,
13125     /**
13126      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13127      */
13128     listClass: '',
13129     /**
13130      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13131      */
13132     selectedClass: 'active',
13133     
13134     /**
13135      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13136      */
13137     shadow:'sides',
13138     /**
13139      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13140      * anchor positions (defaults to 'tl-bl')
13141      */
13142     listAlign: 'tl-bl?',
13143     /**
13144      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13145      */
13146     maxHeight: 300,
13147     /**
13148      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13149      * query specified by the allQuery config option (defaults to 'query')
13150      */
13151     triggerAction: 'query',
13152     /**
13153      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13154      * (defaults to 4, does not apply if editable = false)
13155      */
13156     minChars : 4,
13157     /**
13158      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13159      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13160      */
13161     typeAhead: false,
13162     /**
13163      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13164      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13165      */
13166     queryDelay: 500,
13167     /**
13168      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13169      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13170      */
13171     pageSize: 0,
13172     /**
13173      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13174      * when editable = true (defaults to false)
13175      */
13176     selectOnFocus:false,
13177     /**
13178      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13179      */
13180     queryParam: 'query',
13181     /**
13182      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13183      * when mode = 'remote' (defaults to 'Loading...')
13184      */
13185     loadingText: 'Loading...',
13186     /**
13187      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13188      */
13189     resizable: false,
13190     /**
13191      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13192      */
13193     handleHeight : 8,
13194     /**
13195      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13196      * traditional select (defaults to true)
13197      */
13198     editable: true,
13199     /**
13200      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13201      */
13202     allQuery: '',
13203     /**
13204      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13205      */
13206     mode: 'remote',
13207     /**
13208      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13209      * listWidth has a higher value)
13210      */
13211     minListWidth : 70,
13212     /**
13213      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13214      * allow the user to set arbitrary text into the field (defaults to false)
13215      */
13216     forceSelection:false,
13217     /**
13218      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13219      * if typeAhead = true (defaults to 250)
13220      */
13221     typeAheadDelay : 250,
13222     /**
13223      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13224      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13225      */
13226     valueNotFoundText : undefined,
13227     /**
13228      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13229      */
13230     blockFocus : false,
13231     
13232     /**
13233      * @cfg {Boolean} disableClear Disable showing of clear button.
13234      */
13235     disableClear : false,
13236     /**
13237      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13238      */
13239     alwaysQuery : false,
13240     
13241     /**
13242      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13243      */
13244     multiple : false,
13245     
13246     /**
13247      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13248      */
13249     invalidClass : "has-warning",
13250     
13251     /**
13252      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13253      */
13254     validClass : "has-success",
13255     
13256     /**
13257      * @cfg {Boolean} specialFilter (true|false) special filter default false
13258      */
13259     specialFilter : false,
13260     
13261     /**
13262      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13263      */
13264     mobileTouchView : true,
13265     
13266     /**
13267      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13268      */
13269     useNativeIOS : false,
13270     
13271     /**
13272      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13273      */
13274     mobile_restrict_height : false,
13275     
13276     ios_options : false,
13277     
13278     //private
13279     addicon : false,
13280     editicon: false,
13281     
13282     page: 0,
13283     hasQuery: false,
13284     append: false,
13285     loadNext: false,
13286     autoFocus : true,
13287     tickable : false,
13288     btnPosition : 'right',
13289     triggerList : true,
13290     showToggleBtn : true,
13291     animate : true,
13292     emptyResultText: 'Empty',
13293     triggerText : 'Select',
13294     emptyTitle : '',
13295     
13296     // element that contains real text value.. (when hidden is used..)
13297     
13298     getAutoCreate : function()
13299     {   
13300         var cfg = false;
13301         //render
13302         /*
13303          * Render classic select for iso
13304          */
13305         
13306         if(Roo.isIOS && this.useNativeIOS){
13307             cfg = this.getAutoCreateNativeIOS();
13308             return cfg;
13309         }
13310         
13311         /*
13312          * Touch Devices
13313          */
13314         
13315         if(Roo.isTouch && this.mobileTouchView){
13316             cfg = this.getAutoCreateTouchView();
13317             return cfg;;
13318         }
13319         
13320         /*
13321          *  Normal ComboBox
13322          */
13323         if(!this.tickable){
13324             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13325             return cfg;
13326         }
13327         
13328         /*
13329          *  ComboBox with tickable selections
13330          */
13331              
13332         var align = this.labelAlign || this.parentLabelAlign();
13333         
13334         cfg = {
13335             cls : 'form-group roo-combobox-tickable' //input-group
13336         };
13337         
13338         var btn_text_select = '';
13339         var btn_text_done = '';
13340         var btn_text_cancel = '';
13341         
13342         if (this.btn_text_show) {
13343             btn_text_select = 'Select';
13344             btn_text_done = 'Done';
13345             btn_text_cancel = 'Cancel'; 
13346         }
13347         
13348         var buttons = {
13349             tag : 'div',
13350             cls : 'tickable-buttons',
13351             cn : [
13352                 {
13353                     tag : 'button',
13354                     type : 'button',
13355                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13356                     //html : this.triggerText
13357                     html: btn_text_select
13358                 },
13359                 {
13360                     tag : 'button',
13361                     type : 'button',
13362                     name : 'ok',
13363                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13364                     //html : 'Done'
13365                     html: btn_text_done
13366                 },
13367                 {
13368                     tag : 'button',
13369                     type : 'button',
13370                     name : 'cancel',
13371                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13372                     //html : 'Cancel'
13373                     html: btn_text_cancel
13374                 }
13375             ]
13376         };
13377         
13378         if(this.editable){
13379             buttons.cn.unshift({
13380                 tag: 'input',
13381                 cls: 'roo-select2-search-field-input'
13382             });
13383         }
13384         
13385         var _this = this;
13386         
13387         Roo.each(buttons.cn, function(c){
13388             if (_this.size) {
13389                 c.cls += ' btn-' + _this.size;
13390             }
13391
13392             if (_this.disabled) {
13393                 c.disabled = true;
13394             }
13395         });
13396         
13397         var box = {
13398             tag: 'div',
13399             style : 'display: contents',
13400             cn: [
13401                 {
13402                     tag: 'input',
13403                     type : 'hidden',
13404                     cls: 'form-hidden-field'
13405                 },
13406                 {
13407                     tag: 'ul',
13408                     cls: 'roo-select2-choices',
13409                     cn:[
13410                         {
13411                             tag: 'li',
13412                             cls: 'roo-select2-search-field',
13413                             cn: [
13414                                 buttons
13415                             ]
13416                         }
13417                     ]
13418                 }
13419             ]
13420         };
13421         
13422         var combobox = {
13423             cls: 'roo-select2-container input-group roo-select2-container-multi',
13424             cn: [
13425                 
13426                 box
13427 //                {
13428 //                    tag: 'ul',
13429 //                    cls: 'typeahead typeahead-long dropdown-menu',
13430 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13431 //                }
13432             ]
13433         };
13434         
13435         if(this.hasFeedback && !this.allowBlank){
13436             
13437             var feedback = {
13438                 tag: 'span',
13439                 cls: 'glyphicon form-control-feedback'
13440             };
13441
13442             combobox.cn.push(feedback);
13443         }
13444         
13445         var indicator = {
13446             tag : 'i',
13447             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13448             tooltip : 'This field is required'
13449         };
13450         if (Roo.bootstrap.version == 4) {
13451             indicator = {
13452                 tag : 'i',
13453                 style : 'display:none'
13454             };
13455         }
13456         if (align ==='left' && this.fieldLabel.length) {
13457             
13458             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13459             
13460             cfg.cn = [
13461                 indicator,
13462                 {
13463                     tag: 'label',
13464                     'for' :  id,
13465                     cls : 'control-label col-form-label',
13466                     html : this.fieldLabel
13467
13468                 },
13469                 {
13470                     cls : "", 
13471                     cn: [
13472                         combobox
13473                     ]
13474                 }
13475
13476             ];
13477             
13478             var labelCfg = cfg.cn[1];
13479             var contentCfg = cfg.cn[2];
13480             
13481
13482             if(this.indicatorpos == 'right'){
13483                 
13484                 cfg.cn = [
13485                     {
13486                         tag: 'label',
13487                         'for' :  id,
13488                         cls : 'control-label col-form-label',
13489                         cn : [
13490                             {
13491                                 tag : 'span',
13492                                 html : this.fieldLabel
13493                             },
13494                             indicator
13495                         ]
13496                     },
13497                     {
13498                         cls : "",
13499                         cn: [
13500                             combobox
13501                         ]
13502                     }
13503
13504                 ];
13505                 
13506                 
13507                 
13508                 labelCfg = cfg.cn[0];
13509                 contentCfg = cfg.cn[1];
13510             
13511             }
13512             
13513             if(this.labelWidth > 12){
13514                 labelCfg.style = "width: " + this.labelWidth + 'px';
13515             }
13516             
13517             if(this.labelWidth < 13 && this.labelmd == 0){
13518                 this.labelmd = this.labelWidth;
13519             }
13520             
13521             if(this.labellg > 0){
13522                 labelCfg.cls += ' col-lg-' + this.labellg;
13523                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13524             }
13525             
13526             if(this.labelmd > 0){
13527                 labelCfg.cls += ' col-md-' + this.labelmd;
13528                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13529             }
13530             
13531             if(this.labelsm > 0){
13532                 labelCfg.cls += ' col-sm-' + this.labelsm;
13533                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13534             }
13535             
13536             if(this.labelxs > 0){
13537                 labelCfg.cls += ' col-xs-' + this.labelxs;
13538                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13539             }
13540                 
13541                 
13542         } else if ( this.fieldLabel.length) {
13543 //                Roo.log(" label");
13544                  cfg.cn = [
13545                    indicator,
13546                     {
13547                         tag: 'label',
13548                         //cls : 'input-group-addon',
13549                         html : this.fieldLabel
13550                     },
13551                     combobox
13552                 ];
13553                 
13554                 if(this.indicatorpos == 'right'){
13555                     cfg.cn = [
13556                         {
13557                             tag: 'label',
13558                             //cls : 'input-group-addon',
13559                             html : this.fieldLabel
13560                         },
13561                         indicator,
13562                         combobox
13563                     ];
13564                     
13565                 }
13566
13567         } else {
13568             
13569 //                Roo.log(" no label && no align");
13570                 cfg = combobox
13571                      
13572                 
13573         }
13574          
13575         var settings=this;
13576         ['xs','sm','md','lg'].map(function(size){
13577             if (settings[size]) {
13578                 cfg.cls += ' col-' + size + '-' + settings[size];
13579             }
13580         });
13581         
13582         return cfg;
13583         
13584     },
13585     
13586     _initEventsCalled : false,
13587     
13588     // private
13589     initEvents: function()
13590     {   
13591         if (this._initEventsCalled) { // as we call render... prevent looping...
13592             return;
13593         }
13594         this._initEventsCalled = true;
13595         
13596         if (!this.store) {
13597             throw "can not find store for combo";
13598         }
13599         
13600         this.indicator = this.indicatorEl();
13601         
13602         this.store = Roo.factory(this.store, Roo.data);
13603         this.store.parent = this;
13604         
13605         // if we are building from html. then this element is so complex, that we can not really
13606         // use the rendered HTML.
13607         // so we have to trash and replace the previous code.
13608         if (Roo.XComponent.build_from_html) {
13609             // remove this element....
13610             var e = this.el.dom, k=0;
13611             while (e ) { e = e.previousSibling;  ++k;}
13612
13613             this.el.remove();
13614             
13615             this.el=false;
13616             this.rendered = false;
13617             
13618             this.render(this.parent().getChildContainer(true), k);
13619         }
13620         
13621         if(Roo.isIOS && this.useNativeIOS){
13622             this.initIOSView();
13623             return;
13624         }
13625         
13626         /*
13627          * Touch Devices
13628          */
13629         
13630         if(Roo.isTouch && this.mobileTouchView){
13631             this.initTouchView();
13632             return;
13633         }
13634         
13635         if(this.tickable){
13636             this.initTickableEvents();
13637             return;
13638         }
13639         
13640         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13641         
13642         if(this.hiddenName){
13643             
13644             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13645             
13646             this.hiddenField.dom.value =
13647                 this.hiddenValue !== undefined ? this.hiddenValue :
13648                 this.value !== undefined ? this.value : '';
13649
13650             // prevent input submission
13651             this.el.dom.removeAttribute('name');
13652             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13653              
13654              
13655         }
13656         //if(Roo.isGecko){
13657         //    this.el.dom.setAttribute('autocomplete', 'off');
13658         //}
13659         
13660         var cls = 'x-combo-list';
13661         
13662         //this.list = new Roo.Layer({
13663         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13664         //});
13665         
13666         var _this = this;
13667         
13668         (function(){
13669             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13670             _this.list.setWidth(lw);
13671         }).defer(100);
13672         
13673         this.list.on('mouseover', this.onViewOver, this);
13674         this.list.on('mousemove', this.onViewMove, this);
13675         this.list.on('scroll', this.onViewScroll, this);
13676         
13677         /*
13678         this.list.swallowEvent('mousewheel');
13679         this.assetHeight = 0;
13680
13681         if(this.title){
13682             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13683             this.assetHeight += this.header.getHeight();
13684         }
13685
13686         this.innerList = this.list.createChild({cls:cls+'-inner'});
13687         this.innerList.on('mouseover', this.onViewOver, this);
13688         this.innerList.on('mousemove', this.onViewMove, this);
13689         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13690         
13691         if(this.allowBlank && !this.pageSize && !this.disableClear){
13692             this.footer = this.list.createChild({cls:cls+'-ft'});
13693             this.pageTb = new Roo.Toolbar(this.footer);
13694            
13695         }
13696         if(this.pageSize){
13697             this.footer = this.list.createChild({cls:cls+'-ft'});
13698             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13699                     {pageSize: this.pageSize});
13700             
13701         }
13702         
13703         if (this.pageTb && this.allowBlank && !this.disableClear) {
13704             var _this = this;
13705             this.pageTb.add(new Roo.Toolbar.Fill(), {
13706                 cls: 'x-btn-icon x-btn-clear',
13707                 text: '&#160;',
13708                 handler: function()
13709                 {
13710                     _this.collapse();
13711                     _this.clearValue();
13712                     _this.onSelect(false, -1);
13713                 }
13714             });
13715         }
13716         if (this.footer) {
13717             this.assetHeight += this.footer.getHeight();
13718         }
13719         */
13720             
13721         if(!this.tpl){
13722             this.tpl = Roo.bootstrap.version == 4 ?
13723                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13724                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13725         }
13726
13727         this.view = new Roo.View(this.list, this.tpl, {
13728             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13729         });
13730         //this.view.wrapEl.setDisplayed(false);
13731         this.view.on('click', this.onViewClick, this);
13732         
13733         
13734         this.store.on('beforeload', this.onBeforeLoad, this);
13735         this.store.on('load', this.onLoad, this);
13736         this.store.on('loadexception', this.onLoadException, this);
13737         /*
13738         if(this.resizable){
13739             this.resizer = new Roo.Resizable(this.list,  {
13740                pinned:true, handles:'se'
13741             });
13742             this.resizer.on('resize', function(r, w, h){
13743                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13744                 this.listWidth = w;
13745                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13746                 this.restrictHeight();
13747             }, this);
13748             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13749         }
13750         */
13751         if(!this.editable){
13752             this.editable = true;
13753             this.setEditable(false);
13754         }
13755         
13756         /*
13757         
13758         if (typeof(this.events.add.listeners) != 'undefined') {
13759             
13760             this.addicon = this.wrap.createChild(
13761                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13762        
13763             this.addicon.on('click', function(e) {
13764                 this.fireEvent('add', this);
13765             }, this);
13766         }
13767         if (typeof(this.events.edit.listeners) != 'undefined') {
13768             
13769             this.editicon = this.wrap.createChild(
13770                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13771             if (this.addicon) {
13772                 this.editicon.setStyle('margin-left', '40px');
13773             }
13774             this.editicon.on('click', function(e) {
13775                 
13776                 // we fire even  if inothing is selected..
13777                 this.fireEvent('edit', this, this.lastData );
13778                 
13779             }, this);
13780         }
13781         */
13782         
13783         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13784             "up" : function(e){
13785                 this.inKeyMode = true;
13786                 this.selectPrev();
13787             },
13788
13789             "down" : function(e){
13790                 if(!this.isExpanded()){
13791                     this.onTriggerClick();
13792                 }else{
13793                     this.inKeyMode = true;
13794                     this.selectNext();
13795                 }
13796             },
13797
13798             "enter" : function(e){
13799 //                this.onViewClick();
13800                 //return true;
13801                 this.collapse();
13802                 
13803                 if(this.fireEvent("specialkey", this, e)){
13804                     this.onViewClick(false);
13805                 }
13806                 
13807                 return true;
13808             },
13809
13810             "esc" : function(e){
13811                 this.collapse();
13812             },
13813
13814             "tab" : function(e){
13815                 this.collapse();
13816                 
13817                 if(this.fireEvent("specialkey", this, e)){
13818                     this.onViewClick(false);
13819                 }
13820                 
13821                 return true;
13822             },
13823
13824             scope : this,
13825
13826             doRelay : function(foo, bar, hname){
13827                 if(hname == 'down' || this.scope.isExpanded()){
13828                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13829                 }
13830                 return true;
13831             },
13832
13833             forceKeyDown: true
13834         });
13835         
13836         
13837         this.queryDelay = Math.max(this.queryDelay || 10,
13838                 this.mode == 'local' ? 10 : 250);
13839         
13840         
13841         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13842         
13843         if(this.typeAhead){
13844             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13845         }
13846         if(this.editable !== false){
13847             this.inputEl().on("keyup", this.onKeyUp, this);
13848         }
13849         if(this.forceSelection){
13850             this.inputEl().on('blur', this.doForce, this);
13851         }
13852         
13853         if(this.multiple){
13854             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13855             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13856         }
13857     },
13858     
13859     initTickableEvents: function()
13860     {   
13861         this.createList();
13862         
13863         if(this.hiddenName){
13864             
13865             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13866             
13867             this.hiddenField.dom.value =
13868                 this.hiddenValue !== undefined ? this.hiddenValue :
13869                 this.value !== undefined ? this.value : '';
13870
13871             // prevent input submission
13872             this.el.dom.removeAttribute('name');
13873             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13874              
13875              
13876         }
13877         
13878 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13879         
13880         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13881         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13882         if(this.triggerList){
13883             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13884         }
13885          
13886         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13887         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13888         
13889         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13890         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13891         
13892         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13893         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13894         
13895         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13896         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13897         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13898         
13899         this.okBtn.hide();
13900         this.cancelBtn.hide();
13901         
13902         var _this = this;
13903         
13904         (function(){
13905             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13906             _this.list.setWidth(lw);
13907         }).defer(100);
13908         
13909         this.list.on('mouseover', this.onViewOver, this);
13910         this.list.on('mousemove', this.onViewMove, this);
13911         
13912         this.list.on('scroll', this.onViewScroll, this);
13913         
13914         if(!this.tpl){
13915             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13916                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13917         }
13918
13919         this.view = new Roo.View(this.list, this.tpl, {
13920             singleSelect:true,
13921             tickable:true,
13922             parent:this,
13923             store: this.store,
13924             selectedClass: this.selectedClass
13925         });
13926         
13927         //this.view.wrapEl.setDisplayed(false);
13928         this.view.on('click', this.onViewClick, this);
13929         
13930         
13931         
13932         this.store.on('beforeload', this.onBeforeLoad, this);
13933         this.store.on('load', this.onLoad, this);
13934         this.store.on('loadexception', this.onLoadException, this);
13935         
13936         if(this.editable){
13937             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13938                 "up" : function(e){
13939                     this.inKeyMode = true;
13940                     this.selectPrev();
13941                 },
13942
13943                 "down" : function(e){
13944                     this.inKeyMode = true;
13945                     this.selectNext();
13946                 },
13947
13948                 "enter" : function(e){
13949                     if(this.fireEvent("specialkey", this, e)){
13950                         this.onViewClick(false);
13951                     }
13952                     
13953                     return true;
13954                 },
13955
13956                 "esc" : function(e){
13957                     this.onTickableFooterButtonClick(e, false, false);
13958                 },
13959
13960                 "tab" : function(e){
13961                     this.fireEvent("specialkey", this, e);
13962                     
13963                     this.onTickableFooterButtonClick(e, false, false);
13964                     
13965                     return true;
13966                 },
13967
13968                 scope : this,
13969
13970                 doRelay : function(e, fn, key){
13971                     if(this.scope.isExpanded()){
13972                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13973                     }
13974                     return true;
13975                 },
13976
13977                 forceKeyDown: true
13978             });
13979         }
13980         
13981         this.queryDelay = Math.max(this.queryDelay || 10,
13982                 this.mode == 'local' ? 10 : 250);
13983         
13984         
13985         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13986         
13987         if(this.typeAhead){
13988             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13989         }
13990         
13991         if(this.editable !== false){
13992             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13993         }
13994         
13995         this.indicator = this.indicatorEl();
13996         
13997         if(this.indicator){
13998             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13999             this.indicator.hide();
14000         }
14001         
14002     },
14003
14004     onDestroy : function(){
14005         if(this.view){
14006             this.view.setStore(null);
14007             this.view.el.removeAllListeners();
14008             this.view.el.remove();
14009             this.view.purgeListeners();
14010         }
14011         if(this.list){
14012             this.list.dom.innerHTML  = '';
14013         }
14014         
14015         if(this.store){
14016             this.store.un('beforeload', this.onBeforeLoad, this);
14017             this.store.un('load', this.onLoad, this);
14018             this.store.un('loadexception', this.onLoadException, this);
14019         }
14020         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14021     },
14022
14023     // private
14024     fireKey : function(e){
14025         if(e.isNavKeyPress() && !this.list.isVisible()){
14026             this.fireEvent("specialkey", this, e);
14027         }
14028     },
14029
14030     // private
14031     onResize: function(w, h){
14032 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14033 //        
14034 //        if(typeof w != 'number'){
14035 //            // we do not handle it!?!?
14036 //            return;
14037 //        }
14038 //        var tw = this.trigger.getWidth();
14039 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14040 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14041 //        var x = w - tw;
14042 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14043 //            
14044 //        //this.trigger.setStyle('left', x+'px');
14045 //        
14046 //        if(this.list && this.listWidth === undefined){
14047 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14048 //            this.list.setWidth(lw);
14049 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14050 //        }
14051         
14052     
14053         
14054     },
14055
14056     /**
14057      * Allow or prevent the user from directly editing the field text.  If false is passed,
14058      * the user will only be able to select from the items defined in the dropdown list.  This method
14059      * is the runtime equivalent of setting the 'editable' config option at config time.
14060      * @param {Boolean} value True to allow the user to directly edit the field text
14061      */
14062     setEditable : function(value){
14063         if(value == this.editable){
14064             return;
14065         }
14066         this.editable = value;
14067         if(!value){
14068             this.inputEl().dom.setAttribute('readOnly', true);
14069             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14070             this.inputEl().addClass('x-combo-noedit');
14071         }else{
14072             this.inputEl().dom.setAttribute('readOnly', false);
14073             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14074             this.inputEl().removeClass('x-combo-noedit');
14075         }
14076     },
14077
14078     // private
14079     
14080     onBeforeLoad : function(combo,opts){
14081         if(!this.hasFocus){
14082             return;
14083         }
14084          if (!opts.add) {
14085             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14086          }
14087         this.restrictHeight();
14088         this.selectedIndex = -1;
14089     },
14090
14091     // private
14092     onLoad : function(){
14093         
14094         this.hasQuery = false;
14095         
14096         if(!this.hasFocus){
14097             return;
14098         }
14099         
14100         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14101             this.loading.hide();
14102         }
14103         
14104         if(this.store.getCount() > 0){
14105             
14106             this.expand();
14107             this.restrictHeight();
14108             if(this.lastQuery == this.allQuery){
14109                 if(this.editable && !this.tickable){
14110                     this.inputEl().dom.select();
14111                 }
14112                 
14113                 if(
14114                     !this.selectByValue(this.value, true) &&
14115                     this.autoFocus && 
14116                     (
14117                         !this.store.lastOptions ||
14118                         typeof(this.store.lastOptions.add) == 'undefined' || 
14119                         this.store.lastOptions.add != true
14120                     )
14121                 ){
14122                     this.select(0, true);
14123                 }
14124             }else{
14125                 if(this.autoFocus){
14126                     this.selectNext();
14127                 }
14128                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14129                     this.taTask.delay(this.typeAheadDelay);
14130                 }
14131             }
14132         }else{
14133             this.onEmptyResults();
14134         }
14135         
14136         //this.el.focus();
14137     },
14138     // private
14139     onLoadException : function()
14140     {
14141         this.hasQuery = false;
14142         
14143         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14144             this.loading.hide();
14145         }
14146         
14147         if(this.tickable && this.editable){
14148             return;
14149         }
14150         
14151         this.collapse();
14152         // only causes errors at present
14153         //Roo.log(this.store.reader.jsonData);
14154         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14155             // fixme
14156             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14157         //}
14158         
14159         
14160     },
14161     // private
14162     onTypeAhead : function(){
14163         if(this.store.getCount() > 0){
14164             var r = this.store.getAt(0);
14165             var newValue = r.data[this.displayField];
14166             var len = newValue.length;
14167             var selStart = this.getRawValue().length;
14168             
14169             if(selStart != len){
14170                 this.setRawValue(newValue);
14171                 this.selectText(selStart, newValue.length);
14172             }
14173         }
14174     },
14175
14176     // private
14177     onSelect : function(record, index){
14178         
14179         if(this.fireEvent('beforeselect', this, record, index) !== false){
14180         
14181             this.setFromData(index > -1 ? record.data : false);
14182             
14183             this.collapse();
14184             this.fireEvent('select', this, record, index);
14185         }
14186     },
14187
14188     /**
14189      * Returns the currently selected field value or empty string if no value is set.
14190      * @return {String} value The selected value
14191      */
14192     getValue : function()
14193     {
14194         if(Roo.isIOS && this.useNativeIOS){
14195             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14196         }
14197         
14198         if(this.multiple){
14199             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14200         }
14201         
14202         if(this.valueField){
14203             return typeof this.value != 'undefined' ? this.value : '';
14204         }else{
14205             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14206         }
14207     },
14208     
14209     getRawValue : function()
14210     {
14211         if(Roo.isIOS && this.useNativeIOS){
14212             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14213         }
14214         
14215         var v = this.inputEl().getValue();
14216         
14217         return v;
14218     },
14219
14220     /**
14221      * Clears any text/value currently set in the field
14222      */
14223     clearValue : function(){
14224         
14225         if(this.hiddenField){
14226             this.hiddenField.dom.value = '';
14227         }
14228         this.value = '';
14229         this.setRawValue('');
14230         this.lastSelectionText = '';
14231         this.lastData = false;
14232         
14233         var close = this.closeTriggerEl();
14234         
14235         if(close){
14236             close.hide();
14237         }
14238         
14239         this.validate();
14240         
14241     },
14242
14243     /**
14244      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14245      * will be displayed in the field.  If the value does not match the data value of an existing item,
14246      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14247      * Otherwise the field will be blank (although the value will still be set).
14248      * @param {String} value The value to match
14249      */
14250     setValue : function(v)
14251     {
14252         if(Roo.isIOS && this.useNativeIOS){
14253             this.setIOSValue(v);
14254             return;
14255         }
14256         
14257         if(this.multiple){
14258             this.syncValue();
14259             return;
14260         }
14261         
14262         var text = v;
14263         if(this.valueField){
14264             var r = this.findRecord(this.valueField, v);
14265             if(r){
14266                 text = r.data[this.displayField];
14267             }else if(this.valueNotFoundText !== undefined){
14268                 text = this.valueNotFoundText;
14269             }
14270         }
14271         this.lastSelectionText = text;
14272         if(this.hiddenField){
14273             this.hiddenField.dom.value = v;
14274         }
14275         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14276         this.value = v;
14277         
14278         var close = this.closeTriggerEl();
14279         
14280         if(close){
14281             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14282         }
14283         
14284         this.validate();
14285     },
14286     /**
14287      * @property {Object} the last set data for the element
14288      */
14289     
14290     lastData : false,
14291     /**
14292      * Sets the value of the field based on a object which is related to the record format for the store.
14293      * @param {Object} value the value to set as. or false on reset?
14294      */
14295     setFromData : function(o){
14296         
14297         if(this.multiple){
14298             this.addItem(o);
14299             return;
14300         }
14301             
14302         var dv = ''; // display value
14303         var vv = ''; // value value..
14304         this.lastData = o;
14305         if (this.displayField) {
14306             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14307         } else {
14308             // this is an error condition!!!
14309             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14310         }
14311         
14312         if(this.valueField){
14313             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14314         }
14315         
14316         var close = this.closeTriggerEl();
14317         
14318         if(close){
14319             if(dv.length || vv * 1 > 0){
14320                 close.show() ;
14321                 this.blockFocus=true;
14322             } else {
14323                 close.hide();
14324             }             
14325         }
14326         
14327         if(this.hiddenField){
14328             this.hiddenField.dom.value = vv;
14329             
14330             this.lastSelectionText = dv;
14331             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14332             this.value = vv;
14333             return;
14334         }
14335         // no hidden field.. - we store the value in 'value', but still display
14336         // display field!!!!
14337         this.lastSelectionText = dv;
14338         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14339         this.value = vv;
14340         
14341         
14342         
14343     },
14344     // private
14345     reset : function(){
14346         // overridden so that last data is reset..
14347         
14348         if(this.multiple){
14349             this.clearItem();
14350             return;
14351         }
14352         
14353         this.setValue(this.originalValue);
14354         //this.clearInvalid();
14355         this.lastData = false;
14356         if (this.view) {
14357             this.view.clearSelections();
14358         }
14359         
14360         this.validate();
14361     },
14362     // private
14363     findRecord : function(prop, value){
14364         var record;
14365         if(this.store.getCount() > 0){
14366             this.store.each(function(r){
14367                 if(r.data[prop] == value){
14368                     record = r;
14369                     return false;
14370                 }
14371                 return true;
14372             });
14373         }
14374         return record;
14375     },
14376     
14377     getName: function()
14378     {
14379         // returns hidden if it's set..
14380         if (!this.rendered) {return ''};
14381         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14382         
14383     },
14384     // private
14385     onViewMove : function(e, t){
14386         this.inKeyMode = false;
14387     },
14388
14389     // private
14390     onViewOver : function(e, t){
14391         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14392             return;
14393         }
14394         var item = this.view.findItemFromChild(t);
14395         
14396         if(item){
14397             var index = this.view.indexOf(item);
14398             this.select(index, false);
14399         }
14400     },
14401
14402     // private
14403     onViewClick : function(view, doFocus, el, e)
14404     {
14405         var index = this.view.getSelectedIndexes()[0];
14406         
14407         var r = this.store.getAt(index);
14408         
14409         if(this.tickable){
14410             
14411             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14412                 return;
14413             }
14414             
14415             var rm = false;
14416             var _this = this;
14417             
14418             Roo.each(this.tickItems, function(v,k){
14419                 
14420                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14421                     Roo.log(v);
14422                     _this.tickItems.splice(k, 1);
14423                     
14424                     if(typeof(e) == 'undefined' && view == false){
14425                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14426                     }
14427                     
14428                     rm = true;
14429                     return;
14430                 }
14431             });
14432             
14433             if(rm){
14434                 return;
14435             }
14436             
14437             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14438                 this.tickItems.push(r.data);
14439             }
14440             
14441             if(typeof(e) == 'undefined' && view == false){
14442                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14443             }
14444                     
14445             return;
14446         }
14447         
14448         if(r){
14449             this.onSelect(r, index);
14450         }
14451         if(doFocus !== false && !this.blockFocus){
14452             this.inputEl().focus();
14453         }
14454     },
14455
14456     // private
14457     restrictHeight : function(){
14458         //this.innerList.dom.style.height = '';
14459         //var inner = this.innerList.dom;
14460         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14461         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14462         //this.list.beginUpdate();
14463         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14464         this.list.alignTo(this.inputEl(), this.listAlign);
14465         this.list.alignTo(this.inputEl(), this.listAlign);
14466         //this.list.endUpdate();
14467     },
14468
14469     // private
14470     onEmptyResults : function(){
14471         
14472         if(this.tickable && this.editable){
14473             this.hasFocus = false;
14474             this.restrictHeight();
14475             return;
14476         }
14477         
14478         this.collapse();
14479     },
14480
14481     /**
14482      * Returns true if the dropdown list is expanded, else false.
14483      */
14484     isExpanded : function(){
14485         return this.list.isVisible();
14486     },
14487
14488     /**
14489      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14490      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14491      * @param {String} value The data value of the item to select
14492      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14493      * selected item if it is not currently in view (defaults to true)
14494      * @return {Boolean} True if the value matched an item in the list, else false
14495      */
14496     selectByValue : function(v, scrollIntoView){
14497         if(v !== undefined && v !== null){
14498             var r = this.findRecord(this.valueField || this.displayField, v);
14499             if(r){
14500                 this.select(this.store.indexOf(r), scrollIntoView);
14501                 return true;
14502             }
14503         }
14504         return false;
14505     },
14506
14507     /**
14508      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14509      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14510      * @param {Number} index The zero-based index of the list item to select
14511      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14512      * selected item if it is not currently in view (defaults to true)
14513      */
14514     select : function(index, scrollIntoView){
14515         this.selectedIndex = index;
14516         this.view.select(index);
14517         if(scrollIntoView !== false){
14518             var el = this.view.getNode(index);
14519             /*
14520              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14521              */
14522             if(el){
14523                 this.list.scrollChildIntoView(el, false);
14524             }
14525         }
14526     },
14527
14528     // private
14529     selectNext : function(){
14530         var ct = this.store.getCount();
14531         if(ct > 0){
14532             if(this.selectedIndex == -1){
14533                 this.select(0);
14534             }else if(this.selectedIndex < ct-1){
14535                 this.select(this.selectedIndex+1);
14536             }
14537         }
14538     },
14539
14540     // private
14541     selectPrev : function(){
14542         var ct = this.store.getCount();
14543         if(ct > 0){
14544             if(this.selectedIndex == -1){
14545                 this.select(0);
14546             }else if(this.selectedIndex != 0){
14547                 this.select(this.selectedIndex-1);
14548             }
14549         }
14550     },
14551
14552     // private
14553     onKeyUp : function(e){
14554         if(this.editable !== false && !e.isSpecialKey()){
14555             this.lastKey = e.getKey();
14556             this.dqTask.delay(this.queryDelay);
14557         }
14558     },
14559
14560     // private
14561     validateBlur : function(){
14562         return !this.list || !this.list.isVisible();   
14563     },
14564
14565     // private
14566     initQuery : function(){
14567         
14568         var v = this.getRawValue();
14569         
14570         if(this.tickable && this.editable){
14571             v = this.tickableInputEl().getValue();
14572         }
14573         
14574         this.doQuery(v);
14575     },
14576
14577     // private
14578     doForce : function(){
14579         if(this.inputEl().dom.value.length > 0){
14580             this.inputEl().dom.value =
14581                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14582              
14583         }
14584     },
14585
14586     /**
14587      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14588      * query allowing the query action to be canceled if needed.
14589      * @param {String} query The SQL query to execute
14590      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14591      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14592      * saved in the current store (defaults to false)
14593      */
14594     doQuery : function(q, forceAll){
14595         
14596         if(q === undefined || q === null){
14597             q = '';
14598         }
14599         var qe = {
14600             query: q,
14601             forceAll: forceAll,
14602             combo: this,
14603             cancel:false
14604         };
14605         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14606             return false;
14607         }
14608         q = qe.query;
14609         
14610         forceAll = qe.forceAll;
14611         if(forceAll === true || (q.length >= this.minChars)){
14612             
14613             this.hasQuery = true;
14614             
14615             if(this.lastQuery != q || this.alwaysQuery){
14616                 this.lastQuery = q;
14617                 if(this.mode == 'local'){
14618                     this.selectedIndex = -1;
14619                     if(forceAll){
14620                         this.store.clearFilter();
14621                     }else{
14622                         
14623                         if(this.specialFilter){
14624                             this.fireEvent('specialfilter', this);
14625                             this.onLoad();
14626                             return;
14627                         }
14628                         
14629                         this.store.filter(this.displayField, q);
14630                     }
14631                     
14632                     this.store.fireEvent("datachanged", this.store);
14633                     
14634                     this.onLoad();
14635                     
14636                     
14637                 }else{
14638                     
14639                     this.store.baseParams[this.queryParam] = q;
14640                     
14641                     var options = {params : this.getParams(q)};
14642                     
14643                     if(this.loadNext){
14644                         options.add = true;
14645                         options.params.start = this.page * this.pageSize;
14646                     }
14647                     
14648                     this.store.load(options);
14649                     
14650                     /*
14651                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14652                      *  we should expand the list on onLoad
14653                      *  so command out it
14654                      */
14655 //                    this.expand();
14656                 }
14657             }else{
14658                 this.selectedIndex = -1;
14659                 this.onLoad();   
14660             }
14661         }
14662         
14663         this.loadNext = false;
14664     },
14665     
14666     // private
14667     getParams : function(q){
14668         var p = {};
14669         //p[this.queryParam] = q;
14670         
14671         if(this.pageSize){
14672             p.start = 0;
14673             p.limit = this.pageSize;
14674         }
14675         return p;
14676     },
14677
14678     /**
14679      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14680      */
14681     collapse : function(){
14682         if(!this.isExpanded()){
14683             return;
14684         }
14685         
14686         this.list.hide();
14687         
14688         this.hasFocus = false;
14689         
14690         if(this.tickable){
14691             this.okBtn.hide();
14692             this.cancelBtn.hide();
14693             this.trigger.show();
14694             
14695             if(this.editable){
14696                 this.tickableInputEl().dom.value = '';
14697                 this.tickableInputEl().blur();
14698             }
14699             
14700         }
14701         
14702         Roo.get(document).un('mousedown', this.collapseIf, this);
14703         Roo.get(document).un('mousewheel', this.collapseIf, this);
14704         if (!this.editable) {
14705             Roo.get(document).un('keydown', this.listKeyPress, this);
14706         }
14707         this.fireEvent('collapse', this);
14708         
14709         this.validate();
14710     },
14711
14712     // private
14713     collapseIf : function(e){
14714         var in_combo  = e.within(this.el);
14715         var in_list =  e.within(this.list);
14716         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14717         
14718         if (in_combo || in_list || is_list) {
14719             //e.stopPropagation();
14720             return;
14721         }
14722         
14723         if(this.tickable){
14724             this.onTickableFooterButtonClick(e, false, false);
14725         }
14726
14727         this.collapse();
14728         
14729     },
14730
14731     /**
14732      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14733      */
14734     expand : function(){
14735        
14736         if(this.isExpanded() || !this.hasFocus){
14737             return;
14738         }
14739         
14740         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14741         this.list.setWidth(lw);
14742         
14743         Roo.log('expand');
14744         
14745         this.list.show();
14746         
14747         this.restrictHeight();
14748         
14749         if(this.tickable){
14750             
14751             this.tickItems = Roo.apply([], this.item);
14752             
14753             this.okBtn.show();
14754             this.cancelBtn.show();
14755             this.trigger.hide();
14756             
14757             if(this.editable){
14758                 this.tickableInputEl().focus();
14759             }
14760             
14761         }
14762         
14763         Roo.get(document).on('mousedown', this.collapseIf, this);
14764         Roo.get(document).on('mousewheel', this.collapseIf, this);
14765         if (!this.editable) {
14766             Roo.get(document).on('keydown', this.listKeyPress, this);
14767         }
14768         
14769         this.fireEvent('expand', this);
14770     },
14771
14772     // private
14773     // Implements the default empty TriggerField.onTriggerClick function
14774     onTriggerClick : function(e)
14775     {
14776         Roo.log('trigger click');
14777         
14778         if(this.disabled || !this.triggerList){
14779             return;
14780         }
14781         
14782         this.page = 0;
14783         this.loadNext = false;
14784         
14785         if(this.isExpanded()){
14786             this.collapse();
14787             if (!this.blockFocus) {
14788                 this.inputEl().focus();
14789             }
14790             
14791         }else {
14792             this.hasFocus = true;
14793             if(this.triggerAction == 'all') {
14794                 this.doQuery(this.allQuery, true);
14795             } else {
14796                 this.doQuery(this.getRawValue());
14797             }
14798             if (!this.blockFocus) {
14799                 this.inputEl().focus();
14800             }
14801         }
14802     },
14803     
14804     onTickableTriggerClick : function(e)
14805     {
14806         if(this.disabled){
14807             return;
14808         }
14809         
14810         this.page = 0;
14811         this.loadNext = false;
14812         this.hasFocus = true;
14813         
14814         if(this.triggerAction == 'all') {
14815             this.doQuery(this.allQuery, true);
14816         } else {
14817             this.doQuery(this.getRawValue());
14818         }
14819     },
14820     
14821     onSearchFieldClick : function(e)
14822     {
14823         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14824             this.onTickableFooterButtonClick(e, false, false);
14825             return;
14826         }
14827         
14828         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14829             return;
14830         }
14831         
14832         this.page = 0;
14833         this.loadNext = false;
14834         this.hasFocus = true;
14835         
14836         if(this.triggerAction == 'all') {
14837             this.doQuery(this.allQuery, true);
14838         } else {
14839             this.doQuery(this.getRawValue());
14840         }
14841     },
14842     
14843     listKeyPress : function(e)
14844     {
14845         //Roo.log('listkeypress');
14846         // scroll to first matching element based on key pres..
14847         if (e.isSpecialKey()) {
14848             return false;
14849         }
14850         var k = String.fromCharCode(e.getKey()).toUpperCase();
14851         //Roo.log(k);
14852         var match  = false;
14853         var csel = this.view.getSelectedNodes();
14854         var cselitem = false;
14855         if (csel.length) {
14856             var ix = this.view.indexOf(csel[0]);
14857             cselitem  = this.store.getAt(ix);
14858             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14859                 cselitem = false;
14860             }
14861             
14862         }
14863         
14864         this.store.each(function(v) { 
14865             if (cselitem) {
14866                 // start at existing selection.
14867                 if (cselitem.id == v.id) {
14868                     cselitem = false;
14869                 }
14870                 return true;
14871             }
14872                 
14873             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14874                 match = this.store.indexOf(v);
14875                 return false;
14876             }
14877             return true;
14878         }, this);
14879         
14880         if (match === false) {
14881             return true; // no more action?
14882         }
14883         // scroll to?
14884         this.view.select(match);
14885         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14886         sn.scrollIntoView(sn.dom.parentNode, false);
14887     },
14888     
14889     onViewScroll : function(e, t){
14890         
14891         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){
14892             return;
14893         }
14894         
14895         this.hasQuery = true;
14896         
14897         this.loading = this.list.select('.loading', true).first();
14898         
14899         if(this.loading === null){
14900             this.list.createChild({
14901                 tag: 'div',
14902                 cls: 'loading roo-select2-more-results roo-select2-active',
14903                 html: 'Loading more results...'
14904             });
14905             
14906             this.loading = this.list.select('.loading', true).first();
14907             
14908             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14909             
14910             this.loading.hide();
14911         }
14912         
14913         this.loading.show();
14914         
14915         var _combo = this;
14916         
14917         this.page++;
14918         this.loadNext = true;
14919         
14920         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14921         
14922         return;
14923     },
14924     
14925     addItem : function(o)
14926     {   
14927         var dv = ''; // display value
14928         
14929         if (this.displayField) {
14930             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14931         } else {
14932             // this is an error condition!!!
14933             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14934         }
14935         
14936         if(!dv.length){
14937             return;
14938         }
14939         
14940         var choice = this.choices.createChild({
14941             tag: 'li',
14942             cls: 'roo-select2-search-choice',
14943             cn: [
14944                 {
14945                     tag: 'div',
14946                     html: dv
14947                 },
14948                 {
14949                     tag: 'a',
14950                     href: '#',
14951                     cls: 'roo-select2-search-choice-close fa fa-times',
14952                     tabindex: '-1'
14953                 }
14954             ]
14955             
14956         }, this.searchField);
14957         
14958         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14959         
14960         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14961         
14962         this.item.push(o);
14963         
14964         this.lastData = o;
14965         
14966         this.syncValue();
14967         
14968         this.inputEl().dom.value = '';
14969         
14970         this.validate();
14971     },
14972     
14973     onRemoveItem : function(e, _self, o)
14974     {
14975         e.preventDefault();
14976         
14977         this.lastItem = Roo.apply([], this.item);
14978         
14979         var index = this.item.indexOf(o.data) * 1;
14980         
14981         if( index < 0){
14982             Roo.log('not this item?!');
14983             return;
14984         }
14985         
14986         this.item.splice(index, 1);
14987         o.item.remove();
14988         
14989         this.syncValue();
14990         
14991         this.fireEvent('remove', this, e);
14992         
14993         this.validate();
14994         
14995     },
14996     
14997     syncValue : function()
14998     {
14999         if(!this.item.length){
15000             this.clearValue();
15001             return;
15002         }
15003             
15004         var value = [];
15005         var _this = this;
15006         Roo.each(this.item, function(i){
15007             if(_this.valueField){
15008                 value.push(i[_this.valueField]);
15009                 return;
15010             }
15011
15012             value.push(i);
15013         });
15014
15015         this.value = value.join(',');
15016
15017         if(this.hiddenField){
15018             this.hiddenField.dom.value = this.value;
15019         }
15020         
15021         this.store.fireEvent("datachanged", this.store);
15022         
15023         this.validate();
15024     },
15025     
15026     clearItem : function()
15027     {
15028         if(!this.multiple){
15029             return;
15030         }
15031         
15032         this.item = [];
15033         
15034         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15035            c.remove();
15036         });
15037         
15038         this.syncValue();
15039         
15040         this.validate();
15041         
15042         if(this.tickable && !Roo.isTouch){
15043             this.view.refresh();
15044         }
15045     },
15046     
15047     inputEl: function ()
15048     {
15049         if(Roo.isIOS && this.useNativeIOS){
15050             return this.el.select('select.roo-ios-select', true).first();
15051         }
15052         
15053         if(Roo.isTouch && this.mobileTouchView){
15054             return this.el.select('input.form-control',true).first();
15055         }
15056         
15057         if(this.tickable){
15058             return this.searchField;
15059         }
15060         
15061         return this.el.select('input.form-control',true).first();
15062     },
15063     
15064     onTickableFooterButtonClick : function(e, btn, el)
15065     {
15066         e.preventDefault();
15067         
15068         this.lastItem = Roo.apply([], this.item);
15069         
15070         if(btn && btn.name == 'cancel'){
15071             this.tickItems = Roo.apply([], this.item);
15072             this.collapse();
15073             return;
15074         }
15075         
15076         this.clearItem();
15077         
15078         var _this = this;
15079         
15080         Roo.each(this.tickItems, function(o){
15081             _this.addItem(o);
15082         });
15083         
15084         this.collapse();
15085         
15086     },
15087     
15088     validate : function()
15089     {
15090         if(this.getVisibilityEl().hasClass('hidden')){
15091             return true;
15092         }
15093         
15094         var v = this.getRawValue();
15095         
15096         if(this.multiple){
15097             v = this.getValue();
15098         }
15099         
15100         if(this.disabled || this.allowBlank || v.length){
15101             this.markValid();
15102             return true;
15103         }
15104         
15105         this.markInvalid();
15106         return false;
15107     },
15108     
15109     tickableInputEl : function()
15110     {
15111         if(!this.tickable || !this.editable){
15112             return this.inputEl();
15113         }
15114         
15115         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15116     },
15117     
15118     
15119     getAutoCreateTouchView : function()
15120     {
15121         var id = Roo.id();
15122         
15123         var cfg = {
15124             cls: 'form-group' //input-group
15125         };
15126         
15127         var input =  {
15128             tag: 'input',
15129             id : id,
15130             type : this.inputType,
15131             cls : 'form-control x-combo-noedit',
15132             autocomplete: 'new-password',
15133             placeholder : this.placeholder || '',
15134             readonly : true
15135         };
15136         
15137         if (this.name) {
15138             input.name = this.name;
15139         }
15140         
15141         if (this.size) {
15142             input.cls += ' input-' + this.size;
15143         }
15144         
15145         if (this.disabled) {
15146             input.disabled = true;
15147         }
15148         
15149         var inputblock = {
15150             cls : '',
15151             cn : [
15152                 input
15153             ]
15154         };
15155         
15156         if(this.before){
15157             inputblock.cls += ' input-group';
15158             
15159             inputblock.cn.unshift({
15160                 tag :'span',
15161                 cls : 'input-group-addon input-group-prepend input-group-text',
15162                 html : this.before
15163             });
15164         }
15165         
15166         if(this.removable && !this.multiple){
15167             inputblock.cls += ' roo-removable';
15168             
15169             inputblock.cn.push({
15170                 tag: 'button',
15171                 html : 'x',
15172                 cls : 'roo-combo-removable-btn close'
15173             });
15174         }
15175
15176         if(this.hasFeedback && !this.allowBlank){
15177             
15178             inputblock.cls += ' has-feedback';
15179             
15180             inputblock.cn.push({
15181                 tag: 'span',
15182                 cls: 'glyphicon form-control-feedback'
15183             });
15184             
15185         }
15186         
15187         if (this.after) {
15188             
15189             inputblock.cls += (this.before) ? '' : ' input-group';
15190             
15191             inputblock.cn.push({
15192                 tag :'span',
15193                 cls : 'input-group-addon input-group-append input-group-text',
15194                 html : this.after
15195             });
15196         }
15197
15198         
15199         var ibwrap = inputblock;
15200         
15201         if(this.multiple){
15202             ibwrap = {
15203                 tag: 'ul',
15204                 cls: 'roo-select2-choices',
15205                 cn:[
15206                     {
15207                         tag: 'li',
15208                         cls: 'roo-select2-search-field',
15209                         cn: [
15210
15211                             inputblock
15212                         ]
15213                     }
15214                 ]
15215             };
15216         
15217             
15218         }
15219         
15220         var combobox = {
15221             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15222             cn: [
15223                 {
15224                     tag: 'input',
15225                     type : 'hidden',
15226                     cls: 'form-hidden-field'
15227                 },
15228                 ibwrap
15229             ]
15230         };
15231         
15232         if(!this.multiple && this.showToggleBtn){
15233             
15234             var caret = {
15235                         tag: 'span',
15236                         cls: 'caret'
15237             };
15238             
15239             if (this.caret != false) {
15240                 caret = {
15241                      tag: 'i',
15242                      cls: 'fa fa-' + this.caret
15243                 };
15244                 
15245             }
15246             
15247             combobox.cn.push({
15248                 tag :'span',
15249                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15250                 cn : [
15251                     caret,
15252                     {
15253                         tag: 'span',
15254                         cls: 'combobox-clear',
15255                         cn  : [
15256                             {
15257                                 tag : 'i',
15258                                 cls: 'icon-remove'
15259                             }
15260                         ]
15261                     }
15262                 ]
15263
15264             })
15265         }
15266         
15267         if(this.multiple){
15268             combobox.cls += ' roo-select2-container-multi';
15269         }
15270         
15271         var align = this.labelAlign || this.parentLabelAlign();
15272         
15273         if (align ==='left' && this.fieldLabel.length) {
15274
15275             cfg.cn = [
15276                 {
15277                    tag : 'i',
15278                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15279                    tooltip : 'This field is required'
15280                 },
15281                 {
15282                     tag: 'label',
15283                     cls : 'control-label col-form-label',
15284                     html : this.fieldLabel
15285
15286                 },
15287                 {
15288                     cls : '', 
15289                     cn: [
15290                         combobox
15291                     ]
15292                 }
15293             ];
15294             
15295             var labelCfg = cfg.cn[1];
15296             var contentCfg = cfg.cn[2];
15297             
15298
15299             if(this.indicatorpos == 'right'){
15300                 cfg.cn = [
15301                     {
15302                         tag: 'label',
15303                         'for' :  id,
15304                         cls : 'control-label col-form-label',
15305                         cn : [
15306                             {
15307                                 tag : 'span',
15308                                 html : this.fieldLabel
15309                             },
15310                             {
15311                                 tag : 'i',
15312                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15313                                 tooltip : 'This field is required'
15314                             }
15315                         ]
15316                     },
15317                     {
15318                         cls : "",
15319                         cn: [
15320                             combobox
15321                         ]
15322                     }
15323
15324                 ];
15325                 
15326                 labelCfg = cfg.cn[0];
15327                 contentCfg = cfg.cn[1];
15328             }
15329             
15330            
15331             
15332             if(this.labelWidth > 12){
15333                 labelCfg.style = "width: " + this.labelWidth + 'px';
15334             }
15335             
15336             if(this.labelWidth < 13 && this.labelmd == 0){
15337                 this.labelmd = this.labelWidth;
15338             }
15339             
15340             if(this.labellg > 0){
15341                 labelCfg.cls += ' col-lg-' + this.labellg;
15342                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15343             }
15344             
15345             if(this.labelmd > 0){
15346                 labelCfg.cls += ' col-md-' + this.labelmd;
15347                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15348             }
15349             
15350             if(this.labelsm > 0){
15351                 labelCfg.cls += ' col-sm-' + this.labelsm;
15352                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15353             }
15354             
15355             if(this.labelxs > 0){
15356                 labelCfg.cls += ' col-xs-' + this.labelxs;
15357                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15358             }
15359                 
15360                 
15361         } else if ( this.fieldLabel.length) {
15362             cfg.cn = [
15363                 {
15364                    tag : 'i',
15365                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15366                    tooltip : 'This field is required'
15367                 },
15368                 {
15369                     tag: 'label',
15370                     cls : 'control-label',
15371                     html : this.fieldLabel
15372
15373                 },
15374                 {
15375                     cls : '', 
15376                     cn: [
15377                         combobox
15378                     ]
15379                 }
15380             ];
15381             
15382             if(this.indicatorpos == 'right'){
15383                 cfg.cn = [
15384                     {
15385                         tag: 'label',
15386                         cls : 'control-label',
15387                         html : this.fieldLabel,
15388                         cn : [
15389                             {
15390                                tag : 'i',
15391                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15392                                tooltip : 'This field is required'
15393                             }
15394                         ]
15395                     },
15396                     {
15397                         cls : '', 
15398                         cn: [
15399                             combobox
15400                         ]
15401                     }
15402                 ];
15403             }
15404         } else {
15405             cfg.cn = combobox;    
15406         }
15407         
15408         
15409         var settings = this;
15410         
15411         ['xs','sm','md','lg'].map(function(size){
15412             if (settings[size]) {
15413                 cfg.cls += ' col-' + size + '-' + settings[size];
15414             }
15415         });
15416         
15417         return cfg;
15418     },
15419     
15420     initTouchView : function()
15421     {
15422         this.renderTouchView();
15423         
15424         this.touchViewEl.on('scroll', function(){
15425             this.el.dom.scrollTop = 0;
15426         }, this);
15427         
15428         this.originalValue = this.getValue();
15429         
15430         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15431         
15432         this.inputEl().on("click", this.showTouchView, this);
15433         if (this.triggerEl) {
15434             this.triggerEl.on("click", this.showTouchView, this);
15435         }
15436         
15437         
15438         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15439         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15440         
15441         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15442         
15443         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15444         this.store.on('load', this.onTouchViewLoad, this);
15445         this.store.on('loadexception', this.onTouchViewLoadException, this);
15446         
15447         if(this.hiddenName){
15448             
15449             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15450             
15451             this.hiddenField.dom.value =
15452                 this.hiddenValue !== undefined ? this.hiddenValue :
15453                 this.value !== undefined ? this.value : '';
15454         
15455             this.el.dom.removeAttribute('name');
15456             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15457         }
15458         
15459         if(this.multiple){
15460             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15461             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15462         }
15463         
15464         if(this.removable && !this.multiple){
15465             var close = this.closeTriggerEl();
15466             if(close){
15467                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15468                 close.on('click', this.removeBtnClick, this, close);
15469             }
15470         }
15471         /*
15472          * fix the bug in Safari iOS8
15473          */
15474         this.inputEl().on("focus", function(e){
15475             document.activeElement.blur();
15476         }, this);
15477         
15478         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15479         
15480         return;
15481         
15482         
15483     },
15484     
15485     renderTouchView : function()
15486     {
15487         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15488         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15489         
15490         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15491         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15492         
15493         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15494         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15495         this.touchViewBodyEl.setStyle('overflow', 'auto');
15496         
15497         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15498         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15499         
15500         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15501         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15502         
15503     },
15504     
15505     showTouchView : function()
15506     {
15507         if(this.disabled){
15508             return;
15509         }
15510         
15511         this.touchViewHeaderEl.hide();
15512
15513         if(this.modalTitle.length){
15514             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15515             this.touchViewHeaderEl.show();
15516         }
15517
15518         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15519         this.touchViewEl.show();
15520
15521         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15522         
15523         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15524         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15525
15526         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15527
15528         if(this.modalTitle.length){
15529             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15530         }
15531         
15532         this.touchViewBodyEl.setHeight(bodyHeight);
15533
15534         if(this.animate){
15535             var _this = this;
15536             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15537         }else{
15538             this.touchViewEl.addClass('in');
15539         }
15540         
15541         if(this._touchViewMask){
15542             Roo.get(document.body).addClass("x-body-masked");
15543             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15544             this._touchViewMask.setStyle('z-index', 10000);
15545             this._touchViewMask.addClass('show');
15546         }
15547         
15548         this.doTouchViewQuery();
15549         
15550     },
15551     
15552     hideTouchView : function()
15553     {
15554         this.touchViewEl.removeClass('in');
15555
15556         if(this.animate){
15557             var _this = this;
15558             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15559         }else{
15560             this.touchViewEl.setStyle('display', 'none');
15561         }
15562         
15563         if(this._touchViewMask){
15564             this._touchViewMask.removeClass('show');
15565             Roo.get(document.body).removeClass("x-body-masked");
15566         }
15567     },
15568     
15569     setTouchViewValue : function()
15570     {
15571         if(this.multiple){
15572             this.clearItem();
15573         
15574             var _this = this;
15575
15576             Roo.each(this.tickItems, function(o){
15577                 this.addItem(o);
15578             }, this);
15579         }
15580         
15581         this.hideTouchView();
15582     },
15583     
15584     doTouchViewQuery : function()
15585     {
15586         var qe = {
15587             query: '',
15588             forceAll: true,
15589             combo: this,
15590             cancel:false
15591         };
15592         
15593         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15594             return false;
15595         }
15596         
15597         if(!this.alwaysQuery || this.mode == 'local'){
15598             this.onTouchViewLoad();
15599             return;
15600         }
15601         
15602         this.store.load();
15603     },
15604     
15605     onTouchViewBeforeLoad : function(combo,opts)
15606     {
15607         return;
15608     },
15609
15610     // private
15611     onTouchViewLoad : function()
15612     {
15613         if(this.store.getCount() < 1){
15614             this.onTouchViewEmptyResults();
15615             return;
15616         }
15617         
15618         this.clearTouchView();
15619         
15620         var rawValue = this.getRawValue();
15621         
15622         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15623         
15624         this.tickItems = [];
15625         
15626         this.store.data.each(function(d, rowIndex){
15627             var row = this.touchViewListGroup.createChild(template);
15628             
15629             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15630                 row.addClass(d.data.cls);
15631             }
15632             
15633             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15634                 var cfg = {
15635                     data : d.data,
15636                     html : d.data[this.displayField]
15637                 };
15638                 
15639                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15640                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15641                 }
15642             }
15643             row.removeClass('selected');
15644             if(!this.multiple && this.valueField &&
15645                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15646             {
15647                 // radio buttons..
15648                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15649                 row.addClass('selected');
15650             }
15651             
15652             if(this.multiple && this.valueField &&
15653                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15654             {
15655                 
15656                 // checkboxes...
15657                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15658                 this.tickItems.push(d.data);
15659             }
15660             
15661             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15662             
15663         }, this);
15664         
15665         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15666         
15667         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15668
15669         if(this.modalTitle.length){
15670             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15671         }
15672
15673         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15674         
15675         if(this.mobile_restrict_height && listHeight < bodyHeight){
15676             this.touchViewBodyEl.setHeight(listHeight);
15677         }
15678         
15679         var _this = this;
15680         
15681         if(firstChecked && listHeight > bodyHeight){
15682             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15683         }
15684         
15685     },
15686     
15687     onTouchViewLoadException : function()
15688     {
15689         this.hideTouchView();
15690     },
15691     
15692     onTouchViewEmptyResults : function()
15693     {
15694         this.clearTouchView();
15695         
15696         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15697         
15698         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15699         
15700     },
15701     
15702     clearTouchView : function()
15703     {
15704         this.touchViewListGroup.dom.innerHTML = '';
15705     },
15706     
15707     onTouchViewClick : function(e, el, o)
15708     {
15709         e.preventDefault();
15710         
15711         var row = o.row;
15712         var rowIndex = o.rowIndex;
15713         
15714         var r = this.store.getAt(rowIndex);
15715         
15716         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15717             
15718             if(!this.multiple){
15719                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15720                     c.dom.removeAttribute('checked');
15721                 }, this);
15722
15723                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15724
15725                 this.setFromData(r.data);
15726
15727                 var close = this.closeTriggerEl();
15728
15729                 if(close){
15730                     close.show();
15731                 }
15732
15733                 this.hideTouchView();
15734
15735                 this.fireEvent('select', this, r, rowIndex);
15736
15737                 return;
15738             }
15739
15740             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15741                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15742                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15743                 return;
15744             }
15745
15746             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15747             this.addItem(r.data);
15748             this.tickItems.push(r.data);
15749         }
15750     },
15751     
15752     getAutoCreateNativeIOS : function()
15753     {
15754         var cfg = {
15755             cls: 'form-group' //input-group,
15756         };
15757         
15758         var combobox =  {
15759             tag: 'select',
15760             cls : 'roo-ios-select'
15761         };
15762         
15763         if (this.name) {
15764             combobox.name = this.name;
15765         }
15766         
15767         if (this.disabled) {
15768             combobox.disabled = true;
15769         }
15770         
15771         var settings = this;
15772         
15773         ['xs','sm','md','lg'].map(function(size){
15774             if (settings[size]) {
15775                 cfg.cls += ' col-' + size + '-' + settings[size];
15776             }
15777         });
15778         
15779         cfg.cn = combobox;
15780         
15781         return cfg;
15782         
15783     },
15784     
15785     initIOSView : function()
15786     {
15787         this.store.on('load', this.onIOSViewLoad, this);
15788         
15789         return;
15790     },
15791     
15792     onIOSViewLoad : function()
15793     {
15794         if(this.store.getCount() < 1){
15795             return;
15796         }
15797         
15798         this.clearIOSView();
15799         
15800         if(this.allowBlank) {
15801             
15802             var default_text = '-- SELECT --';
15803             
15804             if(this.placeholder.length){
15805                 default_text = this.placeholder;
15806             }
15807             
15808             if(this.emptyTitle.length){
15809                 default_text += ' - ' + this.emptyTitle + ' -';
15810             }
15811             
15812             var opt = this.inputEl().createChild({
15813                 tag: 'option',
15814                 value : 0,
15815                 html : default_text
15816             });
15817             
15818             var o = {};
15819             o[this.valueField] = 0;
15820             o[this.displayField] = default_text;
15821             
15822             this.ios_options.push({
15823                 data : o,
15824                 el : opt
15825             });
15826             
15827         }
15828         
15829         this.store.data.each(function(d, rowIndex){
15830             
15831             var html = '';
15832             
15833             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15834                 html = d.data[this.displayField];
15835             }
15836             
15837             var value = '';
15838             
15839             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15840                 value = d.data[this.valueField];
15841             }
15842             
15843             var option = {
15844                 tag: 'option',
15845                 value : value,
15846                 html : html
15847             };
15848             
15849             if(this.value == d.data[this.valueField]){
15850                 option['selected'] = true;
15851             }
15852             
15853             var opt = this.inputEl().createChild(option);
15854             
15855             this.ios_options.push({
15856                 data : d.data,
15857                 el : opt
15858             });
15859             
15860         }, this);
15861         
15862         this.inputEl().on('change', function(){
15863            this.fireEvent('select', this);
15864         }, this);
15865         
15866     },
15867     
15868     clearIOSView: function()
15869     {
15870         this.inputEl().dom.innerHTML = '';
15871         
15872         this.ios_options = [];
15873     },
15874     
15875     setIOSValue: function(v)
15876     {
15877         this.value = v;
15878         
15879         if(!this.ios_options){
15880             return;
15881         }
15882         
15883         Roo.each(this.ios_options, function(opts){
15884            
15885            opts.el.dom.removeAttribute('selected');
15886            
15887            if(opts.data[this.valueField] != v){
15888                return;
15889            }
15890            
15891            opts.el.dom.setAttribute('selected', true);
15892            
15893         }, this);
15894     }
15895
15896     /** 
15897     * @cfg {Boolean} grow 
15898     * @hide 
15899     */
15900     /** 
15901     * @cfg {Number} growMin 
15902     * @hide 
15903     */
15904     /** 
15905     * @cfg {Number} growMax 
15906     * @hide 
15907     */
15908     /**
15909      * @hide
15910      * @method autoSize
15911      */
15912 });
15913
15914 Roo.apply(Roo.bootstrap.ComboBox,  {
15915     
15916     header : {
15917         tag: 'div',
15918         cls: 'modal-header',
15919         cn: [
15920             {
15921                 tag: 'h4',
15922                 cls: 'modal-title'
15923             }
15924         ]
15925     },
15926     
15927     body : {
15928         tag: 'div',
15929         cls: 'modal-body',
15930         cn: [
15931             {
15932                 tag: 'ul',
15933                 cls: 'list-group'
15934             }
15935         ]
15936     },
15937     
15938     listItemRadio : {
15939         tag: 'li',
15940         cls: 'list-group-item',
15941         cn: [
15942             {
15943                 tag: 'span',
15944                 cls: 'roo-combobox-list-group-item-value'
15945             },
15946             {
15947                 tag: 'div',
15948                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15949                 cn: [
15950                     {
15951                         tag: 'input',
15952                         type: 'radio'
15953                     },
15954                     {
15955                         tag: 'label'
15956                     }
15957                 ]
15958             }
15959         ]
15960     },
15961     
15962     listItemCheckbox : {
15963         tag: 'li',
15964         cls: 'list-group-item',
15965         cn: [
15966             {
15967                 tag: 'span',
15968                 cls: 'roo-combobox-list-group-item-value'
15969             },
15970             {
15971                 tag: 'div',
15972                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15973                 cn: [
15974                     {
15975                         tag: 'input',
15976                         type: 'checkbox'
15977                     },
15978                     {
15979                         tag: 'label'
15980                     }
15981                 ]
15982             }
15983         ]
15984     },
15985     
15986     emptyResult : {
15987         tag: 'div',
15988         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15989     },
15990     
15991     footer : {
15992         tag: 'div',
15993         cls: 'modal-footer',
15994         cn: [
15995             {
15996                 tag: 'div',
15997                 cls: 'row',
15998                 cn: [
15999                     {
16000                         tag: 'div',
16001                         cls: 'col-xs-6 text-left',
16002                         cn: {
16003                             tag: 'button',
16004                             cls: 'btn btn-danger roo-touch-view-cancel',
16005                             html: 'Cancel'
16006                         }
16007                     },
16008                     {
16009                         tag: 'div',
16010                         cls: 'col-xs-6 text-right',
16011                         cn: {
16012                             tag: 'button',
16013                             cls: 'btn btn-success roo-touch-view-ok',
16014                             html: 'OK'
16015                         }
16016                     }
16017                 ]
16018             }
16019         ]
16020         
16021     }
16022 });
16023
16024 Roo.apply(Roo.bootstrap.ComboBox,  {
16025     
16026     touchViewTemplate : {
16027         tag: 'div',
16028         cls: 'modal fade roo-combobox-touch-view',
16029         cn: [
16030             {
16031                 tag: 'div',
16032                 cls: 'modal-dialog',
16033                 style : 'position:fixed', // we have to fix position....
16034                 cn: [
16035                     {
16036                         tag: 'div',
16037                         cls: 'modal-content',
16038                         cn: [
16039                             Roo.bootstrap.ComboBox.header,
16040                             Roo.bootstrap.ComboBox.body,
16041                             Roo.bootstrap.ComboBox.footer
16042                         ]
16043                     }
16044                 ]
16045             }
16046         ]
16047     }
16048 });/*
16049  * Based on:
16050  * Ext JS Library 1.1.1
16051  * Copyright(c) 2006-2007, Ext JS, LLC.
16052  *
16053  * Originally Released Under LGPL - original licence link has changed is not relivant.
16054  *
16055  * Fork - LGPL
16056  * <script type="text/javascript">
16057  */
16058
16059 /**
16060  * @class Roo.View
16061  * @extends Roo.util.Observable
16062  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16063  * This class also supports single and multi selection modes. <br>
16064  * Create a data model bound view:
16065  <pre><code>
16066  var store = new Roo.data.Store(...);
16067
16068  var view = new Roo.View({
16069     el : "my-element",
16070     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16071  
16072     singleSelect: true,
16073     selectedClass: "ydataview-selected",
16074     store: store
16075  });
16076
16077  // listen for node click?
16078  view.on("click", function(vw, index, node, e){
16079  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16080  });
16081
16082  // load XML data
16083  dataModel.load("foobar.xml");
16084  </code></pre>
16085  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16086  * <br><br>
16087  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16088  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16089  * 
16090  * Note: old style constructor is still suported (container, template, config)
16091  * 
16092  * @constructor
16093  * Create a new View
16094  * @param {Object} config The config object
16095  * 
16096  */
16097 Roo.View = function(config, depreciated_tpl, depreciated_config){
16098     
16099     this.parent = false;
16100     
16101     if (typeof(depreciated_tpl) == 'undefined') {
16102         // new way.. - universal constructor.
16103         Roo.apply(this, config);
16104         this.el  = Roo.get(this.el);
16105     } else {
16106         // old format..
16107         this.el  = Roo.get(config);
16108         this.tpl = depreciated_tpl;
16109         Roo.apply(this, depreciated_config);
16110     }
16111     this.wrapEl  = this.el.wrap().wrap();
16112     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16113     
16114     
16115     if(typeof(this.tpl) == "string"){
16116         this.tpl = new Roo.Template(this.tpl);
16117     } else {
16118         // support xtype ctors..
16119         this.tpl = new Roo.factory(this.tpl, Roo);
16120     }
16121     
16122     
16123     this.tpl.compile();
16124     
16125     /** @private */
16126     this.addEvents({
16127         /**
16128          * @event beforeclick
16129          * Fires before a click is processed. Returns false to cancel the default action.
16130          * @param {Roo.View} this
16131          * @param {Number} index The index of the target node
16132          * @param {HTMLElement} node The target node
16133          * @param {Roo.EventObject} e The raw event object
16134          */
16135             "beforeclick" : true,
16136         /**
16137          * @event click
16138          * Fires when a template node is clicked.
16139          * @param {Roo.View} this
16140          * @param {Number} index The index of the target node
16141          * @param {HTMLElement} node The target node
16142          * @param {Roo.EventObject} e The raw event object
16143          */
16144             "click" : true,
16145         /**
16146          * @event dblclick
16147          * Fires when a template node is double clicked.
16148          * @param {Roo.View} this
16149          * @param {Number} index The index of the target node
16150          * @param {HTMLElement} node The target node
16151          * @param {Roo.EventObject} e The raw event object
16152          */
16153             "dblclick" : true,
16154         /**
16155          * @event contextmenu
16156          * Fires when a template node is right clicked.
16157          * @param {Roo.View} this
16158          * @param {Number} index The index of the target node
16159          * @param {HTMLElement} node The target node
16160          * @param {Roo.EventObject} e The raw event object
16161          */
16162             "contextmenu" : true,
16163         /**
16164          * @event selectionchange
16165          * Fires when the selected nodes change.
16166          * @param {Roo.View} this
16167          * @param {Array} selections Array of the selected nodes
16168          */
16169             "selectionchange" : true,
16170     
16171         /**
16172          * @event beforeselect
16173          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16174          * @param {Roo.View} this
16175          * @param {HTMLElement} node The node to be selected
16176          * @param {Array} selections Array of currently selected nodes
16177          */
16178             "beforeselect" : true,
16179         /**
16180          * @event preparedata
16181          * Fires on every row to render, to allow you to change the data.
16182          * @param {Roo.View} this
16183          * @param {Object} data to be rendered (change this)
16184          */
16185           "preparedata" : true
16186           
16187           
16188         });
16189
16190
16191
16192     this.el.on({
16193         "click": this.onClick,
16194         "dblclick": this.onDblClick,
16195         "contextmenu": this.onContextMenu,
16196         scope:this
16197     });
16198
16199     this.selections = [];
16200     this.nodes = [];
16201     this.cmp = new Roo.CompositeElementLite([]);
16202     if(this.store){
16203         this.store = Roo.factory(this.store, Roo.data);
16204         this.setStore(this.store, true);
16205     }
16206     
16207     if ( this.footer && this.footer.xtype) {
16208            
16209          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16210         
16211         this.footer.dataSource = this.store;
16212         this.footer.container = fctr;
16213         this.footer = Roo.factory(this.footer, Roo);
16214         fctr.insertFirst(this.el);
16215         
16216         // this is a bit insane - as the paging toolbar seems to detach the el..
16217 //        dom.parentNode.parentNode.parentNode
16218          // they get detached?
16219     }
16220     
16221     
16222     Roo.View.superclass.constructor.call(this);
16223     
16224     
16225 };
16226
16227 Roo.extend(Roo.View, Roo.util.Observable, {
16228     
16229      /**
16230      * @cfg {Roo.data.Store} store Data store to load data from.
16231      */
16232     store : false,
16233     
16234     /**
16235      * @cfg {String|Roo.Element} el The container element.
16236      */
16237     el : '',
16238     
16239     /**
16240      * @cfg {String|Roo.Template} tpl The template used by this View 
16241      */
16242     tpl : false,
16243     /**
16244      * @cfg {String} dataName the named area of the template to use as the data area
16245      *                          Works with domtemplates roo-name="name"
16246      */
16247     dataName: false,
16248     /**
16249      * @cfg {String} selectedClass The css class to add to selected nodes
16250      */
16251     selectedClass : "x-view-selected",
16252      /**
16253      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16254      */
16255     emptyText : "",
16256     
16257     /**
16258      * @cfg {String} text to display on mask (default Loading)
16259      */
16260     mask : false,
16261     /**
16262      * @cfg {Boolean} multiSelect Allow multiple selection
16263      */
16264     multiSelect : false,
16265     /**
16266      * @cfg {Boolean} singleSelect Allow single selection
16267      */
16268     singleSelect:  false,
16269     
16270     /**
16271      * @cfg {Boolean} toggleSelect - selecting 
16272      */
16273     toggleSelect : false,
16274     
16275     /**
16276      * @cfg {Boolean} tickable - selecting 
16277      */
16278     tickable : false,
16279     
16280     /**
16281      * Returns the element this view is bound to.
16282      * @return {Roo.Element}
16283      */
16284     getEl : function(){
16285         return this.wrapEl;
16286     },
16287     
16288     
16289
16290     /**
16291      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16292      */
16293     refresh : function(){
16294         //Roo.log('refresh');
16295         var t = this.tpl;
16296         
16297         // if we are using something like 'domtemplate', then
16298         // the what gets used is:
16299         // t.applySubtemplate(NAME, data, wrapping data..)
16300         // the outer template then get' applied with
16301         //     the store 'extra data'
16302         // and the body get's added to the
16303         //      roo-name="data" node?
16304         //      <span class='roo-tpl-{name}'></span> ?????
16305         
16306         
16307         
16308         this.clearSelections();
16309         this.el.update("");
16310         var html = [];
16311         var records = this.store.getRange();
16312         if(records.length < 1) {
16313             
16314             // is this valid??  = should it render a template??
16315             
16316             this.el.update(this.emptyText);
16317             return;
16318         }
16319         var el = this.el;
16320         if (this.dataName) {
16321             this.el.update(t.apply(this.store.meta)); //????
16322             el = this.el.child('.roo-tpl-' + this.dataName);
16323         }
16324         
16325         for(var i = 0, len = records.length; i < len; i++){
16326             var data = this.prepareData(records[i].data, i, records[i]);
16327             this.fireEvent("preparedata", this, data, i, records[i]);
16328             
16329             var d = Roo.apply({}, data);
16330             
16331             if(this.tickable){
16332                 Roo.apply(d, {'roo-id' : Roo.id()});
16333                 
16334                 var _this = this;
16335             
16336                 Roo.each(this.parent.item, function(item){
16337                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16338                         return;
16339                     }
16340                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16341                 });
16342             }
16343             
16344             html[html.length] = Roo.util.Format.trim(
16345                 this.dataName ?
16346                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16347                     t.apply(d)
16348             );
16349         }
16350         
16351         
16352         
16353         el.update(html.join(""));
16354         this.nodes = el.dom.childNodes;
16355         this.updateIndexes(0);
16356     },
16357     
16358
16359     /**
16360      * Function to override to reformat the data that is sent to
16361      * the template for each node.
16362      * DEPRICATED - use the preparedata event handler.
16363      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16364      * a JSON object for an UpdateManager bound view).
16365      */
16366     prepareData : function(data, index, record)
16367     {
16368         this.fireEvent("preparedata", this, data, index, record);
16369         return data;
16370     },
16371
16372     onUpdate : function(ds, record){
16373         // Roo.log('on update');   
16374         this.clearSelections();
16375         var index = this.store.indexOf(record);
16376         var n = this.nodes[index];
16377         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16378         n.parentNode.removeChild(n);
16379         this.updateIndexes(index, index);
16380     },
16381
16382     
16383     
16384 // --------- FIXME     
16385     onAdd : function(ds, records, index)
16386     {
16387         //Roo.log(['on Add', ds, records, index] );        
16388         this.clearSelections();
16389         if(this.nodes.length == 0){
16390             this.refresh();
16391             return;
16392         }
16393         var n = this.nodes[index];
16394         for(var i = 0, len = records.length; i < len; i++){
16395             var d = this.prepareData(records[i].data, i, records[i]);
16396             if(n){
16397                 this.tpl.insertBefore(n, d);
16398             }else{
16399                 
16400                 this.tpl.append(this.el, d);
16401             }
16402         }
16403         this.updateIndexes(index);
16404     },
16405
16406     onRemove : function(ds, record, index){
16407        // Roo.log('onRemove');
16408         this.clearSelections();
16409         var el = this.dataName  ?
16410             this.el.child('.roo-tpl-' + this.dataName) :
16411             this.el; 
16412         
16413         el.dom.removeChild(this.nodes[index]);
16414         this.updateIndexes(index);
16415     },
16416
16417     /**
16418      * Refresh an individual node.
16419      * @param {Number} index
16420      */
16421     refreshNode : function(index){
16422         this.onUpdate(this.store, this.store.getAt(index));
16423     },
16424
16425     updateIndexes : function(startIndex, endIndex){
16426         var ns = this.nodes;
16427         startIndex = startIndex || 0;
16428         endIndex = endIndex || ns.length - 1;
16429         for(var i = startIndex; i <= endIndex; i++){
16430             ns[i].nodeIndex = i;
16431         }
16432     },
16433
16434     /**
16435      * Changes the data store this view uses and refresh the view.
16436      * @param {Store} store
16437      */
16438     setStore : function(store, initial){
16439         if(!initial && this.store){
16440             this.store.un("datachanged", this.refresh);
16441             this.store.un("add", this.onAdd);
16442             this.store.un("remove", this.onRemove);
16443             this.store.un("update", this.onUpdate);
16444             this.store.un("clear", this.refresh);
16445             this.store.un("beforeload", this.onBeforeLoad);
16446             this.store.un("load", this.onLoad);
16447             this.store.un("loadexception", this.onLoad);
16448         }
16449         if(store){
16450           
16451             store.on("datachanged", this.refresh, this);
16452             store.on("add", this.onAdd, this);
16453             store.on("remove", this.onRemove, this);
16454             store.on("update", this.onUpdate, this);
16455             store.on("clear", this.refresh, this);
16456             store.on("beforeload", this.onBeforeLoad, this);
16457             store.on("load", this.onLoad, this);
16458             store.on("loadexception", this.onLoad, this);
16459         }
16460         
16461         if(store){
16462             this.refresh();
16463         }
16464     },
16465     /**
16466      * onbeforeLoad - masks the loading area.
16467      *
16468      */
16469     onBeforeLoad : function(store,opts)
16470     {
16471          //Roo.log('onBeforeLoad');   
16472         if (!opts.add) {
16473             this.el.update("");
16474         }
16475         this.el.mask(this.mask ? this.mask : "Loading" ); 
16476     },
16477     onLoad : function ()
16478     {
16479         this.el.unmask();
16480     },
16481     
16482
16483     /**
16484      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16485      * @param {HTMLElement} node
16486      * @return {HTMLElement} The template node
16487      */
16488     findItemFromChild : function(node){
16489         var el = this.dataName  ?
16490             this.el.child('.roo-tpl-' + this.dataName,true) :
16491             this.el.dom; 
16492         
16493         if(!node || node.parentNode == el){
16494                     return node;
16495             }
16496             var p = node.parentNode;
16497             while(p && p != el){
16498             if(p.parentNode == el){
16499                 return p;
16500             }
16501             p = p.parentNode;
16502         }
16503             return null;
16504     },
16505
16506     /** @ignore */
16507     onClick : function(e){
16508         var item = this.findItemFromChild(e.getTarget());
16509         if(item){
16510             var index = this.indexOf(item);
16511             if(this.onItemClick(item, index, e) !== false){
16512                 this.fireEvent("click", this, index, item, e);
16513             }
16514         }else{
16515             this.clearSelections();
16516         }
16517     },
16518
16519     /** @ignore */
16520     onContextMenu : function(e){
16521         var item = this.findItemFromChild(e.getTarget());
16522         if(item){
16523             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16524         }
16525     },
16526
16527     /** @ignore */
16528     onDblClick : function(e){
16529         var item = this.findItemFromChild(e.getTarget());
16530         if(item){
16531             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16532         }
16533     },
16534
16535     onItemClick : function(item, index, e)
16536     {
16537         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16538             return false;
16539         }
16540         if (this.toggleSelect) {
16541             var m = this.isSelected(item) ? 'unselect' : 'select';
16542             //Roo.log(m);
16543             var _t = this;
16544             _t[m](item, true, false);
16545             return true;
16546         }
16547         if(this.multiSelect || this.singleSelect){
16548             if(this.multiSelect && e.shiftKey && this.lastSelection){
16549                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16550             }else{
16551                 this.select(item, this.multiSelect && e.ctrlKey);
16552                 this.lastSelection = item;
16553             }
16554             
16555             if(!this.tickable){
16556                 e.preventDefault();
16557             }
16558             
16559         }
16560         return true;
16561     },
16562
16563     /**
16564      * Get the number of selected nodes.
16565      * @return {Number}
16566      */
16567     getSelectionCount : function(){
16568         return this.selections.length;
16569     },
16570
16571     /**
16572      * Get the currently selected nodes.
16573      * @return {Array} An array of HTMLElements
16574      */
16575     getSelectedNodes : function(){
16576         return this.selections;
16577     },
16578
16579     /**
16580      * Get the indexes of the selected nodes.
16581      * @return {Array}
16582      */
16583     getSelectedIndexes : function(){
16584         var indexes = [], s = this.selections;
16585         for(var i = 0, len = s.length; i < len; i++){
16586             indexes.push(s[i].nodeIndex);
16587         }
16588         return indexes;
16589     },
16590
16591     /**
16592      * Clear all selections
16593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16594      */
16595     clearSelections : function(suppressEvent){
16596         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16597             this.cmp.elements = this.selections;
16598             this.cmp.removeClass(this.selectedClass);
16599             this.selections = [];
16600             if(!suppressEvent){
16601                 this.fireEvent("selectionchange", this, this.selections);
16602             }
16603         }
16604     },
16605
16606     /**
16607      * Returns true if the passed node is selected
16608      * @param {HTMLElement/Number} node The node or node index
16609      * @return {Boolean}
16610      */
16611     isSelected : function(node){
16612         var s = this.selections;
16613         if(s.length < 1){
16614             return false;
16615         }
16616         node = this.getNode(node);
16617         return s.indexOf(node) !== -1;
16618     },
16619
16620     /**
16621      * Selects nodes.
16622      * @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
16623      * @param {Boolean} keepExisting (optional) true to keep existing selections
16624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16625      */
16626     select : function(nodeInfo, keepExisting, suppressEvent){
16627         if(nodeInfo instanceof Array){
16628             if(!keepExisting){
16629                 this.clearSelections(true);
16630             }
16631             for(var i = 0, len = nodeInfo.length; i < len; i++){
16632                 this.select(nodeInfo[i], true, true);
16633             }
16634             return;
16635         } 
16636         var node = this.getNode(nodeInfo);
16637         if(!node || this.isSelected(node)){
16638             return; // already selected.
16639         }
16640         if(!keepExisting){
16641             this.clearSelections(true);
16642         }
16643         
16644         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16645             Roo.fly(node).addClass(this.selectedClass);
16646             this.selections.push(node);
16647             if(!suppressEvent){
16648                 this.fireEvent("selectionchange", this, this.selections);
16649             }
16650         }
16651         
16652         
16653     },
16654       /**
16655      * Unselects nodes.
16656      * @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
16657      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16658      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16659      */
16660     unselect : function(nodeInfo, keepExisting, suppressEvent)
16661     {
16662         if(nodeInfo instanceof Array){
16663             Roo.each(this.selections, function(s) {
16664                 this.unselect(s, nodeInfo);
16665             }, this);
16666             return;
16667         }
16668         var node = this.getNode(nodeInfo);
16669         if(!node || !this.isSelected(node)){
16670             //Roo.log("not selected");
16671             return; // not selected.
16672         }
16673         // fireevent???
16674         var ns = [];
16675         Roo.each(this.selections, function(s) {
16676             if (s == node ) {
16677                 Roo.fly(node).removeClass(this.selectedClass);
16678
16679                 return;
16680             }
16681             ns.push(s);
16682         },this);
16683         
16684         this.selections= ns;
16685         this.fireEvent("selectionchange", this, this.selections);
16686     },
16687
16688     /**
16689      * Gets a template node.
16690      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16691      * @return {HTMLElement} The node or null if it wasn't found
16692      */
16693     getNode : function(nodeInfo){
16694         if(typeof nodeInfo == "string"){
16695             return document.getElementById(nodeInfo);
16696         }else if(typeof nodeInfo == "number"){
16697             return this.nodes[nodeInfo];
16698         }
16699         return nodeInfo;
16700     },
16701
16702     /**
16703      * Gets a range template nodes.
16704      * @param {Number} startIndex
16705      * @param {Number} endIndex
16706      * @return {Array} An array of nodes
16707      */
16708     getNodes : function(start, end){
16709         var ns = this.nodes;
16710         start = start || 0;
16711         end = typeof end == "undefined" ? ns.length - 1 : end;
16712         var nodes = [];
16713         if(start <= end){
16714             for(var i = start; i <= end; i++){
16715                 nodes.push(ns[i]);
16716             }
16717         } else{
16718             for(var i = start; i >= end; i--){
16719                 nodes.push(ns[i]);
16720             }
16721         }
16722         return nodes;
16723     },
16724
16725     /**
16726      * Finds the index of the passed node
16727      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16728      * @return {Number} The index of the node or -1
16729      */
16730     indexOf : function(node){
16731         node = this.getNode(node);
16732         if(typeof node.nodeIndex == "number"){
16733             return node.nodeIndex;
16734         }
16735         var ns = this.nodes;
16736         for(var i = 0, len = ns.length; i < len; i++){
16737             if(ns[i] == node){
16738                 return i;
16739             }
16740         }
16741         return -1;
16742     }
16743 });
16744 /*
16745  * - LGPL
16746  *
16747  * based on jquery fullcalendar
16748  * 
16749  */
16750
16751 Roo.bootstrap = Roo.bootstrap || {};
16752 /**
16753  * @class Roo.bootstrap.Calendar
16754  * @extends Roo.bootstrap.Component
16755  * Bootstrap Calendar class
16756  * @cfg {Boolean} loadMask (true|false) default false
16757  * @cfg {Object} header generate the user specific header of the calendar, default false
16758
16759  * @constructor
16760  * Create a new Container
16761  * @param {Object} config The config object
16762  */
16763
16764
16765
16766 Roo.bootstrap.Calendar = function(config){
16767     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16768      this.addEvents({
16769         /**
16770              * @event select
16771              * Fires when a date is selected
16772              * @param {DatePicker} this
16773              * @param {Date} date The selected date
16774              */
16775         'select': true,
16776         /**
16777              * @event monthchange
16778              * Fires when the displayed month changes 
16779              * @param {DatePicker} this
16780              * @param {Date} date The selected month
16781              */
16782         'monthchange': true,
16783         /**
16784              * @event evententer
16785              * Fires when mouse over an event
16786              * @param {Calendar} this
16787              * @param {event} Event
16788              */
16789         'evententer': true,
16790         /**
16791              * @event eventleave
16792              * Fires when the mouse leaves an
16793              * @param {Calendar} this
16794              * @param {event}
16795              */
16796         'eventleave': true,
16797         /**
16798              * @event eventclick
16799              * Fires when the mouse click an
16800              * @param {Calendar} this
16801              * @param {event}
16802              */
16803         'eventclick': true
16804         
16805     });
16806
16807 };
16808
16809 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16810     
16811      /**
16812      * @cfg {Number} startDay
16813      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16814      */
16815     startDay : 0,
16816     
16817     loadMask : false,
16818     
16819     header : false,
16820       
16821     getAutoCreate : function(){
16822         
16823         
16824         var fc_button = function(name, corner, style, content ) {
16825             return Roo.apply({},{
16826                 tag : 'span',
16827                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16828                          (corner.length ?
16829                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16830                             ''
16831                         ),
16832                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16833                 unselectable: 'on'
16834             });
16835         };
16836         
16837         var header = {};
16838         
16839         if(!this.header){
16840             header = {
16841                 tag : 'table',
16842                 cls : 'fc-header',
16843                 style : 'width:100%',
16844                 cn : [
16845                     {
16846                         tag: 'tr',
16847                         cn : [
16848                             {
16849                                 tag : 'td',
16850                                 cls : 'fc-header-left',
16851                                 cn : [
16852                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16853                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16854                                     { tag: 'span', cls: 'fc-header-space' },
16855                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16856
16857
16858                                 ]
16859                             },
16860
16861                             {
16862                                 tag : 'td',
16863                                 cls : 'fc-header-center',
16864                                 cn : [
16865                                     {
16866                                         tag: 'span',
16867                                         cls: 'fc-header-title',
16868                                         cn : {
16869                                             tag: 'H2',
16870                                             html : 'month / year'
16871                                         }
16872                                     }
16873
16874                                 ]
16875                             },
16876                             {
16877                                 tag : 'td',
16878                                 cls : 'fc-header-right',
16879                                 cn : [
16880                               /*      fc_button('month', 'left', '', 'month' ),
16881                                     fc_button('week', '', '', 'week' ),
16882                                     fc_button('day', 'right', '', 'day' )
16883                                 */    
16884
16885                                 ]
16886                             }
16887
16888                         ]
16889                     }
16890                 ]
16891             };
16892         }
16893         
16894         header = this.header;
16895         
16896        
16897         var cal_heads = function() {
16898             var ret = [];
16899             // fixme - handle this.
16900             
16901             for (var i =0; i < Date.dayNames.length; i++) {
16902                 var d = Date.dayNames[i];
16903                 ret.push({
16904                     tag: 'th',
16905                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16906                     html : d.substring(0,3)
16907                 });
16908                 
16909             }
16910             ret[0].cls += ' fc-first';
16911             ret[6].cls += ' fc-last';
16912             return ret;
16913         };
16914         var cal_cell = function(n) {
16915             return  {
16916                 tag: 'td',
16917                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16918                 cn : [
16919                     {
16920                         cn : [
16921                             {
16922                                 cls: 'fc-day-number',
16923                                 html: 'D'
16924                             },
16925                             {
16926                                 cls: 'fc-day-content',
16927                              
16928                                 cn : [
16929                                      {
16930                                         style: 'position: relative;' // height: 17px;
16931                                     }
16932                                 ]
16933                             }
16934                             
16935                             
16936                         ]
16937                     }
16938                 ]
16939                 
16940             }
16941         };
16942         var cal_rows = function() {
16943             
16944             var ret = [];
16945             for (var r = 0; r < 6; r++) {
16946                 var row= {
16947                     tag : 'tr',
16948                     cls : 'fc-week',
16949                     cn : []
16950                 };
16951                 
16952                 for (var i =0; i < Date.dayNames.length; i++) {
16953                     var d = Date.dayNames[i];
16954                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16955
16956                 }
16957                 row.cn[0].cls+=' fc-first';
16958                 row.cn[0].cn[0].style = 'min-height:90px';
16959                 row.cn[6].cls+=' fc-last';
16960                 ret.push(row);
16961                 
16962             }
16963             ret[0].cls += ' fc-first';
16964             ret[4].cls += ' fc-prev-last';
16965             ret[5].cls += ' fc-last';
16966             return ret;
16967             
16968         };
16969         
16970         var cal_table = {
16971             tag: 'table',
16972             cls: 'fc-border-separate',
16973             style : 'width:100%',
16974             cellspacing  : 0,
16975             cn : [
16976                 { 
16977                     tag: 'thead',
16978                     cn : [
16979                         { 
16980                             tag: 'tr',
16981                             cls : 'fc-first fc-last',
16982                             cn : cal_heads()
16983                         }
16984                     ]
16985                 },
16986                 { 
16987                     tag: 'tbody',
16988                     cn : cal_rows()
16989                 }
16990                   
16991             ]
16992         };
16993          
16994          var cfg = {
16995             cls : 'fc fc-ltr',
16996             cn : [
16997                 header,
16998                 {
16999                     cls : 'fc-content',
17000                     style : "position: relative;",
17001                     cn : [
17002                         {
17003                             cls : 'fc-view fc-view-month fc-grid',
17004                             style : 'position: relative',
17005                             unselectable : 'on',
17006                             cn : [
17007                                 {
17008                                     cls : 'fc-event-container',
17009                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17010                                 },
17011                                 cal_table
17012                             ]
17013                         }
17014                     ]
17015     
17016                 }
17017            ] 
17018             
17019         };
17020         
17021          
17022         
17023         return cfg;
17024     },
17025     
17026     
17027     initEvents : function()
17028     {
17029         if(!this.store){
17030             throw "can not find store for calendar";
17031         }
17032         
17033         var mark = {
17034             tag: "div",
17035             cls:"x-dlg-mask",
17036             style: "text-align:center",
17037             cn: [
17038                 {
17039                     tag: "div",
17040                     style: "background-color:white;width:50%;margin:250 auto",
17041                     cn: [
17042                         {
17043                             tag: "img",
17044                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17045                         },
17046                         {
17047                             tag: "span",
17048                             html: "Loading"
17049                         }
17050                         
17051                     ]
17052                 }
17053             ]
17054         };
17055         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17056         
17057         var size = this.el.select('.fc-content', true).first().getSize();
17058         this.maskEl.setSize(size.width, size.height);
17059         this.maskEl.enableDisplayMode("block");
17060         if(!this.loadMask){
17061             this.maskEl.hide();
17062         }
17063         
17064         this.store = Roo.factory(this.store, Roo.data);
17065         this.store.on('load', this.onLoad, this);
17066         this.store.on('beforeload', this.onBeforeLoad, this);
17067         
17068         this.resize();
17069         
17070         this.cells = this.el.select('.fc-day',true);
17071         //Roo.log(this.cells);
17072         this.textNodes = this.el.query('.fc-day-number');
17073         this.cells.addClassOnOver('fc-state-hover');
17074         
17075         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17076         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17077         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17078         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17079         
17080         this.on('monthchange', this.onMonthChange, this);
17081         
17082         this.update(new Date().clearTime());
17083     },
17084     
17085     resize : function() {
17086         var sz  = this.el.getSize();
17087         
17088         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17089         this.el.select('.fc-day-content div',true).setHeight(34);
17090     },
17091     
17092     
17093     // private
17094     showPrevMonth : function(e){
17095         this.update(this.activeDate.add("mo", -1));
17096     },
17097     showToday : function(e){
17098         this.update(new Date().clearTime());
17099     },
17100     // private
17101     showNextMonth : function(e){
17102         this.update(this.activeDate.add("mo", 1));
17103     },
17104
17105     // private
17106     showPrevYear : function(){
17107         this.update(this.activeDate.add("y", -1));
17108     },
17109
17110     // private
17111     showNextYear : function(){
17112         this.update(this.activeDate.add("y", 1));
17113     },
17114
17115     
17116    // private
17117     update : function(date)
17118     {
17119         var vd = this.activeDate;
17120         this.activeDate = date;
17121 //        if(vd && this.el){
17122 //            var t = date.getTime();
17123 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17124 //                Roo.log('using add remove');
17125 //                
17126 //                this.fireEvent('monthchange', this, date);
17127 //                
17128 //                this.cells.removeClass("fc-state-highlight");
17129 //                this.cells.each(function(c){
17130 //                   if(c.dateValue == t){
17131 //                       c.addClass("fc-state-highlight");
17132 //                       setTimeout(function(){
17133 //                            try{c.dom.firstChild.focus();}catch(e){}
17134 //                       }, 50);
17135 //                       return false;
17136 //                   }
17137 //                   return true;
17138 //                });
17139 //                return;
17140 //            }
17141 //        }
17142         
17143         var days = date.getDaysInMonth();
17144         
17145         var firstOfMonth = date.getFirstDateOfMonth();
17146         var startingPos = firstOfMonth.getDay()-this.startDay;
17147         
17148         if(startingPos < this.startDay){
17149             startingPos += 7;
17150         }
17151         
17152         var pm = date.add(Date.MONTH, -1);
17153         var prevStart = pm.getDaysInMonth()-startingPos;
17154 //        
17155         this.cells = this.el.select('.fc-day',true);
17156         this.textNodes = this.el.query('.fc-day-number');
17157         this.cells.addClassOnOver('fc-state-hover');
17158         
17159         var cells = this.cells.elements;
17160         var textEls = this.textNodes;
17161         
17162         Roo.each(cells, function(cell){
17163             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17164         });
17165         
17166         days += startingPos;
17167
17168         // convert everything to numbers so it's fast
17169         var day = 86400000;
17170         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17171         //Roo.log(d);
17172         //Roo.log(pm);
17173         //Roo.log(prevStart);
17174         
17175         var today = new Date().clearTime().getTime();
17176         var sel = date.clearTime().getTime();
17177         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17178         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17179         var ddMatch = this.disabledDatesRE;
17180         var ddText = this.disabledDatesText;
17181         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17182         var ddaysText = this.disabledDaysText;
17183         var format = this.format;
17184         
17185         var setCellClass = function(cal, cell){
17186             cell.row = 0;
17187             cell.events = [];
17188             cell.more = [];
17189             //Roo.log('set Cell Class');
17190             cell.title = "";
17191             var t = d.getTime();
17192             
17193             //Roo.log(d);
17194             
17195             cell.dateValue = t;
17196             if(t == today){
17197                 cell.className += " fc-today";
17198                 cell.className += " fc-state-highlight";
17199                 cell.title = cal.todayText;
17200             }
17201             if(t == sel){
17202                 // disable highlight in other month..
17203                 //cell.className += " fc-state-highlight";
17204                 
17205             }
17206             // disabling
17207             if(t < min) {
17208                 cell.className = " fc-state-disabled";
17209                 cell.title = cal.minText;
17210                 return;
17211             }
17212             if(t > max) {
17213                 cell.className = " fc-state-disabled";
17214                 cell.title = cal.maxText;
17215                 return;
17216             }
17217             if(ddays){
17218                 if(ddays.indexOf(d.getDay()) != -1){
17219                     cell.title = ddaysText;
17220                     cell.className = " fc-state-disabled";
17221                 }
17222             }
17223             if(ddMatch && format){
17224                 var fvalue = d.dateFormat(format);
17225                 if(ddMatch.test(fvalue)){
17226                     cell.title = ddText.replace("%0", fvalue);
17227                     cell.className = " fc-state-disabled";
17228                 }
17229             }
17230             
17231             if (!cell.initialClassName) {
17232                 cell.initialClassName = cell.dom.className;
17233             }
17234             
17235             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17236         };
17237
17238         var i = 0;
17239         
17240         for(; i < startingPos; i++) {
17241             textEls[i].innerHTML = (++prevStart);
17242             d.setDate(d.getDate()+1);
17243             
17244             cells[i].className = "fc-past fc-other-month";
17245             setCellClass(this, cells[i]);
17246         }
17247         
17248         var intDay = 0;
17249         
17250         for(; i < days; i++){
17251             intDay = i - startingPos + 1;
17252             textEls[i].innerHTML = (intDay);
17253             d.setDate(d.getDate()+1);
17254             
17255             cells[i].className = ''; // "x-date-active";
17256             setCellClass(this, cells[i]);
17257         }
17258         var extraDays = 0;
17259         
17260         for(; i < 42; i++) {
17261             textEls[i].innerHTML = (++extraDays);
17262             d.setDate(d.getDate()+1);
17263             
17264             cells[i].className = "fc-future fc-other-month";
17265             setCellClass(this, cells[i]);
17266         }
17267         
17268         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17269         
17270         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17271         
17272         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17273         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17274         
17275         if(totalRows != 6){
17276             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17277             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17278         }
17279         
17280         this.fireEvent('monthchange', this, date);
17281         
17282         
17283         /*
17284         if(!this.internalRender){
17285             var main = this.el.dom.firstChild;
17286             var w = main.offsetWidth;
17287             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17288             Roo.fly(main).setWidth(w);
17289             this.internalRender = true;
17290             // opera does not respect the auto grow header center column
17291             // then, after it gets a width opera refuses to recalculate
17292             // without a second pass
17293             if(Roo.isOpera && !this.secondPass){
17294                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17295                 this.secondPass = true;
17296                 this.update.defer(10, this, [date]);
17297             }
17298         }
17299         */
17300         
17301     },
17302     
17303     findCell : function(dt) {
17304         dt = dt.clearTime().getTime();
17305         var ret = false;
17306         this.cells.each(function(c){
17307             //Roo.log("check " +c.dateValue + '?=' + dt);
17308             if(c.dateValue == dt){
17309                 ret = c;
17310                 return false;
17311             }
17312             return true;
17313         });
17314         
17315         return ret;
17316     },
17317     
17318     findCells : function(ev) {
17319         var s = ev.start.clone().clearTime().getTime();
17320        // Roo.log(s);
17321         var e= ev.end.clone().clearTime().getTime();
17322        // Roo.log(e);
17323         var ret = [];
17324         this.cells.each(function(c){
17325              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17326             
17327             if(c.dateValue > e){
17328                 return ;
17329             }
17330             if(c.dateValue < s){
17331                 return ;
17332             }
17333             ret.push(c);
17334         });
17335         
17336         return ret;    
17337     },
17338     
17339 //    findBestRow: function(cells)
17340 //    {
17341 //        var ret = 0;
17342 //        
17343 //        for (var i =0 ; i < cells.length;i++) {
17344 //            ret  = Math.max(cells[i].rows || 0,ret);
17345 //        }
17346 //        return ret;
17347 //        
17348 //    },
17349     
17350     
17351     addItem : function(ev)
17352     {
17353         // look for vertical location slot in
17354         var cells = this.findCells(ev);
17355         
17356 //        ev.row = this.findBestRow(cells);
17357         
17358         // work out the location.
17359         
17360         var crow = false;
17361         var rows = [];
17362         for(var i =0; i < cells.length; i++) {
17363             
17364             cells[i].row = cells[0].row;
17365             
17366             if(i == 0){
17367                 cells[i].row = cells[i].row + 1;
17368             }
17369             
17370             if (!crow) {
17371                 crow = {
17372                     start : cells[i],
17373                     end :  cells[i]
17374                 };
17375                 continue;
17376             }
17377             if (crow.start.getY() == cells[i].getY()) {
17378                 // on same row.
17379                 crow.end = cells[i];
17380                 continue;
17381             }
17382             // different row.
17383             rows.push(crow);
17384             crow = {
17385                 start: cells[i],
17386                 end : cells[i]
17387             };
17388             
17389         }
17390         
17391         rows.push(crow);
17392         ev.els = [];
17393         ev.rows = rows;
17394         ev.cells = cells;
17395         
17396         cells[0].events.push(ev);
17397         
17398         this.calevents.push(ev);
17399     },
17400     
17401     clearEvents: function() {
17402         
17403         if(!this.calevents){
17404             return;
17405         }
17406         
17407         Roo.each(this.cells.elements, function(c){
17408             c.row = 0;
17409             c.events = [];
17410             c.more = [];
17411         });
17412         
17413         Roo.each(this.calevents, function(e) {
17414             Roo.each(e.els, function(el) {
17415                 el.un('mouseenter' ,this.onEventEnter, this);
17416                 el.un('mouseleave' ,this.onEventLeave, this);
17417                 el.remove();
17418             },this);
17419         },this);
17420         
17421         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17422             e.remove();
17423         });
17424         
17425     },
17426     
17427     renderEvents: function()
17428     {   
17429         var _this = this;
17430         
17431         this.cells.each(function(c) {
17432             
17433             if(c.row < 5){
17434                 return;
17435             }
17436             
17437             var ev = c.events;
17438             
17439             var r = 4;
17440             if(c.row != c.events.length){
17441                 r = 4 - (4 - (c.row - c.events.length));
17442             }
17443             
17444             c.events = ev.slice(0, r);
17445             c.more = ev.slice(r);
17446             
17447             if(c.more.length && c.more.length == 1){
17448                 c.events.push(c.more.pop());
17449             }
17450             
17451             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17452             
17453         });
17454             
17455         this.cells.each(function(c) {
17456             
17457             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17458             
17459             
17460             for (var e = 0; e < c.events.length; e++){
17461                 var ev = c.events[e];
17462                 var rows = ev.rows;
17463                 
17464                 for(var i = 0; i < rows.length; i++) {
17465                 
17466                     // how many rows should it span..
17467
17468                     var  cfg = {
17469                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17470                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17471
17472                         unselectable : "on",
17473                         cn : [
17474                             {
17475                                 cls: 'fc-event-inner',
17476                                 cn : [
17477     //                                {
17478     //                                  tag:'span',
17479     //                                  cls: 'fc-event-time',
17480     //                                  html : cells.length > 1 ? '' : ev.time
17481     //                                },
17482                                     {
17483                                       tag:'span',
17484                                       cls: 'fc-event-title',
17485                                       html : String.format('{0}', ev.title)
17486                                     }
17487
17488
17489                                 ]
17490                             },
17491                             {
17492                                 cls: 'ui-resizable-handle ui-resizable-e',
17493                                 html : '&nbsp;&nbsp;&nbsp'
17494                             }
17495
17496                         ]
17497                     };
17498
17499                     if (i == 0) {
17500                         cfg.cls += ' fc-event-start';
17501                     }
17502                     if ((i+1) == rows.length) {
17503                         cfg.cls += ' fc-event-end';
17504                     }
17505
17506                     var ctr = _this.el.select('.fc-event-container',true).first();
17507                     var cg = ctr.createChild(cfg);
17508
17509                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17510                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17511
17512                     var r = (c.more.length) ? 1 : 0;
17513                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17514                     cg.setWidth(ebox.right - sbox.x -2);
17515
17516                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17517                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17518                     cg.on('click', _this.onEventClick, _this, ev);
17519
17520                     ev.els.push(cg);
17521                     
17522                 }
17523                 
17524             }
17525             
17526             
17527             if(c.more.length){
17528                 var  cfg = {
17529                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17530                     style : 'position: absolute',
17531                     unselectable : "on",
17532                     cn : [
17533                         {
17534                             cls: 'fc-event-inner',
17535                             cn : [
17536                                 {
17537                                   tag:'span',
17538                                   cls: 'fc-event-title',
17539                                   html : 'More'
17540                                 }
17541
17542
17543                             ]
17544                         },
17545                         {
17546                             cls: 'ui-resizable-handle ui-resizable-e',
17547                             html : '&nbsp;&nbsp;&nbsp'
17548                         }
17549
17550                     ]
17551                 };
17552
17553                 var ctr = _this.el.select('.fc-event-container',true).first();
17554                 var cg = ctr.createChild(cfg);
17555
17556                 var sbox = c.select('.fc-day-content',true).first().getBox();
17557                 var ebox = c.select('.fc-day-content',true).first().getBox();
17558                 //Roo.log(cg);
17559                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17560                 cg.setWidth(ebox.right - sbox.x -2);
17561
17562                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17563                 
17564             }
17565             
17566         });
17567         
17568         
17569         
17570     },
17571     
17572     onEventEnter: function (e, el,event,d) {
17573         this.fireEvent('evententer', this, el, event);
17574     },
17575     
17576     onEventLeave: function (e, el,event,d) {
17577         this.fireEvent('eventleave', this, el, event);
17578     },
17579     
17580     onEventClick: function (e, el,event,d) {
17581         this.fireEvent('eventclick', this, el, event);
17582     },
17583     
17584     onMonthChange: function () {
17585         this.store.load();
17586     },
17587     
17588     onMoreEventClick: function(e, el, more)
17589     {
17590         var _this = this;
17591         
17592         this.calpopover.placement = 'right';
17593         this.calpopover.setTitle('More');
17594         
17595         this.calpopover.setContent('');
17596         
17597         var ctr = this.calpopover.el.select('.popover-content', true).first();
17598         
17599         Roo.each(more, function(m){
17600             var cfg = {
17601                 cls : 'fc-event-hori fc-event-draggable',
17602                 html : m.title
17603             };
17604             var cg = ctr.createChild(cfg);
17605             
17606             cg.on('click', _this.onEventClick, _this, m);
17607         });
17608         
17609         this.calpopover.show(el);
17610         
17611         
17612     },
17613     
17614     onLoad: function () 
17615     {   
17616         this.calevents = [];
17617         var cal = this;
17618         
17619         if(this.store.getCount() > 0){
17620             this.store.data.each(function(d){
17621                cal.addItem({
17622                     id : d.data.id,
17623                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17624                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17625                     time : d.data.start_time,
17626                     title : d.data.title,
17627                     description : d.data.description,
17628                     venue : d.data.venue
17629                 });
17630             });
17631         }
17632         
17633         this.renderEvents();
17634         
17635         if(this.calevents.length && this.loadMask){
17636             this.maskEl.hide();
17637         }
17638     },
17639     
17640     onBeforeLoad: function()
17641     {
17642         this.clearEvents();
17643         if(this.loadMask){
17644             this.maskEl.show();
17645         }
17646     }
17647 });
17648
17649  
17650  /*
17651  * - LGPL
17652  *
17653  * element
17654  * 
17655  */
17656
17657 /**
17658  * @class Roo.bootstrap.Popover
17659  * @extends Roo.bootstrap.Component
17660  * Bootstrap Popover class
17661  * @cfg {String} html contents of the popover   (or false to use children..)
17662  * @cfg {String} title of popover (or false to hide)
17663  * @cfg {String} placement how it is placed
17664  * @cfg {String} trigger click || hover (or false to trigger manually)
17665  * @cfg {String} over what (parent or false to trigger manually.)
17666  * @cfg {Number} delay - delay before showing
17667  
17668  * @constructor
17669  * Create a new Popover
17670  * @param {Object} config The config object
17671  */
17672
17673 Roo.bootstrap.Popover = function(config){
17674     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17675     
17676     this.addEvents({
17677         // raw events
17678          /**
17679          * @event show
17680          * After the popover show
17681          * 
17682          * @param {Roo.bootstrap.Popover} this
17683          */
17684         "show" : true,
17685         /**
17686          * @event hide
17687          * After the popover hide
17688          * 
17689          * @param {Roo.bootstrap.Popover} this
17690          */
17691         "hide" : true
17692     });
17693 };
17694
17695 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17696     
17697     title: 'Fill in a title',
17698     html: false,
17699     
17700     placement : 'right',
17701     trigger : 'hover', // hover
17702     
17703     delay : 0,
17704     
17705     over: 'parent',
17706     
17707     can_build_overlaid : false,
17708     
17709     getChildContainer : function()
17710     {
17711         return this.el.select('.popover-content',true).first();
17712     },
17713     
17714     getAutoCreate : function(){
17715          
17716         var cfg = {
17717            cls : 'popover roo-dynamic',
17718            style: 'display:block',
17719            cn : [
17720                 {
17721                     cls : 'arrow'
17722                 },
17723                 {
17724                     cls : 'popover-inner',
17725                     cn : [
17726                         {
17727                             tag: 'h3',
17728                             cls: 'popover-title popover-header',
17729                             html : this.title
17730                         },
17731                         {
17732                             cls : 'popover-content popover-body',
17733                             html : this.html
17734                         }
17735                     ]
17736                     
17737                 }
17738            ]
17739         };
17740         
17741         return cfg;
17742     },
17743     setTitle: function(str)
17744     {
17745         this.title = str;
17746         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17747     },
17748     setContent: function(str)
17749     {
17750         this.html = str;
17751         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17752     },
17753     // as it get's added to the bottom of the page.
17754     onRender : function(ct, position)
17755     {
17756         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17757         if(!this.el){
17758             var cfg = Roo.apply({},  this.getAutoCreate());
17759             cfg.id = Roo.id();
17760             
17761             if (this.cls) {
17762                 cfg.cls += ' ' + this.cls;
17763             }
17764             if (this.style) {
17765                 cfg.style = this.style;
17766             }
17767             //Roo.log("adding to ");
17768             this.el = Roo.get(document.body).createChild(cfg, position);
17769 //            Roo.log(this.el);
17770         }
17771         this.initEvents();
17772     },
17773     
17774     initEvents : function()
17775     {
17776         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17777         this.el.enableDisplayMode('block');
17778         this.el.hide();
17779         if (this.over === false) {
17780             return; 
17781         }
17782         if (this.triggers === false) {
17783             return;
17784         }
17785         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17786         var triggers = this.trigger ? this.trigger.split(' ') : [];
17787         Roo.each(triggers, function(trigger) {
17788         
17789             if (trigger == 'click') {
17790                 on_el.on('click', this.toggle, this);
17791             } else if (trigger != 'manual') {
17792                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17793                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17794       
17795                 on_el.on(eventIn  ,this.enter, this);
17796                 on_el.on(eventOut, this.leave, this);
17797             }
17798         }, this);
17799         
17800     },
17801     
17802     
17803     // private
17804     timeout : null,
17805     hoverState : null,
17806     
17807     toggle : function () {
17808         this.hoverState == 'in' ? this.leave() : this.enter();
17809     },
17810     
17811     enter : function () {
17812         
17813         clearTimeout(this.timeout);
17814     
17815         this.hoverState = 'in';
17816     
17817         if (!this.delay || !this.delay.show) {
17818             this.show();
17819             return;
17820         }
17821         var _t = this;
17822         this.timeout = setTimeout(function () {
17823             if (_t.hoverState == 'in') {
17824                 _t.show();
17825             }
17826         }, this.delay.show)
17827     },
17828     
17829     leave : function() {
17830         clearTimeout(this.timeout);
17831     
17832         this.hoverState = 'out';
17833     
17834         if (!this.delay || !this.delay.hide) {
17835             this.hide();
17836             return;
17837         }
17838         var _t = this;
17839         this.timeout = setTimeout(function () {
17840             if (_t.hoverState == 'out') {
17841                 _t.hide();
17842             }
17843         }, this.delay.hide)
17844     },
17845     
17846     show : function (on_el)
17847     {
17848         if (!on_el) {
17849             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17850         }
17851         
17852         // set content.
17853         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17854         if (this.html !== false) {
17855             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17856         }
17857         this.el.removeClass([
17858             'fade','top','bottom', 'left', 'right','in',
17859             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17860         ]);
17861         if (!this.title.length) {
17862             this.el.select('.popover-title',true).hide();
17863         }
17864         
17865         var placement = typeof this.placement == 'function' ?
17866             this.placement.call(this, this.el, on_el) :
17867             this.placement;
17868             
17869         var autoToken = /\s?auto?\s?/i;
17870         var autoPlace = autoToken.test(placement);
17871         if (autoPlace) {
17872             placement = placement.replace(autoToken, '') || 'top';
17873         }
17874         
17875         //this.el.detach()
17876         //this.el.setXY([0,0]);
17877         this.el.show();
17878         this.el.dom.style.display='block';
17879         this.el.addClass(placement);
17880         
17881         //this.el.appendTo(on_el);
17882         
17883         var p = this.getPosition();
17884         var box = this.el.getBox();
17885         
17886         if (autoPlace) {
17887             // fixme..
17888         }
17889         var align = Roo.bootstrap.Popover.alignment[placement];
17890         
17891 //        Roo.log(align);
17892         this.el.alignTo(on_el, align[0],align[1]);
17893         //var arrow = this.el.select('.arrow',true).first();
17894         //arrow.set(align[2], 
17895         
17896         this.el.addClass('in');
17897         
17898         
17899         if (this.el.hasClass('fade')) {
17900             // fade it?
17901         }
17902         
17903         this.hoverState = 'in';
17904         
17905         this.fireEvent('show', this);
17906         
17907     },
17908     hide : function()
17909     {
17910         this.el.setXY([0,0]);
17911         this.el.removeClass('in');
17912         this.el.hide();
17913         this.hoverState = null;
17914         
17915         this.fireEvent('hide', this);
17916     }
17917     
17918 });
17919
17920 Roo.bootstrap.Popover.alignment = {
17921     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17922     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17923     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17924     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17925 };
17926
17927  /*
17928  * - LGPL
17929  *
17930  * Progress
17931  * 
17932  */
17933
17934 /**
17935  * @class Roo.bootstrap.Progress
17936  * @extends Roo.bootstrap.Component
17937  * Bootstrap Progress class
17938  * @cfg {Boolean} striped striped of the progress bar
17939  * @cfg {Boolean} active animated of the progress bar
17940  * 
17941  * 
17942  * @constructor
17943  * Create a new Progress
17944  * @param {Object} config The config object
17945  */
17946
17947 Roo.bootstrap.Progress = function(config){
17948     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17949 };
17950
17951 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17952     
17953     striped : false,
17954     active: false,
17955     
17956     getAutoCreate : function(){
17957         var cfg = {
17958             tag: 'div',
17959             cls: 'progress'
17960         };
17961         
17962         
17963         if(this.striped){
17964             cfg.cls += ' progress-striped';
17965         }
17966       
17967         if(this.active){
17968             cfg.cls += ' active';
17969         }
17970         
17971         
17972         return cfg;
17973     }
17974    
17975 });
17976
17977  
17978
17979  /*
17980  * - LGPL
17981  *
17982  * ProgressBar
17983  * 
17984  */
17985
17986 /**
17987  * @class Roo.bootstrap.ProgressBar
17988  * @extends Roo.bootstrap.Component
17989  * Bootstrap ProgressBar class
17990  * @cfg {Number} aria_valuenow aria-value now
17991  * @cfg {Number} aria_valuemin aria-value min
17992  * @cfg {Number} aria_valuemax aria-value max
17993  * @cfg {String} label label for the progress bar
17994  * @cfg {String} panel (success | info | warning | danger )
17995  * @cfg {String} role role of the progress bar
17996  * @cfg {String} sr_only text
17997  * 
17998  * 
17999  * @constructor
18000  * Create a new ProgressBar
18001  * @param {Object} config The config object
18002  */
18003
18004 Roo.bootstrap.ProgressBar = function(config){
18005     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18006 };
18007
18008 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18009     
18010     aria_valuenow : 0,
18011     aria_valuemin : 0,
18012     aria_valuemax : 100,
18013     label : false,
18014     panel : false,
18015     role : false,
18016     sr_only: false,
18017     
18018     getAutoCreate : function()
18019     {
18020         
18021         var cfg = {
18022             tag: 'div',
18023             cls: 'progress-bar',
18024             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18025         };
18026         
18027         if(this.sr_only){
18028             cfg.cn = {
18029                 tag: 'span',
18030                 cls: 'sr-only',
18031                 html: this.sr_only
18032             }
18033         }
18034         
18035         if(this.role){
18036             cfg.role = this.role;
18037         }
18038         
18039         if(this.aria_valuenow){
18040             cfg['aria-valuenow'] = this.aria_valuenow;
18041         }
18042         
18043         if(this.aria_valuemin){
18044             cfg['aria-valuemin'] = this.aria_valuemin;
18045         }
18046         
18047         if(this.aria_valuemax){
18048             cfg['aria-valuemax'] = this.aria_valuemax;
18049         }
18050         
18051         if(this.label && !this.sr_only){
18052             cfg.html = this.label;
18053         }
18054         
18055         if(this.panel){
18056             cfg.cls += ' progress-bar-' + this.panel;
18057         }
18058         
18059         return cfg;
18060     },
18061     
18062     update : function(aria_valuenow)
18063     {
18064         this.aria_valuenow = aria_valuenow;
18065         
18066         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18067     }
18068    
18069 });
18070
18071  
18072
18073  /*
18074  * - LGPL
18075  *
18076  * column
18077  * 
18078  */
18079
18080 /**
18081  * @class Roo.bootstrap.TabGroup
18082  * @extends Roo.bootstrap.Column
18083  * Bootstrap Column class
18084  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18085  * @cfg {Boolean} carousel true to make the group behave like a carousel
18086  * @cfg {Boolean} bullets show bullets for the panels
18087  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18088  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18089  * @cfg {Boolean} showarrow (true|false) show arrow default true
18090  * 
18091  * @constructor
18092  * Create a new TabGroup
18093  * @param {Object} config The config object
18094  */
18095
18096 Roo.bootstrap.TabGroup = function(config){
18097     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18098     if (!this.navId) {
18099         this.navId = Roo.id();
18100     }
18101     this.tabs = [];
18102     Roo.bootstrap.TabGroup.register(this);
18103     
18104 };
18105
18106 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18107     
18108     carousel : false,
18109     transition : false,
18110     bullets : 0,
18111     timer : 0,
18112     autoslide : false,
18113     slideFn : false,
18114     slideOnTouch : false,
18115     showarrow : true,
18116     
18117     getAutoCreate : function()
18118     {
18119         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18120         
18121         cfg.cls += ' tab-content';
18122         
18123         if (this.carousel) {
18124             cfg.cls += ' carousel slide';
18125             
18126             cfg.cn = [{
18127                cls : 'carousel-inner',
18128                cn : []
18129             }];
18130         
18131             if(this.bullets  && !Roo.isTouch){
18132                 
18133                 var bullets = {
18134                     cls : 'carousel-bullets',
18135                     cn : []
18136                 };
18137                
18138                 if(this.bullets_cls){
18139                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18140                 }
18141                 
18142                 bullets.cn.push({
18143                     cls : 'clear'
18144                 });
18145                 
18146                 cfg.cn[0].cn.push(bullets);
18147             }
18148             
18149             if(this.showarrow){
18150                 cfg.cn[0].cn.push({
18151                     tag : 'div',
18152                     class : 'carousel-arrow',
18153                     cn : [
18154                         {
18155                             tag : 'div',
18156                             class : 'carousel-prev',
18157                             cn : [
18158                                 {
18159                                     tag : 'i',
18160                                     class : 'fa fa-chevron-left'
18161                                 }
18162                             ]
18163                         },
18164                         {
18165                             tag : 'div',
18166                             class : 'carousel-next',
18167                             cn : [
18168                                 {
18169                                     tag : 'i',
18170                                     class : 'fa fa-chevron-right'
18171                                 }
18172                             ]
18173                         }
18174                     ]
18175                 });
18176             }
18177             
18178         }
18179         
18180         return cfg;
18181     },
18182     
18183     initEvents:  function()
18184     {
18185 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18186 //            this.el.on("touchstart", this.onTouchStart, this);
18187 //        }
18188         
18189         if(this.autoslide){
18190             var _this = this;
18191             
18192             this.slideFn = window.setInterval(function() {
18193                 _this.showPanelNext();
18194             }, this.timer);
18195         }
18196         
18197         if(this.showarrow){
18198             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18199             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18200         }
18201         
18202         
18203     },
18204     
18205 //    onTouchStart : function(e, el, o)
18206 //    {
18207 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18208 //            return;
18209 //        }
18210 //        
18211 //        this.showPanelNext();
18212 //    },
18213     
18214     
18215     getChildContainer : function()
18216     {
18217         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18218     },
18219     
18220     /**
18221     * register a Navigation item
18222     * @param {Roo.bootstrap.NavItem} the navitem to add
18223     */
18224     register : function(item)
18225     {
18226         this.tabs.push( item);
18227         item.navId = this.navId; // not really needed..
18228         this.addBullet();
18229     
18230     },
18231     
18232     getActivePanel : function()
18233     {
18234         var r = false;
18235         Roo.each(this.tabs, function(t) {
18236             if (t.active) {
18237                 r = t;
18238                 return false;
18239             }
18240             return null;
18241         });
18242         return r;
18243         
18244     },
18245     getPanelByName : function(n)
18246     {
18247         var r = false;
18248         Roo.each(this.tabs, function(t) {
18249             if (t.tabId == n) {
18250                 r = t;
18251                 return false;
18252             }
18253             return null;
18254         });
18255         return r;
18256     },
18257     indexOfPanel : function(p)
18258     {
18259         var r = false;
18260         Roo.each(this.tabs, function(t,i) {
18261             if (t.tabId == p.tabId) {
18262                 r = i;
18263                 return false;
18264             }
18265             return null;
18266         });
18267         return r;
18268     },
18269     /**
18270      * show a specific panel
18271      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18272      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18273      */
18274     showPanel : function (pan)
18275     {
18276         if(this.transition || typeof(pan) == 'undefined'){
18277             Roo.log("waiting for the transitionend");
18278             return;
18279         }
18280         
18281         if (typeof(pan) == 'number') {
18282             pan = this.tabs[pan];
18283         }
18284         
18285         if (typeof(pan) == 'string') {
18286             pan = this.getPanelByName(pan);
18287         }
18288         
18289         var cur = this.getActivePanel();
18290         
18291         if(!pan || !cur){
18292             Roo.log('pan or acitve pan is undefined');
18293             return false;
18294         }
18295         
18296         if (pan.tabId == this.getActivePanel().tabId) {
18297             return true;
18298         }
18299         
18300         if (false === cur.fireEvent('beforedeactivate')) {
18301             return false;
18302         }
18303         
18304         if(this.bullets > 0 && !Roo.isTouch){
18305             this.setActiveBullet(this.indexOfPanel(pan));
18306         }
18307         
18308         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18309             
18310             this.transition = true;
18311             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18312             var lr = dir == 'next' ? 'left' : 'right';
18313             pan.el.addClass(dir); // or prev
18314             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18315             cur.el.addClass(lr); // or right
18316             pan.el.addClass(lr);
18317             
18318             var _this = this;
18319             cur.el.on('transitionend', function() {
18320                 Roo.log("trans end?");
18321                 
18322                 pan.el.removeClass([lr,dir]);
18323                 pan.setActive(true);
18324                 
18325                 cur.el.removeClass([lr]);
18326                 cur.setActive(false);
18327                 
18328                 _this.transition = false;
18329                 
18330             }, this, { single:  true } );
18331             
18332             return true;
18333         }
18334         
18335         cur.setActive(false);
18336         pan.setActive(true);
18337         
18338         return true;
18339         
18340     },
18341     showPanelNext : function()
18342     {
18343         var i = this.indexOfPanel(this.getActivePanel());
18344         
18345         if (i >= this.tabs.length - 1 && !this.autoslide) {
18346             return;
18347         }
18348         
18349         if (i >= this.tabs.length - 1 && this.autoslide) {
18350             i = -1;
18351         }
18352         
18353         this.showPanel(this.tabs[i+1]);
18354     },
18355     
18356     showPanelPrev : function()
18357     {
18358         var i = this.indexOfPanel(this.getActivePanel());
18359         
18360         if (i  < 1 && !this.autoslide) {
18361             return;
18362         }
18363         
18364         if (i < 1 && this.autoslide) {
18365             i = this.tabs.length;
18366         }
18367         
18368         this.showPanel(this.tabs[i-1]);
18369     },
18370     
18371     
18372     addBullet: function()
18373     {
18374         if(!this.bullets || Roo.isTouch){
18375             return;
18376         }
18377         var ctr = this.el.select('.carousel-bullets',true).first();
18378         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18379         var bullet = ctr.createChild({
18380             cls : 'bullet bullet-' + i
18381         },ctr.dom.lastChild);
18382         
18383         
18384         var _this = this;
18385         
18386         bullet.on('click', (function(e, el, o, ii, t){
18387
18388             e.preventDefault();
18389
18390             this.showPanel(ii);
18391
18392             if(this.autoslide && this.slideFn){
18393                 clearInterval(this.slideFn);
18394                 this.slideFn = window.setInterval(function() {
18395                     _this.showPanelNext();
18396                 }, this.timer);
18397             }
18398
18399         }).createDelegate(this, [i, bullet], true));
18400                 
18401         
18402     },
18403      
18404     setActiveBullet : function(i)
18405     {
18406         if(Roo.isTouch){
18407             return;
18408         }
18409         
18410         Roo.each(this.el.select('.bullet', true).elements, function(el){
18411             el.removeClass('selected');
18412         });
18413
18414         var bullet = this.el.select('.bullet-' + i, true).first();
18415         
18416         if(!bullet){
18417             return;
18418         }
18419         
18420         bullet.addClass('selected');
18421     }
18422     
18423     
18424   
18425 });
18426
18427  
18428
18429  
18430  
18431 Roo.apply(Roo.bootstrap.TabGroup, {
18432     
18433     groups: {},
18434      /**
18435     * register a Navigation Group
18436     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18437     */
18438     register : function(navgrp)
18439     {
18440         this.groups[navgrp.navId] = navgrp;
18441         
18442     },
18443     /**
18444     * fetch a Navigation Group based on the navigation ID
18445     * if one does not exist , it will get created.
18446     * @param {string} the navgroup to add
18447     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18448     */
18449     get: function(navId) {
18450         if (typeof(this.groups[navId]) == 'undefined') {
18451             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18452         }
18453         return this.groups[navId] ;
18454     }
18455     
18456     
18457     
18458 });
18459
18460  /*
18461  * - LGPL
18462  *
18463  * TabPanel
18464  * 
18465  */
18466
18467 /**
18468  * @class Roo.bootstrap.TabPanel
18469  * @extends Roo.bootstrap.Component
18470  * Bootstrap TabPanel class
18471  * @cfg {Boolean} active panel active
18472  * @cfg {String} html panel content
18473  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18474  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18475  * @cfg {String} href click to link..
18476  * 
18477  * 
18478  * @constructor
18479  * Create a new TabPanel
18480  * @param {Object} config The config object
18481  */
18482
18483 Roo.bootstrap.TabPanel = function(config){
18484     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18485     this.addEvents({
18486         /**
18487              * @event changed
18488              * Fires when the active status changes
18489              * @param {Roo.bootstrap.TabPanel} this
18490              * @param {Boolean} state the new state
18491             
18492          */
18493         'changed': true,
18494         /**
18495              * @event beforedeactivate
18496              * Fires before a tab is de-activated - can be used to do validation on a form.
18497              * @param {Roo.bootstrap.TabPanel} this
18498              * @return {Boolean} false if there is an error
18499             
18500          */
18501         'beforedeactivate': true
18502      });
18503     
18504     this.tabId = this.tabId || Roo.id();
18505   
18506 };
18507
18508 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18509     
18510     active: false,
18511     html: false,
18512     tabId: false,
18513     navId : false,
18514     href : '',
18515     
18516     getAutoCreate : function(){
18517         var cfg = {
18518             tag: 'div',
18519             // item is needed for carousel - not sure if it has any effect otherwise
18520             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18521             html: this.html || ''
18522         };
18523         
18524         if(this.active){
18525             cfg.cls += ' active';
18526         }
18527         
18528         if(this.tabId){
18529             cfg.tabId = this.tabId;
18530         }
18531         
18532         
18533         return cfg;
18534     },
18535     
18536     initEvents:  function()
18537     {
18538         var p = this.parent();
18539         
18540         this.navId = this.navId || p.navId;
18541         
18542         if (typeof(this.navId) != 'undefined') {
18543             // not really needed.. but just in case.. parent should be a NavGroup.
18544             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18545             
18546             tg.register(this);
18547             
18548             var i = tg.tabs.length - 1;
18549             
18550             if(this.active && tg.bullets > 0 && i < tg.bullets){
18551                 tg.setActiveBullet(i);
18552             }
18553         }
18554         
18555         this.el.on('click', this.onClick, this);
18556         
18557         if(Roo.isTouch){
18558             this.el.on("touchstart", this.onTouchStart, this);
18559             this.el.on("touchmove", this.onTouchMove, this);
18560             this.el.on("touchend", this.onTouchEnd, this);
18561         }
18562         
18563     },
18564     
18565     onRender : function(ct, position)
18566     {
18567         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18568     },
18569     
18570     setActive : function(state)
18571     {
18572         Roo.log("panel - set active " + this.tabId + "=" + state);
18573         
18574         this.active = state;
18575         if (!state) {
18576             this.el.removeClass('active');
18577             
18578         } else  if (!this.el.hasClass('active')) {
18579             this.el.addClass('active');
18580         }
18581         
18582         this.fireEvent('changed', this, state);
18583     },
18584     
18585     onClick : function(e)
18586     {
18587         e.preventDefault();
18588         
18589         if(!this.href.length){
18590             return;
18591         }
18592         
18593         window.location.href = this.href;
18594     },
18595     
18596     startX : 0,
18597     startY : 0,
18598     endX : 0,
18599     endY : 0,
18600     swiping : false,
18601     
18602     onTouchStart : function(e)
18603     {
18604         this.swiping = false;
18605         
18606         this.startX = e.browserEvent.touches[0].clientX;
18607         this.startY = e.browserEvent.touches[0].clientY;
18608     },
18609     
18610     onTouchMove : function(e)
18611     {
18612         this.swiping = true;
18613         
18614         this.endX = e.browserEvent.touches[0].clientX;
18615         this.endY = e.browserEvent.touches[0].clientY;
18616     },
18617     
18618     onTouchEnd : function(e)
18619     {
18620         if(!this.swiping){
18621             this.onClick(e);
18622             return;
18623         }
18624         
18625         var tabGroup = this.parent();
18626         
18627         if(this.endX > this.startX){ // swiping right
18628             tabGroup.showPanelPrev();
18629             return;
18630         }
18631         
18632         if(this.startX > this.endX){ // swiping left
18633             tabGroup.showPanelNext();
18634             return;
18635         }
18636     }
18637     
18638     
18639 });
18640  
18641
18642  
18643
18644  /*
18645  * - LGPL
18646  *
18647  * DateField
18648  * 
18649  */
18650
18651 /**
18652  * @class Roo.bootstrap.DateField
18653  * @extends Roo.bootstrap.Input
18654  * Bootstrap DateField class
18655  * @cfg {Number} weekStart default 0
18656  * @cfg {String} viewMode default empty, (months|years)
18657  * @cfg {String} minViewMode default empty, (months|years)
18658  * @cfg {Number} startDate default -Infinity
18659  * @cfg {Number} endDate default Infinity
18660  * @cfg {Boolean} todayHighlight default false
18661  * @cfg {Boolean} todayBtn default false
18662  * @cfg {Boolean} calendarWeeks default false
18663  * @cfg {Object} daysOfWeekDisabled default empty
18664  * @cfg {Boolean} singleMode default false (true | false)
18665  * 
18666  * @cfg {Boolean} keyboardNavigation default true
18667  * @cfg {String} language default en
18668  * 
18669  * @constructor
18670  * Create a new DateField
18671  * @param {Object} config The config object
18672  */
18673
18674 Roo.bootstrap.DateField = function(config){
18675     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18676      this.addEvents({
18677             /**
18678              * @event show
18679              * Fires when this field show.
18680              * @param {Roo.bootstrap.DateField} this
18681              * @param {Mixed} date The date value
18682              */
18683             show : true,
18684             /**
18685              * @event show
18686              * Fires when this field hide.
18687              * @param {Roo.bootstrap.DateField} this
18688              * @param {Mixed} date The date value
18689              */
18690             hide : true,
18691             /**
18692              * @event select
18693              * Fires when select a date.
18694              * @param {Roo.bootstrap.DateField} this
18695              * @param {Mixed} date The date value
18696              */
18697             select : true,
18698             /**
18699              * @event beforeselect
18700              * Fires when before select a date.
18701              * @param {Roo.bootstrap.DateField} this
18702              * @param {Mixed} date The date value
18703              */
18704             beforeselect : true
18705         });
18706 };
18707
18708 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18709     
18710     /**
18711      * @cfg {String} format
18712      * The default date format string which can be overriden for localization support.  The format must be
18713      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18714      */
18715     format : "m/d/y",
18716     /**
18717      * @cfg {String} altFormats
18718      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18719      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18720      */
18721     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18722     
18723     weekStart : 0,
18724     
18725     viewMode : '',
18726     
18727     minViewMode : '',
18728     
18729     todayHighlight : false,
18730     
18731     todayBtn: false,
18732     
18733     language: 'en',
18734     
18735     keyboardNavigation: true,
18736     
18737     calendarWeeks: false,
18738     
18739     startDate: -Infinity,
18740     
18741     endDate: Infinity,
18742     
18743     daysOfWeekDisabled: [],
18744     
18745     _events: [],
18746     
18747     singleMode : false,
18748     
18749     UTCDate: function()
18750     {
18751         return new Date(Date.UTC.apply(Date, arguments));
18752     },
18753     
18754     UTCToday: function()
18755     {
18756         var today = new Date();
18757         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18758     },
18759     
18760     getDate: function() {
18761             var d = this.getUTCDate();
18762             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18763     },
18764     
18765     getUTCDate: function() {
18766             return this.date;
18767     },
18768     
18769     setDate: function(d) {
18770             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18771     },
18772     
18773     setUTCDate: function(d) {
18774             this.date = d;
18775             this.setValue(this.formatDate(this.date));
18776     },
18777         
18778     onRender: function(ct, position)
18779     {
18780         
18781         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18782         
18783         this.language = this.language || 'en';
18784         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18785         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18786         
18787         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18788         this.format = this.format || 'm/d/y';
18789         this.isInline = false;
18790         this.isInput = true;
18791         this.component = this.el.select('.add-on', true).first() || false;
18792         this.component = (this.component && this.component.length === 0) ? false : this.component;
18793         this.hasInput = this.component && this.inputEl().length;
18794         
18795         if (typeof(this.minViewMode === 'string')) {
18796             switch (this.minViewMode) {
18797                 case 'months':
18798                     this.minViewMode = 1;
18799                     break;
18800                 case 'years':
18801                     this.minViewMode = 2;
18802                     break;
18803                 default:
18804                     this.minViewMode = 0;
18805                     break;
18806             }
18807         }
18808         
18809         if (typeof(this.viewMode === 'string')) {
18810             switch (this.viewMode) {
18811                 case 'months':
18812                     this.viewMode = 1;
18813                     break;
18814                 case 'years':
18815                     this.viewMode = 2;
18816                     break;
18817                 default:
18818                     this.viewMode = 0;
18819                     break;
18820             }
18821         }
18822                 
18823         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18824         
18825 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18826         
18827         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18828         
18829         this.picker().on('mousedown', this.onMousedown, this);
18830         this.picker().on('click', this.onClick, this);
18831         
18832         this.picker().addClass('datepicker-dropdown');
18833         
18834         this.startViewMode = this.viewMode;
18835         
18836         if(this.singleMode){
18837             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18838                 v.setVisibilityMode(Roo.Element.DISPLAY);
18839                 v.hide();
18840             });
18841             
18842             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18843                 v.setStyle('width', '189px');
18844             });
18845         }
18846         
18847         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18848             if(!this.calendarWeeks){
18849                 v.remove();
18850                 return;
18851             }
18852             
18853             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18854             v.attr('colspan', function(i, val){
18855                 return parseInt(val) + 1;
18856             });
18857         });
18858                         
18859         
18860         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18861         
18862         this.setStartDate(this.startDate);
18863         this.setEndDate(this.endDate);
18864         
18865         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18866         
18867         this.fillDow();
18868         this.fillMonths();
18869         this.update();
18870         this.showMode();
18871         
18872         if(this.isInline) {
18873             this.showPopup();
18874         }
18875     },
18876     
18877     picker : function()
18878     {
18879         return this.pickerEl;
18880 //        return this.el.select('.datepicker', true).first();
18881     },
18882     
18883     fillDow: function()
18884     {
18885         var dowCnt = this.weekStart;
18886         
18887         var dow = {
18888             tag: 'tr',
18889             cn: [
18890                 
18891             ]
18892         };
18893         
18894         if(this.calendarWeeks){
18895             dow.cn.push({
18896                 tag: 'th',
18897                 cls: 'cw',
18898                 html: '&nbsp;'
18899             })
18900         }
18901         
18902         while (dowCnt < this.weekStart + 7) {
18903             dow.cn.push({
18904                 tag: 'th',
18905                 cls: 'dow',
18906                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18907             });
18908         }
18909         
18910         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18911     },
18912     
18913     fillMonths: function()
18914     {    
18915         var i = 0;
18916         var months = this.picker().select('>.datepicker-months td', true).first();
18917         
18918         months.dom.innerHTML = '';
18919         
18920         while (i < 12) {
18921             var month = {
18922                 tag: 'span',
18923                 cls: 'month',
18924                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18925             };
18926             
18927             months.createChild(month);
18928         }
18929         
18930     },
18931     
18932     update: function()
18933     {
18934         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;
18935         
18936         if (this.date < this.startDate) {
18937             this.viewDate = new Date(this.startDate);
18938         } else if (this.date > this.endDate) {
18939             this.viewDate = new Date(this.endDate);
18940         } else {
18941             this.viewDate = new Date(this.date);
18942         }
18943         
18944         this.fill();
18945     },
18946     
18947     fill: function() 
18948     {
18949         var d = new Date(this.viewDate),
18950                 year = d.getUTCFullYear(),
18951                 month = d.getUTCMonth(),
18952                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18953                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18954                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18955                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18956                 currentDate = this.date && this.date.valueOf(),
18957                 today = this.UTCToday();
18958         
18959         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18960         
18961 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18962         
18963 //        this.picker.select('>tfoot th.today').
18964 //                                              .text(dates[this.language].today)
18965 //                                              .toggle(this.todayBtn !== false);
18966     
18967         this.updateNavArrows();
18968         this.fillMonths();
18969                                                 
18970         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18971         
18972         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18973          
18974         prevMonth.setUTCDate(day);
18975         
18976         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18977         
18978         var nextMonth = new Date(prevMonth);
18979         
18980         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18981         
18982         nextMonth = nextMonth.valueOf();
18983         
18984         var fillMonths = false;
18985         
18986         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18987         
18988         while(prevMonth.valueOf() <= nextMonth) {
18989             var clsName = '';
18990             
18991             if (prevMonth.getUTCDay() === this.weekStart) {
18992                 if(fillMonths){
18993                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18994                 }
18995                     
18996                 fillMonths = {
18997                     tag: 'tr',
18998                     cn: []
18999                 };
19000                 
19001                 if(this.calendarWeeks){
19002                     // ISO 8601: First week contains first thursday.
19003                     // ISO also states week starts on Monday, but we can be more abstract here.
19004                     var
19005                     // Start of current week: based on weekstart/current date
19006                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19007                     // Thursday of this week
19008                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19009                     // First Thursday of year, year from thursday
19010                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19011                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19012                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19013                     
19014                     fillMonths.cn.push({
19015                         tag: 'td',
19016                         cls: 'cw',
19017                         html: calWeek
19018                     });
19019                 }
19020             }
19021             
19022             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19023                 clsName += ' old';
19024             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19025                 clsName += ' new';
19026             }
19027             if (this.todayHighlight &&
19028                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19029                 prevMonth.getUTCMonth() == today.getMonth() &&
19030                 prevMonth.getUTCDate() == today.getDate()) {
19031                 clsName += ' today';
19032             }
19033             
19034             if (currentDate && prevMonth.valueOf() === currentDate) {
19035                 clsName += ' active';
19036             }
19037             
19038             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19039                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19040                     clsName += ' disabled';
19041             }
19042             
19043             fillMonths.cn.push({
19044                 tag: 'td',
19045                 cls: 'day ' + clsName,
19046                 html: prevMonth.getDate()
19047             });
19048             
19049             prevMonth.setDate(prevMonth.getDate()+1);
19050         }
19051           
19052         var currentYear = this.date && this.date.getUTCFullYear();
19053         var currentMonth = this.date && this.date.getUTCMonth();
19054         
19055         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19056         
19057         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19058             v.removeClass('active');
19059             
19060             if(currentYear === year && k === currentMonth){
19061                 v.addClass('active');
19062             }
19063             
19064             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19065                 v.addClass('disabled');
19066             }
19067             
19068         });
19069         
19070         
19071         year = parseInt(year/10, 10) * 10;
19072         
19073         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19074         
19075         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19076         
19077         year -= 1;
19078         for (var i = -1; i < 11; i++) {
19079             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19080                 tag: 'span',
19081                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19082                 html: year
19083             });
19084             
19085             year += 1;
19086         }
19087     },
19088     
19089     showMode: function(dir) 
19090     {
19091         if (dir) {
19092             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19093         }
19094         
19095         Roo.each(this.picker().select('>div',true).elements, function(v){
19096             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19097             v.hide();
19098         });
19099         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19100     },
19101     
19102     place: function()
19103     {
19104         if(this.isInline) {
19105             return;
19106         }
19107         
19108         this.picker().removeClass(['bottom', 'top']);
19109         
19110         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19111             /*
19112              * place to the top of element!
19113              *
19114              */
19115             
19116             this.picker().addClass('top');
19117             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19118             
19119             return;
19120         }
19121         
19122         this.picker().addClass('bottom');
19123         
19124         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19125     },
19126     
19127     parseDate : function(value)
19128     {
19129         if(!value || value instanceof Date){
19130             return value;
19131         }
19132         var v = Date.parseDate(value, this.format);
19133         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19134             v = Date.parseDate(value, 'Y-m-d');
19135         }
19136         if(!v && this.altFormats){
19137             if(!this.altFormatsArray){
19138                 this.altFormatsArray = this.altFormats.split("|");
19139             }
19140             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19141                 v = Date.parseDate(value, this.altFormatsArray[i]);
19142             }
19143         }
19144         return v;
19145     },
19146     
19147     formatDate : function(date, fmt)
19148     {   
19149         return (!date || !(date instanceof Date)) ?
19150         date : date.dateFormat(fmt || this.format);
19151     },
19152     
19153     onFocus : function()
19154     {
19155         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19156         this.showPopup();
19157     },
19158     
19159     onBlur : function()
19160     {
19161         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19162         
19163         var d = this.inputEl().getValue();
19164         
19165         this.setValue(d);
19166                 
19167         this.hidePopup();
19168     },
19169     
19170     showPopup : function()
19171     {
19172         this.picker().show();
19173         this.update();
19174         this.place();
19175         
19176         this.fireEvent('showpopup', this, this.date);
19177     },
19178     
19179     hidePopup : function()
19180     {
19181         if(this.isInline) {
19182             return;
19183         }
19184         this.picker().hide();
19185         this.viewMode = this.startViewMode;
19186         this.showMode();
19187         
19188         this.fireEvent('hidepopup', this, this.date);
19189         
19190     },
19191     
19192     onMousedown: function(e)
19193     {
19194         e.stopPropagation();
19195         e.preventDefault();
19196     },
19197     
19198     keyup: function(e)
19199     {
19200         Roo.bootstrap.DateField.superclass.keyup.call(this);
19201         this.update();
19202     },
19203
19204     setValue: function(v)
19205     {
19206         if(this.fireEvent('beforeselect', this, v) !== false){
19207             var d = new Date(this.parseDate(v) ).clearTime();
19208         
19209             if(isNaN(d.getTime())){
19210                 this.date = this.viewDate = '';
19211                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19212                 return;
19213             }
19214
19215             v = this.formatDate(d);
19216
19217             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19218
19219             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19220
19221             this.update();
19222
19223             this.fireEvent('select', this, this.date);
19224         }
19225     },
19226     
19227     getValue: function()
19228     {
19229         return this.formatDate(this.date);
19230     },
19231     
19232     fireKey: function(e)
19233     {
19234         if (!this.picker().isVisible()){
19235             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19236                 this.showPopup();
19237             }
19238             return;
19239         }
19240         
19241         var dateChanged = false,
19242         dir, day, month,
19243         newDate, newViewDate;
19244         
19245         switch(e.keyCode){
19246             case 27: // escape
19247                 this.hidePopup();
19248                 e.preventDefault();
19249                 break;
19250             case 37: // left
19251             case 39: // right
19252                 if (!this.keyboardNavigation) {
19253                     break;
19254                 }
19255                 dir = e.keyCode == 37 ? -1 : 1;
19256                 
19257                 if (e.ctrlKey){
19258                     newDate = this.moveYear(this.date, dir);
19259                     newViewDate = this.moveYear(this.viewDate, dir);
19260                 } else if (e.shiftKey){
19261                     newDate = this.moveMonth(this.date, dir);
19262                     newViewDate = this.moveMonth(this.viewDate, dir);
19263                 } else {
19264                     newDate = new Date(this.date);
19265                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19266                     newViewDate = new Date(this.viewDate);
19267                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19268                 }
19269                 if (this.dateWithinRange(newDate)){
19270                     this.date = newDate;
19271                     this.viewDate = newViewDate;
19272                     this.setValue(this.formatDate(this.date));
19273 //                    this.update();
19274                     e.preventDefault();
19275                     dateChanged = true;
19276                 }
19277                 break;
19278             case 38: // up
19279             case 40: // down
19280                 if (!this.keyboardNavigation) {
19281                     break;
19282                 }
19283                 dir = e.keyCode == 38 ? -1 : 1;
19284                 if (e.ctrlKey){
19285                     newDate = this.moveYear(this.date, dir);
19286                     newViewDate = this.moveYear(this.viewDate, dir);
19287                 } else if (e.shiftKey){
19288                     newDate = this.moveMonth(this.date, dir);
19289                     newViewDate = this.moveMonth(this.viewDate, dir);
19290                 } else {
19291                     newDate = new Date(this.date);
19292                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19293                     newViewDate = new Date(this.viewDate);
19294                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19295                 }
19296                 if (this.dateWithinRange(newDate)){
19297                     this.date = newDate;
19298                     this.viewDate = newViewDate;
19299                     this.setValue(this.formatDate(this.date));
19300 //                    this.update();
19301                     e.preventDefault();
19302                     dateChanged = true;
19303                 }
19304                 break;
19305             case 13: // enter
19306                 this.setValue(this.formatDate(this.date));
19307                 this.hidePopup();
19308                 e.preventDefault();
19309                 break;
19310             case 9: // tab
19311                 this.setValue(this.formatDate(this.date));
19312                 this.hidePopup();
19313                 break;
19314             case 16: // shift
19315             case 17: // ctrl
19316             case 18: // alt
19317                 break;
19318             default :
19319                 this.hidePopup();
19320                 
19321         }
19322     },
19323     
19324     
19325     onClick: function(e) 
19326     {
19327         e.stopPropagation();
19328         e.preventDefault();
19329         
19330         var target = e.getTarget();
19331         
19332         if(target.nodeName.toLowerCase() === 'i'){
19333             target = Roo.get(target).dom.parentNode;
19334         }
19335         
19336         var nodeName = target.nodeName;
19337         var className = target.className;
19338         var html = target.innerHTML;
19339         //Roo.log(nodeName);
19340         
19341         switch(nodeName.toLowerCase()) {
19342             case 'th':
19343                 switch(className) {
19344                     case 'switch':
19345                         this.showMode(1);
19346                         break;
19347                     case 'prev':
19348                     case 'next':
19349                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19350                         switch(this.viewMode){
19351                                 case 0:
19352                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19353                                         break;
19354                                 case 1:
19355                                 case 2:
19356                                         this.viewDate = this.moveYear(this.viewDate, dir);
19357                                         break;
19358                         }
19359                         this.fill();
19360                         break;
19361                     case 'today':
19362                         var date = new Date();
19363                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19364 //                        this.fill()
19365                         this.setValue(this.formatDate(this.date));
19366                         
19367                         this.hidePopup();
19368                         break;
19369                 }
19370                 break;
19371             case 'span':
19372                 if (className.indexOf('disabled') < 0) {
19373                     this.viewDate.setUTCDate(1);
19374                     if (className.indexOf('month') > -1) {
19375                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19376                     } else {
19377                         var year = parseInt(html, 10) || 0;
19378                         this.viewDate.setUTCFullYear(year);
19379                         
19380                     }
19381                     
19382                     if(this.singleMode){
19383                         this.setValue(this.formatDate(this.viewDate));
19384                         this.hidePopup();
19385                         return;
19386                     }
19387                     
19388                     this.showMode(-1);
19389                     this.fill();
19390                 }
19391                 break;
19392                 
19393             case 'td':
19394                 //Roo.log(className);
19395                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19396                     var day = parseInt(html, 10) || 1;
19397                     var year = this.viewDate.getUTCFullYear(),
19398                         month = this.viewDate.getUTCMonth();
19399
19400                     if (className.indexOf('old') > -1) {
19401                         if(month === 0 ){
19402                             month = 11;
19403                             year -= 1;
19404                         }else{
19405                             month -= 1;
19406                         }
19407                     } else if (className.indexOf('new') > -1) {
19408                         if (month == 11) {
19409                             month = 0;
19410                             year += 1;
19411                         } else {
19412                             month += 1;
19413                         }
19414                     }
19415                     //Roo.log([year,month,day]);
19416                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19417                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19418 //                    this.fill();
19419                     //Roo.log(this.formatDate(this.date));
19420                     this.setValue(this.formatDate(this.date));
19421                     this.hidePopup();
19422                 }
19423                 break;
19424         }
19425     },
19426     
19427     setStartDate: function(startDate)
19428     {
19429         this.startDate = startDate || -Infinity;
19430         if (this.startDate !== -Infinity) {
19431             this.startDate = this.parseDate(this.startDate);
19432         }
19433         this.update();
19434         this.updateNavArrows();
19435     },
19436
19437     setEndDate: function(endDate)
19438     {
19439         this.endDate = endDate || Infinity;
19440         if (this.endDate !== Infinity) {
19441             this.endDate = this.parseDate(this.endDate);
19442         }
19443         this.update();
19444         this.updateNavArrows();
19445     },
19446     
19447     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19448     {
19449         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19450         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19451             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19452         }
19453         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19454             return parseInt(d, 10);
19455         });
19456         this.update();
19457         this.updateNavArrows();
19458     },
19459     
19460     updateNavArrows: function() 
19461     {
19462         if(this.singleMode){
19463             return;
19464         }
19465         
19466         var d = new Date(this.viewDate),
19467         year = d.getUTCFullYear(),
19468         month = d.getUTCMonth();
19469         
19470         Roo.each(this.picker().select('.prev', true).elements, function(v){
19471             v.show();
19472             switch (this.viewMode) {
19473                 case 0:
19474
19475                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19476                         v.hide();
19477                     }
19478                     break;
19479                 case 1:
19480                 case 2:
19481                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19482                         v.hide();
19483                     }
19484                     break;
19485             }
19486         });
19487         
19488         Roo.each(this.picker().select('.next', true).elements, function(v){
19489             v.show();
19490             switch (this.viewMode) {
19491                 case 0:
19492
19493                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19494                         v.hide();
19495                     }
19496                     break;
19497                 case 1:
19498                 case 2:
19499                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19500                         v.hide();
19501                     }
19502                     break;
19503             }
19504         })
19505     },
19506     
19507     moveMonth: function(date, dir)
19508     {
19509         if (!dir) {
19510             return date;
19511         }
19512         var new_date = new Date(date.valueOf()),
19513         day = new_date.getUTCDate(),
19514         month = new_date.getUTCMonth(),
19515         mag = Math.abs(dir),
19516         new_month, test;
19517         dir = dir > 0 ? 1 : -1;
19518         if (mag == 1){
19519             test = dir == -1
19520             // If going back one month, make sure month is not current month
19521             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19522             ? function(){
19523                 return new_date.getUTCMonth() == month;
19524             }
19525             // If going forward one month, make sure month is as expected
19526             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19527             : function(){
19528                 return new_date.getUTCMonth() != new_month;
19529             };
19530             new_month = month + dir;
19531             new_date.setUTCMonth(new_month);
19532             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19533             if (new_month < 0 || new_month > 11) {
19534                 new_month = (new_month + 12) % 12;
19535             }
19536         } else {
19537             // For magnitudes >1, move one month at a time...
19538             for (var i=0; i<mag; i++) {
19539                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19540                 new_date = this.moveMonth(new_date, dir);
19541             }
19542             // ...then reset the day, keeping it in the new month
19543             new_month = new_date.getUTCMonth();
19544             new_date.setUTCDate(day);
19545             test = function(){
19546                 return new_month != new_date.getUTCMonth();
19547             };
19548         }
19549         // Common date-resetting loop -- if date is beyond end of month, make it
19550         // end of month
19551         while (test()){
19552             new_date.setUTCDate(--day);
19553             new_date.setUTCMonth(new_month);
19554         }
19555         return new_date;
19556     },
19557
19558     moveYear: function(date, dir)
19559     {
19560         return this.moveMonth(date, dir*12);
19561     },
19562
19563     dateWithinRange: function(date)
19564     {
19565         return date >= this.startDate && date <= this.endDate;
19566     },
19567
19568     
19569     remove: function() 
19570     {
19571         this.picker().remove();
19572     },
19573     
19574     validateValue : function(value)
19575     {
19576         if(this.getVisibilityEl().hasClass('hidden')){
19577             return true;
19578         }
19579         
19580         if(value.length < 1)  {
19581             if(this.allowBlank){
19582                 return true;
19583             }
19584             return false;
19585         }
19586         
19587         if(value.length < this.minLength){
19588             return false;
19589         }
19590         if(value.length > this.maxLength){
19591             return false;
19592         }
19593         if(this.vtype){
19594             var vt = Roo.form.VTypes;
19595             if(!vt[this.vtype](value, this)){
19596                 return false;
19597             }
19598         }
19599         if(typeof this.validator == "function"){
19600             var msg = this.validator(value);
19601             if(msg !== true){
19602                 return false;
19603             }
19604         }
19605         
19606         if(this.regex && !this.regex.test(value)){
19607             return false;
19608         }
19609         
19610         if(typeof(this.parseDate(value)) == 'undefined'){
19611             return false;
19612         }
19613         
19614         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19615             return false;
19616         }      
19617         
19618         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19619             return false;
19620         } 
19621         
19622         
19623         return true;
19624     },
19625     
19626     reset : function()
19627     {
19628         this.date = this.viewDate = '';
19629         
19630         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19631     }
19632    
19633 });
19634
19635 Roo.apply(Roo.bootstrap.DateField,  {
19636     
19637     head : {
19638         tag: 'thead',
19639         cn: [
19640         {
19641             tag: 'tr',
19642             cn: [
19643             {
19644                 tag: 'th',
19645                 cls: 'prev',
19646                 html: '<i class="fa fa-arrow-left"/>'
19647             },
19648             {
19649                 tag: 'th',
19650                 cls: 'switch',
19651                 colspan: '5'
19652             },
19653             {
19654                 tag: 'th',
19655                 cls: 'next',
19656                 html: '<i class="fa fa-arrow-right"/>'
19657             }
19658
19659             ]
19660         }
19661         ]
19662     },
19663     
19664     content : {
19665         tag: 'tbody',
19666         cn: [
19667         {
19668             tag: 'tr',
19669             cn: [
19670             {
19671                 tag: 'td',
19672                 colspan: '7'
19673             }
19674             ]
19675         }
19676         ]
19677     },
19678     
19679     footer : {
19680         tag: 'tfoot',
19681         cn: [
19682         {
19683             tag: 'tr',
19684             cn: [
19685             {
19686                 tag: 'th',
19687                 colspan: '7',
19688                 cls: 'today'
19689             }
19690                     
19691             ]
19692         }
19693         ]
19694     },
19695     
19696     dates:{
19697         en: {
19698             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19699             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19700             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19701             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19702             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19703             today: "Today"
19704         }
19705     },
19706     
19707     modes: [
19708     {
19709         clsName: 'days',
19710         navFnc: 'Month',
19711         navStep: 1
19712     },
19713     {
19714         clsName: 'months',
19715         navFnc: 'FullYear',
19716         navStep: 1
19717     },
19718     {
19719         clsName: 'years',
19720         navFnc: 'FullYear',
19721         navStep: 10
19722     }]
19723 });
19724
19725 Roo.apply(Roo.bootstrap.DateField,  {
19726   
19727     template : {
19728         tag: 'div',
19729         cls: 'datepicker dropdown-menu roo-dynamic',
19730         cn: [
19731         {
19732             tag: 'div',
19733             cls: 'datepicker-days',
19734             cn: [
19735             {
19736                 tag: 'table',
19737                 cls: 'table-condensed',
19738                 cn:[
19739                 Roo.bootstrap.DateField.head,
19740                 {
19741                     tag: 'tbody'
19742                 },
19743                 Roo.bootstrap.DateField.footer
19744                 ]
19745             }
19746             ]
19747         },
19748         {
19749             tag: 'div',
19750             cls: 'datepicker-months',
19751             cn: [
19752             {
19753                 tag: 'table',
19754                 cls: 'table-condensed',
19755                 cn:[
19756                 Roo.bootstrap.DateField.head,
19757                 Roo.bootstrap.DateField.content,
19758                 Roo.bootstrap.DateField.footer
19759                 ]
19760             }
19761             ]
19762         },
19763         {
19764             tag: 'div',
19765             cls: 'datepicker-years',
19766             cn: [
19767             {
19768                 tag: 'table',
19769                 cls: 'table-condensed',
19770                 cn:[
19771                 Roo.bootstrap.DateField.head,
19772                 Roo.bootstrap.DateField.content,
19773                 Roo.bootstrap.DateField.footer
19774                 ]
19775             }
19776             ]
19777         }
19778         ]
19779     }
19780 });
19781
19782  
19783
19784  /*
19785  * - LGPL
19786  *
19787  * TimeField
19788  * 
19789  */
19790
19791 /**
19792  * @class Roo.bootstrap.TimeField
19793  * @extends Roo.bootstrap.Input
19794  * Bootstrap DateField class
19795  * 
19796  * 
19797  * @constructor
19798  * Create a new TimeField
19799  * @param {Object} config The config object
19800  */
19801
19802 Roo.bootstrap.TimeField = function(config){
19803     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19804     this.addEvents({
19805             /**
19806              * @event show
19807              * Fires when this field show.
19808              * @param {Roo.bootstrap.DateField} thisthis
19809              * @param {Mixed} date The date value
19810              */
19811             show : true,
19812             /**
19813              * @event show
19814              * Fires when this field hide.
19815              * @param {Roo.bootstrap.DateField} this
19816              * @param {Mixed} date The date value
19817              */
19818             hide : true,
19819             /**
19820              * @event select
19821              * Fires when select a date.
19822              * @param {Roo.bootstrap.DateField} this
19823              * @param {Mixed} date The date value
19824              */
19825             select : true
19826         });
19827 };
19828
19829 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19830     
19831     /**
19832      * @cfg {String} format
19833      * The default time format string which can be overriden for localization support.  The format must be
19834      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19835      */
19836     format : "H:i",
19837        
19838     onRender: function(ct, position)
19839     {
19840         
19841         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19842                 
19843         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19844         
19845         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19846         
19847         this.pop = this.picker().select('>.datepicker-time',true).first();
19848         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19849         
19850         this.picker().on('mousedown', this.onMousedown, this);
19851         this.picker().on('click', this.onClick, this);
19852         
19853         this.picker().addClass('datepicker-dropdown');
19854     
19855         this.fillTime();
19856         this.update();
19857             
19858         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19859         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19860         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19861         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19862         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19863         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19864
19865     },
19866     
19867     fireKey: function(e){
19868         if (!this.picker().isVisible()){
19869             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19870                 this.show();
19871             }
19872             return;
19873         }
19874
19875         e.preventDefault();
19876         
19877         switch(e.keyCode){
19878             case 27: // escape
19879                 this.hide();
19880                 break;
19881             case 37: // left
19882             case 39: // right
19883                 this.onTogglePeriod();
19884                 break;
19885             case 38: // up
19886                 this.onIncrementMinutes();
19887                 break;
19888             case 40: // down
19889                 this.onDecrementMinutes();
19890                 break;
19891             case 13: // enter
19892             case 9: // tab
19893                 this.setTime();
19894                 break;
19895         }
19896     },
19897     
19898     onClick: function(e) {
19899         e.stopPropagation();
19900         e.preventDefault();
19901     },
19902     
19903     picker : function()
19904     {
19905         return this.el.select('.datepicker', true).first();
19906     },
19907     
19908     fillTime: function()
19909     {    
19910         var time = this.pop.select('tbody', true).first();
19911         
19912         time.dom.innerHTML = '';
19913         
19914         time.createChild({
19915             tag: 'tr',
19916             cn: [
19917                 {
19918                     tag: 'td',
19919                     cn: [
19920                         {
19921                             tag: 'a',
19922                             href: '#',
19923                             cls: 'btn',
19924                             cn: [
19925                                 {
19926                                     tag: 'span',
19927                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19928                                 }
19929                             ]
19930                         } 
19931                     ]
19932                 },
19933                 {
19934                     tag: 'td',
19935                     cls: 'separator'
19936                 },
19937                 {
19938                     tag: 'td',
19939                     cn: [
19940                         {
19941                             tag: 'a',
19942                             href: '#',
19943                             cls: 'btn',
19944                             cn: [
19945                                 {
19946                                     tag: 'span',
19947                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19948                                 }
19949                             ]
19950                         }
19951                     ]
19952                 },
19953                 {
19954                     tag: 'td',
19955                     cls: 'separator'
19956                 }
19957             ]
19958         });
19959         
19960         time.createChild({
19961             tag: 'tr',
19962             cn: [
19963                 {
19964                     tag: 'td',
19965                     cn: [
19966                         {
19967                             tag: 'span',
19968                             cls: 'timepicker-hour',
19969                             html: '00'
19970                         }  
19971                     ]
19972                 },
19973                 {
19974                     tag: 'td',
19975                     cls: 'separator',
19976                     html: ':'
19977                 },
19978                 {
19979                     tag: 'td',
19980                     cn: [
19981                         {
19982                             tag: 'span',
19983                             cls: 'timepicker-minute',
19984                             html: '00'
19985                         }  
19986                     ]
19987                 },
19988                 {
19989                     tag: 'td',
19990                     cls: 'separator'
19991                 },
19992                 {
19993                     tag: 'td',
19994                     cn: [
19995                         {
19996                             tag: 'button',
19997                             type: 'button',
19998                             cls: 'btn btn-primary period',
19999                             html: 'AM'
20000                             
20001                         }
20002                     ]
20003                 }
20004             ]
20005         });
20006         
20007         time.createChild({
20008             tag: 'tr',
20009             cn: [
20010                 {
20011                     tag: 'td',
20012                     cn: [
20013                         {
20014                             tag: 'a',
20015                             href: '#',
20016                             cls: 'btn',
20017                             cn: [
20018                                 {
20019                                     tag: 'span',
20020                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20021                                 }
20022                             ]
20023                         }
20024                     ]
20025                 },
20026                 {
20027                     tag: 'td',
20028                     cls: 'separator'
20029                 },
20030                 {
20031                     tag: 'td',
20032                     cn: [
20033                         {
20034                             tag: 'a',
20035                             href: '#',
20036                             cls: 'btn',
20037                             cn: [
20038                                 {
20039                                     tag: 'span',
20040                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20041                                 }
20042                             ]
20043                         }
20044                     ]
20045                 },
20046                 {
20047                     tag: 'td',
20048                     cls: 'separator'
20049                 }
20050             ]
20051         });
20052         
20053     },
20054     
20055     update: function()
20056     {
20057         
20058         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20059         
20060         this.fill();
20061     },
20062     
20063     fill: function() 
20064     {
20065         var hours = this.time.getHours();
20066         var minutes = this.time.getMinutes();
20067         var period = 'AM';
20068         
20069         if(hours > 11){
20070             period = 'PM';
20071         }
20072         
20073         if(hours == 0){
20074             hours = 12;
20075         }
20076         
20077         
20078         if(hours > 12){
20079             hours = hours - 12;
20080         }
20081         
20082         if(hours < 10){
20083             hours = '0' + hours;
20084         }
20085         
20086         if(minutes < 10){
20087             minutes = '0' + minutes;
20088         }
20089         
20090         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20091         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20092         this.pop.select('button', true).first().dom.innerHTML = period;
20093         
20094     },
20095     
20096     place: function()
20097     {   
20098         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20099         
20100         var cls = ['bottom'];
20101         
20102         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20103             cls.pop();
20104             cls.push('top');
20105         }
20106         
20107         cls.push('right');
20108         
20109         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20110             cls.pop();
20111             cls.push('left');
20112         }
20113         
20114         this.picker().addClass(cls.join('-'));
20115         
20116         var _this = this;
20117         
20118         Roo.each(cls, function(c){
20119             if(c == 'bottom'){
20120                 _this.picker().setTop(_this.inputEl().getHeight());
20121                 return;
20122             }
20123             if(c == 'top'){
20124                 _this.picker().setTop(0 - _this.picker().getHeight());
20125                 return;
20126             }
20127             
20128             if(c == 'left'){
20129                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20130                 return;
20131             }
20132             if(c == 'right'){
20133                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20134                 return;
20135             }
20136         });
20137         
20138     },
20139   
20140     onFocus : function()
20141     {
20142         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20143         this.show();
20144     },
20145     
20146     onBlur : function()
20147     {
20148         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20149         this.hide();
20150     },
20151     
20152     show : function()
20153     {
20154         this.picker().show();
20155         this.pop.show();
20156         this.update();
20157         this.place();
20158         
20159         this.fireEvent('show', this, this.date);
20160     },
20161     
20162     hide : function()
20163     {
20164         this.picker().hide();
20165         this.pop.hide();
20166         
20167         this.fireEvent('hide', this, this.date);
20168     },
20169     
20170     setTime : function()
20171     {
20172         this.hide();
20173         this.setValue(this.time.format(this.format));
20174         
20175         this.fireEvent('select', this, this.date);
20176         
20177         
20178     },
20179     
20180     onMousedown: function(e){
20181         e.stopPropagation();
20182         e.preventDefault();
20183     },
20184     
20185     onIncrementHours: function()
20186     {
20187         Roo.log('onIncrementHours');
20188         this.time = this.time.add(Date.HOUR, 1);
20189         this.update();
20190         
20191     },
20192     
20193     onDecrementHours: function()
20194     {
20195         Roo.log('onDecrementHours');
20196         this.time = this.time.add(Date.HOUR, -1);
20197         this.update();
20198     },
20199     
20200     onIncrementMinutes: function()
20201     {
20202         Roo.log('onIncrementMinutes');
20203         this.time = this.time.add(Date.MINUTE, 1);
20204         this.update();
20205     },
20206     
20207     onDecrementMinutes: function()
20208     {
20209         Roo.log('onDecrementMinutes');
20210         this.time = this.time.add(Date.MINUTE, -1);
20211         this.update();
20212     },
20213     
20214     onTogglePeriod: function()
20215     {
20216         Roo.log('onTogglePeriod');
20217         this.time = this.time.add(Date.HOUR, 12);
20218         this.update();
20219     }
20220     
20221    
20222 });
20223
20224 Roo.apply(Roo.bootstrap.TimeField,  {
20225     
20226     content : {
20227         tag: 'tbody',
20228         cn: [
20229             {
20230                 tag: 'tr',
20231                 cn: [
20232                 {
20233                     tag: 'td',
20234                     colspan: '7'
20235                 }
20236                 ]
20237             }
20238         ]
20239     },
20240     
20241     footer : {
20242         tag: 'tfoot',
20243         cn: [
20244             {
20245                 tag: 'tr',
20246                 cn: [
20247                 {
20248                     tag: 'th',
20249                     colspan: '7',
20250                     cls: '',
20251                     cn: [
20252                         {
20253                             tag: 'button',
20254                             cls: 'btn btn-info ok',
20255                             html: 'OK'
20256                         }
20257                     ]
20258                 }
20259
20260                 ]
20261             }
20262         ]
20263     }
20264 });
20265
20266 Roo.apply(Roo.bootstrap.TimeField,  {
20267   
20268     template : {
20269         tag: 'div',
20270         cls: 'datepicker dropdown-menu',
20271         cn: [
20272             {
20273                 tag: 'div',
20274                 cls: 'datepicker-time',
20275                 cn: [
20276                 {
20277                     tag: 'table',
20278                     cls: 'table-condensed',
20279                     cn:[
20280                     Roo.bootstrap.TimeField.content,
20281                     Roo.bootstrap.TimeField.footer
20282                     ]
20283                 }
20284                 ]
20285             }
20286         ]
20287     }
20288 });
20289
20290  
20291
20292  /*
20293  * - LGPL
20294  *
20295  * MonthField
20296  * 
20297  */
20298
20299 /**
20300  * @class Roo.bootstrap.MonthField
20301  * @extends Roo.bootstrap.Input
20302  * Bootstrap MonthField class
20303  * 
20304  * @cfg {String} language default en
20305  * 
20306  * @constructor
20307  * Create a new MonthField
20308  * @param {Object} config The config object
20309  */
20310
20311 Roo.bootstrap.MonthField = function(config){
20312     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20313     
20314     this.addEvents({
20315         /**
20316          * @event show
20317          * Fires when this field show.
20318          * @param {Roo.bootstrap.MonthField} this
20319          * @param {Mixed} date The date value
20320          */
20321         show : true,
20322         /**
20323          * @event show
20324          * Fires when this field hide.
20325          * @param {Roo.bootstrap.MonthField} this
20326          * @param {Mixed} date The date value
20327          */
20328         hide : true,
20329         /**
20330          * @event select
20331          * Fires when select a date.
20332          * @param {Roo.bootstrap.MonthField} this
20333          * @param {String} oldvalue The old value
20334          * @param {String} newvalue The new value
20335          */
20336         select : true
20337     });
20338 };
20339
20340 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20341     
20342     onRender: function(ct, position)
20343     {
20344         
20345         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20346         
20347         this.language = this.language || 'en';
20348         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20349         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20350         
20351         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20352         this.isInline = false;
20353         this.isInput = true;
20354         this.component = this.el.select('.add-on', true).first() || false;
20355         this.component = (this.component && this.component.length === 0) ? false : this.component;
20356         this.hasInput = this.component && this.inputEL().length;
20357         
20358         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20359         
20360         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20361         
20362         this.picker().on('mousedown', this.onMousedown, this);
20363         this.picker().on('click', this.onClick, this);
20364         
20365         this.picker().addClass('datepicker-dropdown');
20366         
20367         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20368             v.setStyle('width', '189px');
20369         });
20370         
20371         this.fillMonths();
20372         
20373         this.update();
20374         
20375         if(this.isInline) {
20376             this.show();
20377         }
20378         
20379     },
20380     
20381     setValue: function(v, suppressEvent)
20382     {   
20383         var o = this.getValue();
20384         
20385         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20386         
20387         this.update();
20388
20389         if(suppressEvent !== true){
20390             this.fireEvent('select', this, o, v);
20391         }
20392         
20393     },
20394     
20395     getValue: function()
20396     {
20397         return this.value;
20398     },
20399     
20400     onClick: function(e) 
20401     {
20402         e.stopPropagation();
20403         e.preventDefault();
20404         
20405         var target = e.getTarget();
20406         
20407         if(target.nodeName.toLowerCase() === 'i'){
20408             target = Roo.get(target).dom.parentNode;
20409         }
20410         
20411         var nodeName = target.nodeName;
20412         var className = target.className;
20413         var html = target.innerHTML;
20414         
20415         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20416             return;
20417         }
20418         
20419         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20420         
20421         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20422         
20423         this.hide();
20424                         
20425     },
20426     
20427     picker : function()
20428     {
20429         return this.pickerEl;
20430     },
20431     
20432     fillMonths: function()
20433     {    
20434         var i = 0;
20435         var months = this.picker().select('>.datepicker-months td', true).first();
20436         
20437         months.dom.innerHTML = '';
20438         
20439         while (i < 12) {
20440             var month = {
20441                 tag: 'span',
20442                 cls: 'month',
20443                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20444             };
20445             
20446             months.createChild(month);
20447         }
20448         
20449     },
20450     
20451     update: function()
20452     {
20453         var _this = this;
20454         
20455         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20456             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20457         }
20458         
20459         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20460             e.removeClass('active');
20461             
20462             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20463                 e.addClass('active');
20464             }
20465         })
20466     },
20467     
20468     place: function()
20469     {
20470         if(this.isInline) {
20471             return;
20472         }
20473         
20474         this.picker().removeClass(['bottom', 'top']);
20475         
20476         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20477             /*
20478              * place to the top of element!
20479              *
20480              */
20481             
20482             this.picker().addClass('top');
20483             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20484             
20485             return;
20486         }
20487         
20488         this.picker().addClass('bottom');
20489         
20490         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20491     },
20492     
20493     onFocus : function()
20494     {
20495         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20496         this.show();
20497     },
20498     
20499     onBlur : function()
20500     {
20501         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20502         
20503         var d = this.inputEl().getValue();
20504         
20505         this.setValue(d);
20506                 
20507         this.hide();
20508     },
20509     
20510     show : function()
20511     {
20512         this.picker().show();
20513         this.picker().select('>.datepicker-months', true).first().show();
20514         this.update();
20515         this.place();
20516         
20517         this.fireEvent('show', this, this.date);
20518     },
20519     
20520     hide : function()
20521     {
20522         if(this.isInline) {
20523             return;
20524         }
20525         this.picker().hide();
20526         this.fireEvent('hide', this, this.date);
20527         
20528     },
20529     
20530     onMousedown: function(e)
20531     {
20532         e.stopPropagation();
20533         e.preventDefault();
20534     },
20535     
20536     keyup: function(e)
20537     {
20538         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20539         this.update();
20540     },
20541
20542     fireKey: function(e)
20543     {
20544         if (!this.picker().isVisible()){
20545             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20546                 this.show();
20547             }
20548             return;
20549         }
20550         
20551         var dir;
20552         
20553         switch(e.keyCode){
20554             case 27: // escape
20555                 this.hide();
20556                 e.preventDefault();
20557                 break;
20558             case 37: // left
20559             case 39: // right
20560                 dir = e.keyCode == 37 ? -1 : 1;
20561                 
20562                 this.vIndex = this.vIndex + dir;
20563                 
20564                 if(this.vIndex < 0){
20565                     this.vIndex = 0;
20566                 }
20567                 
20568                 if(this.vIndex > 11){
20569                     this.vIndex = 11;
20570                 }
20571                 
20572                 if(isNaN(this.vIndex)){
20573                     this.vIndex = 0;
20574                 }
20575                 
20576                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20577                 
20578                 break;
20579             case 38: // up
20580             case 40: // down
20581                 
20582                 dir = e.keyCode == 38 ? -1 : 1;
20583                 
20584                 this.vIndex = this.vIndex + dir * 4;
20585                 
20586                 if(this.vIndex < 0){
20587                     this.vIndex = 0;
20588                 }
20589                 
20590                 if(this.vIndex > 11){
20591                     this.vIndex = 11;
20592                 }
20593                 
20594                 if(isNaN(this.vIndex)){
20595                     this.vIndex = 0;
20596                 }
20597                 
20598                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20599                 break;
20600                 
20601             case 13: // enter
20602                 
20603                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20604                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20605                 }
20606                 
20607                 this.hide();
20608                 e.preventDefault();
20609                 break;
20610             case 9: // tab
20611                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20612                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20613                 }
20614                 this.hide();
20615                 break;
20616             case 16: // shift
20617             case 17: // ctrl
20618             case 18: // alt
20619                 break;
20620             default :
20621                 this.hide();
20622                 
20623         }
20624     },
20625     
20626     remove: function() 
20627     {
20628         this.picker().remove();
20629     }
20630    
20631 });
20632
20633 Roo.apply(Roo.bootstrap.MonthField,  {
20634     
20635     content : {
20636         tag: 'tbody',
20637         cn: [
20638         {
20639             tag: 'tr',
20640             cn: [
20641             {
20642                 tag: 'td',
20643                 colspan: '7'
20644             }
20645             ]
20646         }
20647         ]
20648     },
20649     
20650     dates:{
20651         en: {
20652             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20653             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20654         }
20655     }
20656 });
20657
20658 Roo.apply(Roo.bootstrap.MonthField,  {
20659   
20660     template : {
20661         tag: 'div',
20662         cls: 'datepicker dropdown-menu roo-dynamic',
20663         cn: [
20664             {
20665                 tag: 'div',
20666                 cls: 'datepicker-months',
20667                 cn: [
20668                 {
20669                     tag: 'table',
20670                     cls: 'table-condensed',
20671                     cn:[
20672                         Roo.bootstrap.DateField.content
20673                     ]
20674                 }
20675                 ]
20676             }
20677         ]
20678     }
20679 });
20680
20681  
20682
20683  
20684  /*
20685  * - LGPL
20686  *
20687  * CheckBox
20688  * 
20689  */
20690
20691 /**
20692  * @class Roo.bootstrap.CheckBox
20693  * @extends Roo.bootstrap.Input
20694  * Bootstrap CheckBox class
20695  * 
20696  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20697  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20698  * @cfg {String} boxLabel The text that appears beside the checkbox
20699  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20700  * @cfg {Boolean} checked initnal the element
20701  * @cfg {Boolean} inline inline the element (default false)
20702  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20703  * @cfg {String} tooltip label tooltip
20704  * 
20705  * @constructor
20706  * Create a new CheckBox
20707  * @param {Object} config The config object
20708  */
20709
20710 Roo.bootstrap.CheckBox = function(config){
20711     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20712    
20713     this.addEvents({
20714         /**
20715         * @event check
20716         * Fires when the element is checked or unchecked.
20717         * @param {Roo.bootstrap.CheckBox} this This input
20718         * @param {Boolean} checked The new checked value
20719         */
20720        check : true,
20721        /**
20722         * @event click
20723         * Fires when the element is click.
20724         * @param {Roo.bootstrap.CheckBox} this This input
20725         */
20726        click : true
20727     });
20728     
20729 };
20730
20731 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20732   
20733     inputType: 'checkbox',
20734     inputValue: 1,
20735     valueOff: 0,
20736     boxLabel: false,
20737     checked: false,
20738     weight : false,
20739     inline: false,
20740     tooltip : '',
20741     
20742     getAutoCreate : function()
20743     {
20744         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20745         
20746         var id = Roo.id();
20747         
20748         var cfg = {};
20749         
20750         cfg.cls = 'form-group ' + this.inputType; //input-group
20751         
20752         if(this.inline){
20753             cfg.cls += ' ' + this.inputType + '-inline';
20754         }
20755         
20756         var input =  {
20757             tag: 'input',
20758             id : id,
20759             type : this.inputType,
20760             value : this.inputValue,
20761             cls : 'roo-' + this.inputType, //'form-box',
20762             placeholder : this.placeholder || ''
20763             
20764         };
20765         
20766         if(this.inputType != 'radio'){
20767             var hidden =  {
20768                 tag: 'input',
20769                 type : 'hidden',
20770                 cls : 'roo-hidden-value',
20771                 value : this.checked ? this.inputValue : this.valueOff
20772             };
20773         }
20774         
20775             
20776         if (this.weight) { // Validity check?
20777             cfg.cls += " " + this.inputType + "-" + this.weight;
20778         }
20779         
20780         if (this.disabled) {
20781             input.disabled=true;
20782         }
20783         
20784         if(this.checked){
20785             input.checked = this.checked;
20786         }
20787         
20788         if (this.name) {
20789             
20790             input.name = this.name;
20791             
20792             if(this.inputType != 'radio'){
20793                 hidden.name = this.name;
20794                 input.name = '_hidden_' + this.name;
20795             }
20796         }
20797         
20798         if (this.size) {
20799             input.cls += ' input-' + this.size;
20800         }
20801         
20802         var settings=this;
20803         
20804         ['xs','sm','md','lg'].map(function(size){
20805             if (settings[size]) {
20806                 cfg.cls += ' col-' + size + '-' + settings[size];
20807             }
20808         });
20809         
20810         var inputblock = input;
20811          
20812         if (this.before || this.after) {
20813             
20814             inputblock = {
20815                 cls : 'input-group',
20816                 cn :  [] 
20817             };
20818             
20819             if (this.before) {
20820                 inputblock.cn.push({
20821                     tag :'span',
20822                     cls : 'input-group-addon',
20823                     html : this.before
20824                 });
20825             }
20826             
20827             inputblock.cn.push(input);
20828             
20829             if(this.inputType != 'radio'){
20830                 inputblock.cn.push(hidden);
20831             }
20832             
20833             if (this.after) {
20834                 inputblock.cn.push({
20835                     tag :'span',
20836                     cls : 'input-group-addon',
20837                     html : this.after
20838                 });
20839             }
20840             
20841         }
20842         
20843         if (align ==='left' && this.fieldLabel.length) {
20844 //                Roo.log("left and has label");
20845             cfg.cn = [
20846                 {
20847                     tag: 'label',
20848                     'for' :  id,
20849                     cls : 'control-label',
20850                     html : this.fieldLabel
20851                 },
20852                 {
20853                     cls : "", 
20854                     cn: [
20855                         inputblock
20856                     ]
20857                 }
20858             ];
20859             
20860             if(this.labelWidth > 12){
20861                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20862             }
20863             
20864             if(this.labelWidth < 13 && this.labelmd == 0){
20865                 this.labelmd = this.labelWidth;
20866             }
20867             
20868             if(this.labellg > 0){
20869                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20870                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20871             }
20872             
20873             if(this.labelmd > 0){
20874                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20875                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20876             }
20877             
20878             if(this.labelsm > 0){
20879                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20880                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20881             }
20882             
20883             if(this.labelxs > 0){
20884                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20885                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20886             }
20887             
20888         } else if ( this.fieldLabel.length) {
20889 //                Roo.log(" label");
20890                 cfg.cn = [
20891                    
20892                     {
20893                         tag: this.boxLabel ? 'span' : 'label',
20894                         'for': id,
20895                         cls: 'control-label box-input-label',
20896                         //cls : 'input-group-addon',
20897                         html : this.fieldLabel
20898                     },
20899                     
20900                     inputblock
20901                     
20902                 ];
20903
20904         } else {
20905             
20906 //                Roo.log(" no label && no align");
20907                 cfg.cn = [  inputblock ] ;
20908                 
20909                 
20910         }
20911         
20912         if(this.boxLabel){
20913              var boxLabelCfg = {
20914                 tag: 'label',
20915                 //'for': id, // box label is handled by onclick - so no for...
20916                 cls: 'box-label',
20917                 html: this.boxLabel
20918             };
20919             
20920             if(this.tooltip){
20921                 boxLabelCfg.tooltip = this.tooltip;
20922             }
20923              
20924             cfg.cn.push(boxLabelCfg);
20925         }
20926         
20927         if(this.inputType != 'radio'){
20928             cfg.cn.push(hidden);
20929         }
20930         
20931         return cfg;
20932         
20933     },
20934     
20935     /**
20936      * return the real input element.
20937      */
20938     inputEl: function ()
20939     {
20940         return this.el.select('input.roo-' + this.inputType,true).first();
20941     },
20942     hiddenEl: function ()
20943     {
20944         return this.el.select('input.roo-hidden-value',true).first();
20945     },
20946     
20947     labelEl: function()
20948     {
20949         return this.el.select('label.control-label',true).first();
20950     },
20951     /* depricated... */
20952     
20953     label: function()
20954     {
20955         return this.labelEl();
20956     },
20957     
20958     boxLabelEl: function()
20959     {
20960         return this.el.select('label.box-label',true).first();
20961     },
20962     
20963     initEvents : function()
20964     {
20965 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20966         
20967         this.inputEl().on('click', this.onClick,  this);
20968         
20969         if (this.boxLabel) { 
20970             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20971         }
20972         
20973         this.startValue = this.getValue();
20974         
20975         if(this.groupId){
20976             Roo.bootstrap.CheckBox.register(this);
20977         }
20978     },
20979     
20980     onClick : function(e)
20981     {   
20982         if(this.fireEvent('click', this, e) !== false){
20983             this.setChecked(!this.checked);
20984         }
20985         
20986     },
20987     
20988     setChecked : function(state,suppressEvent)
20989     {
20990         this.startValue = this.getValue();
20991
20992         if(this.inputType == 'radio'){
20993             
20994             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20995                 e.dom.checked = false;
20996             });
20997             
20998             this.inputEl().dom.checked = true;
20999             
21000             this.inputEl().dom.value = this.inputValue;
21001             
21002             if(suppressEvent !== true){
21003                 this.fireEvent('check', this, true);
21004             }
21005             
21006             this.validate();
21007             
21008             return;
21009         }
21010         
21011         this.checked = state;
21012         
21013         this.inputEl().dom.checked = state;
21014         
21015         
21016         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21017         
21018         if(suppressEvent !== true){
21019             this.fireEvent('check', this, state);
21020         }
21021         
21022         this.validate();
21023     },
21024     
21025     getValue : function()
21026     {
21027         if(this.inputType == 'radio'){
21028             return this.getGroupValue();
21029         }
21030         
21031         return this.hiddenEl().dom.value;
21032         
21033     },
21034     
21035     getGroupValue : function()
21036     {
21037         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21038             return '';
21039         }
21040         
21041         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21042     },
21043     
21044     setValue : function(v,suppressEvent)
21045     {
21046         if(this.inputType == 'radio'){
21047             this.setGroupValue(v, suppressEvent);
21048             return;
21049         }
21050         
21051         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21052         
21053         this.validate();
21054     },
21055     
21056     setGroupValue : function(v, suppressEvent)
21057     {
21058         this.startValue = this.getValue();
21059         
21060         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21061             e.dom.checked = false;
21062             
21063             if(e.dom.value == v){
21064                 e.dom.checked = true;
21065             }
21066         });
21067         
21068         if(suppressEvent !== true){
21069             this.fireEvent('check', this, true);
21070         }
21071
21072         this.validate();
21073         
21074         return;
21075     },
21076     
21077     validate : function()
21078     {
21079         if(this.getVisibilityEl().hasClass('hidden')){
21080             return true;
21081         }
21082         
21083         if(
21084                 this.disabled || 
21085                 (this.inputType == 'radio' && this.validateRadio()) ||
21086                 (this.inputType == 'checkbox' && this.validateCheckbox())
21087         ){
21088             this.markValid();
21089             return true;
21090         }
21091         
21092         this.markInvalid();
21093         return false;
21094     },
21095     
21096     validateRadio : function()
21097     {
21098         if(this.getVisibilityEl().hasClass('hidden')){
21099             return true;
21100         }
21101         
21102         if(this.allowBlank){
21103             return true;
21104         }
21105         
21106         var valid = false;
21107         
21108         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21109             if(!e.dom.checked){
21110                 return;
21111             }
21112             
21113             valid = true;
21114             
21115             return false;
21116         });
21117         
21118         return valid;
21119     },
21120     
21121     validateCheckbox : function()
21122     {
21123         if(!this.groupId){
21124             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21125             //return (this.getValue() == this.inputValue) ? true : false;
21126         }
21127         
21128         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21129         
21130         if(!group){
21131             return false;
21132         }
21133         
21134         var r = false;
21135         
21136         for(var i in group){
21137             if(group[i].el.isVisible(true)){
21138                 r = false;
21139                 break;
21140             }
21141             
21142             r = true;
21143         }
21144         
21145         for(var i in group){
21146             if(r){
21147                 break;
21148             }
21149             
21150             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21151         }
21152         
21153         return r;
21154     },
21155     
21156     /**
21157      * Mark this field as valid
21158      */
21159     markValid : function()
21160     {
21161         var _this = this;
21162         
21163         this.fireEvent('valid', this);
21164         
21165         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21166         
21167         if(this.groupId){
21168             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21169         }
21170         
21171         if(label){
21172             label.markValid();
21173         }
21174
21175         if(this.inputType == 'radio'){
21176             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21177                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21178                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21179             });
21180             
21181             return;
21182         }
21183
21184         if(!this.groupId){
21185             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21186             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21187             return;
21188         }
21189         
21190         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21191         
21192         if(!group){
21193             return;
21194         }
21195         
21196         for(var i in group){
21197             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21198             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21199         }
21200     },
21201     
21202      /**
21203      * Mark this field as invalid
21204      * @param {String} msg The validation message
21205      */
21206     markInvalid : function(msg)
21207     {
21208         if(this.allowBlank){
21209             return;
21210         }
21211         
21212         var _this = this;
21213         
21214         this.fireEvent('invalid', this, msg);
21215         
21216         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21217         
21218         if(this.groupId){
21219             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21220         }
21221         
21222         if(label){
21223             label.markInvalid();
21224         }
21225             
21226         if(this.inputType == 'radio'){
21227             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21228                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21229                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21230             });
21231             
21232             return;
21233         }
21234         
21235         if(!this.groupId){
21236             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21237             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21238             return;
21239         }
21240         
21241         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21242         
21243         if(!group){
21244             return;
21245         }
21246         
21247         for(var i in group){
21248             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21249             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21250         }
21251         
21252     },
21253     
21254     clearInvalid : function()
21255     {
21256         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21257         
21258         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21259         
21260         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21261         
21262         if (label && label.iconEl) {
21263             label.iconEl.removeClass(label.validClass);
21264             label.iconEl.removeClass(label.invalidClass);
21265         }
21266     },
21267     
21268     disable : function()
21269     {
21270         if(this.inputType != 'radio'){
21271             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21272             return;
21273         }
21274         
21275         var _this = this;
21276         
21277         if(this.rendered){
21278             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21279                 _this.getActionEl().addClass(this.disabledClass);
21280                 e.dom.disabled = true;
21281             });
21282         }
21283         
21284         this.disabled = true;
21285         this.fireEvent("disable", this);
21286         return this;
21287     },
21288
21289     enable : function()
21290     {
21291         if(this.inputType != 'radio'){
21292             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21293             return;
21294         }
21295         
21296         var _this = this;
21297         
21298         if(this.rendered){
21299             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21300                 _this.getActionEl().removeClass(this.disabledClass);
21301                 e.dom.disabled = false;
21302             });
21303         }
21304         
21305         this.disabled = false;
21306         this.fireEvent("enable", this);
21307         return this;
21308     },
21309     
21310     setBoxLabel : function(v)
21311     {
21312         this.boxLabel = v;
21313         
21314         if(this.rendered){
21315             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21316         }
21317     }
21318
21319 });
21320
21321 Roo.apply(Roo.bootstrap.CheckBox, {
21322     
21323     groups: {},
21324     
21325      /**
21326     * register a CheckBox Group
21327     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21328     */
21329     register : function(checkbox)
21330     {
21331         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21332             this.groups[checkbox.groupId] = {};
21333         }
21334         
21335         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21336             return;
21337         }
21338         
21339         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21340         
21341     },
21342     /**
21343     * fetch a CheckBox Group based on the group ID
21344     * @param {string} the group ID
21345     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21346     */
21347     get: function(groupId) {
21348         if (typeof(this.groups[groupId]) == 'undefined') {
21349             return false;
21350         }
21351         
21352         return this.groups[groupId] ;
21353     }
21354     
21355     
21356 });
21357 /*
21358  * - LGPL
21359  *
21360  * RadioItem
21361  * 
21362  */
21363
21364 /**
21365  * @class Roo.bootstrap.Radio
21366  * @extends Roo.bootstrap.Component
21367  * Bootstrap Radio class
21368  * @cfg {String} boxLabel - the label associated
21369  * @cfg {String} value - the value of radio
21370  * 
21371  * @constructor
21372  * Create a new Radio
21373  * @param {Object} config The config object
21374  */
21375 Roo.bootstrap.Radio = function(config){
21376     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21377     
21378 };
21379
21380 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21381     
21382     boxLabel : '',
21383     
21384     value : '',
21385     
21386     getAutoCreate : function()
21387     {
21388         var cfg = {
21389             tag : 'div',
21390             cls : 'form-group radio',
21391             cn : [
21392                 {
21393                     tag : 'label',
21394                     cls : 'box-label',
21395                     html : this.boxLabel
21396                 }
21397             ]
21398         };
21399         
21400         return cfg;
21401     },
21402     
21403     initEvents : function() 
21404     {
21405         this.parent().register(this);
21406         
21407         this.el.on('click', this.onClick, this);
21408         
21409     },
21410     
21411     onClick : function(e)
21412     {
21413         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21414             this.setChecked(true);
21415         }
21416     },
21417     
21418     setChecked : function(state, suppressEvent)
21419     {
21420         this.parent().setValue(this.value, suppressEvent);
21421         
21422     },
21423     
21424     setBoxLabel : function(v)
21425     {
21426         this.boxLabel = v;
21427         
21428         if(this.rendered){
21429             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21430         }
21431     }
21432     
21433 });
21434  
21435
21436  /*
21437  * - LGPL
21438  *
21439  * Input
21440  * 
21441  */
21442
21443 /**
21444  * @class Roo.bootstrap.SecurePass
21445  * @extends Roo.bootstrap.Input
21446  * Bootstrap SecurePass class
21447  *
21448  * 
21449  * @constructor
21450  * Create a new SecurePass
21451  * @param {Object} config The config object
21452  */
21453  
21454 Roo.bootstrap.SecurePass = function (config) {
21455     // these go here, so the translation tool can replace them..
21456     this.errors = {
21457         PwdEmpty: "Please type a password, and then retype it to confirm.",
21458         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21459         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21460         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21461         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21462         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21463         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21464         TooWeak: "Your password is Too Weak."
21465     },
21466     this.meterLabel = "Password strength:";
21467     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21468     this.meterClass = [
21469         "roo-password-meter-tooweak", 
21470         "roo-password-meter-weak", 
21471         "roo-password-meter-medium", 
21472         "roo-password-meter-strong", 
21473         "roo-password-meter-grey"
21474     ];
21475     
21476     this.errors = {};
21477     
21478     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21479 }
21480
21481 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21482     /**
21483      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21484      * {
21485      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21486      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21487      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21488      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21489      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21490      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21491      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21492      * })
21493      */
21494     // private
21495     
21496     meterWidth: 300,
21497     errorMsg :'',    
21498     errors: false,
21499     imageRoot: '/',
21500     /**
21501      * @cfg {String/Object} Label for the strength meter (defaults to
21502      * 'Password strength:')
21503      */
21504     // private
21505     meterLabel: '',
21506     /**
21507      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21508      * ['Weak', 'Medium', 'Strong'])
21509      */
21510     // private    
21511     pwdStrengths: false,    
21512     // private
21513     strength: 0,
21514     // private
21515     _lastPwd: null,
21516     // private
21517     kCapitalLetter: 0,
21518     kSmallLetter: 1,
21519     kDigit: 2,
21520     kPunctuation: 3,
21521     
21522     insecure: false,
21523     // private
21524     initEvents: function ()
21525     {
21526         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21527
21528         if (this.el.is('input[type=password]') && Roo.isSafari) {
21529             this.el.on('keydown', this.SafariOnKeyDown, this);
21530         }
21531
21532         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21533     },
21534     // private
21535     onRender: function (ct, position)
21536     {
21537         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21538         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21539         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21540
21541         this.trigger.createChild({
21542                    cn: [
21543                     {
21544                     //id: 'PwdMeter',
21545                     tag: 'div',
21546                     cls: 'roo-password-meter-grey col-xs-12',
21547                     style: {
21548                         //width: 0,
21549                         //width: this.meterWidth + 'px'                                                
21550                         }
21551                     },
21552                     {                            
21553                          cls: 'roo-password-meter-text'                          
21554                     }
21555                 ]            
21556         });
21557
21558          
21559         if (this.hideTrigger) {
21560             this.trigger.setDisplayed(false);
21561         }
21562         this.setSize(this.width || '', this.height || '');
21563     },
21564     // private
21565     onDestroy: function ()
21566     {
21567         if (this.trigger) {
21568             this.trigger.removeAllListeners();
21569             this.trigger.remove();
21570         }
21571         if (this.wrap) {
21572             this.wrap.remove();
21573         }
21574         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21575     },
21576     // private
21577     checkStrength: function ()
21578     {
21579         var pwd = this.inputEl().getValue();
21580         if (pwd == this._lastPwd) {
21581             return;
21582         }
21583
21584         var strength;
21585         if (this.ClientSideStrongPassword(pwd)) {
21586             strength = 3;
21587         } else if (this.ClientSideMediumPassword(pwd)) {
21588             strength = 2;
21589         } else if (this.ClientSideWeakPassword(pwd)) {
21590             strength = 1;
21591         } else {
21592             strength = 0;
21593         }
21594         
21595         Roo.log('strength1: ' + strength);
21596         
21597         //var pm = this.trigger.child('div/div/div').dom;
21598         var pm = this.trigger.child('div/div');
21599         pm.removeClass(this.meterClass);
21600         pm.addClass(this.meterClass[strength]);
21601                 
21602         
21603         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21604                 
21605         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21606         
21607         this._lastPwd = pwd;
21608     },
21609     reset: function ()
21610     {
21611         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21612         
21613         this._lastPwd = '';
21614         
21615         var pm = this.trigger.child('div/div');
21616         pm.removeClass(this.meterClass);
21617         pm.addClass('roo-password-meter-grey');        
21618         
21619         
21620         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21621         
21622         pt.innerHTML = '';
21623         this.inputEl().dom.type='password';
21624     },
21625     // private
21626     validateValue: function (value)
21627     {
21628         
21629         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21630             return false;
21631         }
21632         if (value.length == 0) {
21633             if (this.allowBlank) {
21634                 this.clearInvalid();
21635                 return true;
21636             }
21637
21638             this.markInvalid(this.errors.PwdEmpty);
21639             this.errorMsg = this.errors.PwdEmpty;
21640             return false;
21641         }
21642         
21643         if(this.insecure){
21644             return true;
21645         }
21646         
21647         if ('[\x21-\x7e]*'.match(value)) {
21648             this.markInvalid(this.errors.PwdBadChar);
21649             this.errorMsg = this.errors.PwdBadChar;
21650             return false;
21651         }
21652         if (value.length < 6) {
21653             this.markInvalid(this.errors.PwdShort);
21654             this.errorMsg = this.errors.PwdShort;
21655             return false;
21656         }
21657         if (value.length > 16) {
21658             this.markInvalid(this.errors.PwdLong);
21659             this.errorMsg = this.errors.PwdLong;
21660             return false;
21661         }
21662         var strength;
21663         if (this.ClientSideStrongPassword(value)) {
21664             strength = 3;
21665         } else if (this.ClientSideMediumPassword(value)) {
21666             strength = 2;
21667         } else if (this.ClientSideWeakPassword(value)) {
21668             strength = 1;
21669         } else {
21670             strength = 0;
21671         }
21672
21673         
21674         if (strength < 2) {
21675             //this.markInvalid(this.errors.TooWeak);
21676             this.errorMsg = this.errors.TooWeak;
21677             //return false;
21678         }
21679         
21680         
21681         console.log('strength2: ' + strength);
21682         
21683         //var pm = this.trigger.child('div/div/div').dom;
21684         
21685         var pm = this.trigger.child('div/div');
21686         pm.removeClass(this.meterClass);
21687         pm.addClass(this.meterClass[strength]);
21688                 
21689         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21690                 
21691         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21692         
21693         this.errorMsg = ''; 
21694         return true;
21695     },
21696     // private
21697     CharacterSetChecks: function (type)
21698     {
21699         this.type = type;
21700         this.fResult = false;
21701     },
21702     // private
21703     isctype: function (character, type)
21704     {
21705         switch (type) {  
21706             case this.kCapitalLetter:
21707                 if (character >= 'A' && character <= 'Z') {
21708                     return true;
21709                 }
21710                 break;
21711             
21712             case this.kSmallLetter:
21713                 if (character >= 'a' && character <= 'z') {
21714                     return true;
21715                 }
21716                 break;
21717             
21718             case this.kDigit:
21719                 if (character >= '0' && character <= '9') {
21720                     return true;
21721                 }
21722                 break;
21723             
21724             case this.kPunctuation:
21725                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21726                     return true;
21727                 }
21728                 break;
21729             
21730             default:
21731                 return false;
21732         }
21733
21734     },
21735     // private
21736     IsLongEnough: function (pwd, size)
21737     {
21738         return !(pwd == null || isNaN(size) || pwd.length < size);
21739     },
21740     // private
21741     SpansEnoughCharacterSets: function (word, nb)
21742     {
21743         if (!this.IsLongEnough(word, nb))
21744         {
21745             return false;
21746         }
21747
21748         var characterSetChecks = new Array(
21749             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21750             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21751         );
21752         
21753         for (var index = 0; index < word.length; ++index) {
21754             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21755                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21756                     characterSetChecks[nCharSet].fResult = true;
21757                     break;
21758                 }
21759             }
21760         }
21761
21762         var nCharSets = 0;
21763         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21764             if (characterSetChecks[nCharSet].fResult) {
21765                 ++nCharSets;
21766             }
21767         }
21768
21769         if (nCharSets < nb) {
21770             return false;
21771         }
21772         return true;
21773     },
21774     // private
21775     ClientSideStrongPassword: function (pwd)
21776     {
21777         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21778     },
21779     // private
21780     ClientSideMediumPassword: function (pwd)
21781     {
21782         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21783     },
21784     // private
21785     ClientSideWeakPassword: function (pwd)
21786     {
21787         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21788     }
21789           
21790 })//<script type="text/javascript">
21791
21792 /*
21793  * Based  Ext JS Library 1.1.1
21794  * Copyright(c) 2006-2007, Ext JS, LLC.
21795  * LGPL
21796  *
21797  */
21798  
21799 /**
21800  * @class Roo.HtmlEditorCore
21801  * @extends Roo.Component
21802  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21803  *
21804  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21805  */
21806
21807 Roo.HtmlEditorCore = function(config){
21808     
21809     
21810     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21811     
21812     
21813     this.addEvents({
21814         /**
21815          * @event initialize
21816          * Fires when the editor is fully initialized (including the iframe)
21817          * @param {Roo.HtmlEditorCore} this
21818          */
21819         initialize: true,
21820         /**
21821          * @event activate
21822          * Fires when the editor is first receives the focus. Any insertion must wait
21823          * until after this event.
21824          * @param {Roo.HtmlEditorCore} this
21825          */
21826         activate: true,
21827          /**
21828          * @event beforesync
21829          * Fires before the textarea is updated with content from the editor iframe. Return false
21830          * to cancel the sync.
21831          * @param {Roo.HtmlEditorCore} this
21832          * @param {String} html
21833          */
21834         beforesync: true,
21835          /**
21836          * @event beforepush
21837          * Fires before the iframe editor is updated with content from the textarea. Return false
21838          * to cancel the push.
21839          * @param {Roo.HtmlEditorCore} this
21840          * @param {String} html
21841          */
21842         beforepush: true,
21843          /**
21844          * @event sync
21845          * Fires when the textarea is updated with content from the editor iframe.
21846          * @param {Roo.HtmlEditorCore} this
21847          * @param {String} html
21848          */
21849         sync: true,
21850          /**
21851          * @event push
21852          * Fires when the iframe editor is updated with content from the textarea.
21853          * @param {Roo.HtmlEditorCore} this
21854          * @param {String} html
21855          */
21856         push: true,
21857         
21858         /**
21859          * @event editorevent
21860          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21861          * @param {Roo.HtmlEditorCore} this
21862          */
21863         editorevent: true
21864         
21865     });
21866     
21867     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21868     
21869     // defaults : white / black...
21870     this.applyBlacklists();
21871     
21872     
21873     
21874 };
21875
21876
21877 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21878
21879
21880      /**
21881      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21882      */
21883     
21884     owner : false,
21885     
21886      /**
21887      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21888      *                        Roo.resizable.
21889      */
21890     resizable : false,
21891      /**
21892      * @cfg {Number} height (in pixels)
21893      */   
21894     height: 300,
21895    /**
21896      * @cfg {Number} width (in pixels)
21897      */   
21898     width: 500,
21899     
21900     /**
21901      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21902      * 
21903      */
21904     stylesheets: false,
21905     
21906     // id of frame..
21907     frameId: false,
21908     
21909     // private properties
21910     validationEvent : false,
21911     deferHeight: true,
21912     initialized : false,
21913     activated : false,
21914     sourceEditMode : false,
21915     onFocus : Roo.emptyFn,
21916     iframePad:3,
21917     hideMode:'offsets',
21918     
21919     clearUp: true,
21920     
21921     // blacklist + whitelisted elements..
21922     black: false,
21923     white: false,
21924      
21925     bodyCls : '',
21926
21927     /**
21928      * Protected method that will not generally be called directly. It
21929      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21930      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21931      */
21932     getDocMarkup : function(){
21933         // body styles..
21934         var st = '';
21935         
21936         // inherit styels from page...?? 
21937         if (this.stylesheets === false) {
21938             
21939             Roo.get(document.head).select('style').each(function(node) {
21940                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21941             });
21942             
21943             Roo.get(document.head).select('link').each(function(node) { 
21944                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21945             });
21946             
21947         } else if (!this.stylesheets.length) {
21948                 // simple..
21949                 st = '<style type="text/css">' +
21950                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21951                    '</style>';
21952         } else { 
21953             st = '<style type="text/css">' +
21954                     this.stylesheets +
21955                 '</style>';
21956         }
21957         
21958         st +=  '<style type="text/css">' +
21959             'IMG { cursor: pointer } ' +
21960         '</style>';
21961
21962         var cls = 'roo-htmleditor-body';
21963         
21964         if(this.bodyCls.length){
21965             cls += ' ' + this.bodyCls;
21966         }
21967         
21968         return '<html><head>' + st  +
21969             //<style type="text/css">' +
21970             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21971             //'</style>' +
21972             ' </head><body class="' +  cls + '"></body></html>';
21973     },
21974
21975     // private
21976     onRender : function(ct, position)
21977     {
21978         var _t = this;
21979         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21980         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21981         
21982         
21983         this.el.dom.style.border = '0 none';
21984         this.el.dom.setAttribute('tabIndex', -1);
21985         this.el.addClass('x-hidden hide');
21986         
21987         
21988         
21989         if(Roo.isIE){ // fix IE 1px bogus margin
21990             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21991         }
21992        
21993         
21994         this.frameId = Roo.id();
21995         
21996          
21997         
21998         var iframe = this.owner.wrap.createChild({
21999             tag: 'iframe',
22000             cls: 'form-control', // bootstrap..
22001             id: this.frameId,
22002             name: this.frameId,
22003             frameBorder : 'no',
22004             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22005         }, this.el
22006         );
22007         
22008         
22009         this.iframe = iframe.dom;
22010
22011          this.assignDocWin();
22012         
22013         this.doc.designMode = 'on';
22014        
22015         this.doc.open();
22016         this.doc.write(this.getDocMarkup());
22017         this.doc.close();
22018
22019         
22020         var task = { // must defer to wait for browser to be ready
22021             run : function(){
22022                 //console.log("run task?" + this.doc.readyState);
22023                 this.assignDocWin();
22024                 if(this.doc.body || this.doc.readyState == 'complete'){
22025                     try {
22026                         this.doc.designMode="on";
22027                     } catch (e) {
22028                         return;
22029                     }
22030                     Roo.TaskMgr.stop(task);
22031                     this.initEditor.defer(10, this);
22032                 }
22033             },
22034             interval : 10,
22035             duration: 10000,
22036             scope: this
22037         };
22038         Roo.TaskMgr.start(task);
22039
22040     },
22041
22042     // private
22043     onResize : function(w, h)
22044     {
22045          Roo.log('resize: ' +w + ',' + h );
22046         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22047         if(!this.iframe){
22048             return;
22049         }
22050         if(typeof w == 'number'){
22051             
22052             this.iframe.style.width = w + 'px';
22053         }
22054         if(typeof h == 'number'){
22055             
22056             this.iframe.style.height = h + 'px';
22057             if(this.doc){
22058                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22059             }
22060         }
22061         
22062     },
22063
22064     /**
22065      * Toggles the editor between standard and source edit mode.
22066      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22067      */
22068     toggleSourceEdit : function(sourceEditMode){
22069         
22070         this.sourceEditMode = sourceEditMode === true;
22071         
22072         if(this.sourceEditMode){
22073  
22074             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22075             
22076         }else{
22077             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22078             //this.iframe.className = '';
22079             this.deferFocus();
22080         }
22081         //this.setSize(this.owner.wrap.getSize());
22082         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22083     },
22084
22085     
22086   
22087
22088     /**
22089      * Protected method that will not generally be called directly. If you need/want
22090      * custom HTML cleanup, this is the method you should override.
22091      * @param {String} html The HTML to be cleaned
22092      * return {String} The cleaned HTML
22093      */
22094     cleanHtml : function(html){
22095         html = String(html);
22096         if(html.length > 5){
22097             if(Roo.isSafari){ // strip safari nonsense
22098                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22099             }
22100         }
22101         if(html == '&nbsp;'){
22102             html = '';
22103         }
22104         return html;
22105     },
22106
22107     /**
22108      * HTML Editor -> Textarea
22109      * Protected method that will not generally be called directly. Syncs the contents
22110      * of the editor iframe with the textarea.
22111      */
22112     syncValue : function(){
22113         if(this.initialized){
22114             var bd = (this.doc.body || this.doc.documentElement);
22115             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22116             var html = bd.innerHTML;
22117             if(Roo.isSafari){
22118                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22119                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22120                 if(m && m[1]){
22121                     html = '<div style="'+m[0]+'">' + html + '</div>';
22122                 }
22123             }
22124             html = this.cleanHtml(html);
22125             // fix up the special chars.. normaly like back quotes in word...
22126             // however we do not want to do this with chinese..
22127             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22128                 var cc = b.charCodeAt();
22129                 if (
22130                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22131                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22132                     (cc >= 0xf900 && cc < 0xfb00 )
22133                 ) {
22134                         return b;
22135                 }
22136                 return "&#"+cc+";" 
22137             });
22138             if(this.owner.fireEvent('beforesync', this, html) !== false){
22139                 this.el.dom.value = html;
22140                 this.owner.fireEvent('sync', this, html);
22141             }
22142         }
22143     },
22144
22145     /**
22146      * Protected method that will not generally be called directly. Pushes the value of the textarea
22147      * into the iframe editor.
22148      */
22149     pushValue : function(){
22150         if(this.initialized){
22151             var v = this.el.dom.value.trim();
22152             
22153 //            if(v.length < 1){
22154 //                v = '&#160;';
22155 //            }
22156             
22157             if(this.owner.fireEvent('beforepush', this, v) !== false){
22158                 var d = (this.doc.body || this.doc.documentElement);
22159                 d.innerHTML = v;
22160                 this.cleanUpPaste();
22161                 this.el.dom.value = d.innerHTML;
22162                 this.owner.fireEvent('push', this, v);
22163             }
22164         }
22165     },
22166
22167     // private
22168     deferFocus : function(){
22169         this.focus.defer(10, this);
22170     },
22171
22172     // doc'ed in Field
22173     focus : function(){
22174         if(this.win && !this.sourceEditMode){
22175             this.win.focus();
22176         }else{
22177             this.el.focus();
22178         }
22179     },
22180     
22181     assignDocWin: function()
22182     {
22183         var iframe = this.iframe;
22184         
22185          if(Roo.isIE){
22186             this.doc = iframe.contentWindow.document;
22187             this.win = iframe.contentWindow;
22188         } else {
22189 //            if (!Roo.get(this.frameId)) {
22190 //                return;
22191 //            }
22192 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22193 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22194             
22195             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22196                 return;
22197             }
22198             
22199             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22200             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22201         }
22202     },
22203     
22204     // private
22205     initEditor : function(){
22206         //console.log("INIT EDITOR");
22207         this.assignDocWin();
22208         
22209         
22210         
22211         this.doc.designMode="on";
22212         this.doc.open();
22213         this.doc.write(this.getDocMarkup());
22214         this.doc.close();
22215         
22216         var dbody = (this.doc.body || this.doc.documentElement);
22217         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22218         // this copies styles from the containing element into thsi one..
22219         // not sure why we need all of this..
22220         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22221         
22222         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22223         //ss['background-attachment'] = 'fixed'; // w3c
22224         dbody.bgProperties = 'fixed'; // ie
22225         //Roo.DomHelper.applyStyles(dbody, ss);
22226         Roo.EventManager.on(this.doc, {
22227             //'mousedown': this.onEditorEvent,
22228             'mouseup': this.onEditorEvent,
22229             'dblclick': this.onEditorEvent,
22230             'click': this.onEditorEvent,
22231             'keyup': this.onEditorEvent,
22232             buffer:100,
22233             scope: this
22234         });
22235         if(Roo.isGecko){
22236             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22237         }
22238         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22239             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22240         }
22241         this.initialized = true;
22242
22243         this.owner.fireEvent('initialize', this);
22244         this.pushValue();
22245     },
22246
22247     // private
22248     onDestroy : function(){
22249         
22250         
22251         
22252         if(this.rendered){
22253             
22254             //for (var i =0; i < this.toolbars.length;i++) {
22255             //    // fixme - ask toolbars for heights?
22256             //    this.toolbars[i].onDestroy();
22257            // }
22258             
22259             //this.wrap.dom.innerHTML = '';
22260             //this.wrap.remove();
22261         }
22262     },
22263
22264     // private
22265     onFirstFocus : function(){
22266         
22267         this.assignDocWin();
22268         
22269         
22270         this.activated = true;
22271          
22272     
22273         if(Roo.isGecko){ // prevent silly gecko errors
22274             this.win.focus();
22275             var s = this.win.getSelection();
22276             if(!s.focusNode || s.focusNode.nodeType != 3){
22277                 var r = s.getRangeAt(0);
22278                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22279                 r.collapse(true);
22280                 this.deferFocus();
22281             }
22282             try{
22283                 this.execCmd('useCSS', true);
22284                 this.execCmd('styleWithCSS', false);
22285             }catch(e){}
22286         }
22287         this.owner.fireEvent('activate', this);
22288     },
22289
22290     // private
22291     adjustFont: function(btn){
22292         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22293         //if(Roo.isSafari){ // safari
22294         //    adjust *= 2;
22295        // }
22296         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22297         if(Roo.isSafari){ // safari
22298             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22299             v =  (v < 10) ? 10 : v;
22300             v =  (v > 48) ? 48 : v;
22301             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22302             
22303         }
22304         
22305         
22306         v = Math.max(1, v+adjust);
22307         
22308         this.execCmd('FontSize', v  );
22309     },
22310
22311     onEditorEvent : function(e)
22312     {
22313         this.owner.fireEvent('editorevent', this, e);
22314       //  this.updateToolbar();
22315         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22316     },
22317
22318     insertTag : function(tg)
22319     {
22320         // could be a bit smarter... -> wrap the current selected tRoo..
22321         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22322             
22323             range = this.createRange(this.getSelection());
22324             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22325             wrappingNode.appendChild(range.extractContents());
22326             range.insertNode(wrappingNode);
22327
22328             return;
22329             
22330             
22331             
22332         }
22333         this.execCmd("formatblock",   tg);
22334         
22335     },
22336     
22337     insertText : function(txt)
22338     {
22339         
22340         
22341         var range = this.createRange();
22342         range.deleteContents();
22343                //alert(Sender.getAttribute('label'));
22344                
22345         range.insertNode(this.doc.createTextNode(txt));
22346     } ,
22347     
22348      
22349
22350     /**
22351      * Executes a Midas editor command on the editor document and performs necessary focus and
22352      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22353      * @param {String} cmd The Midas command
22354      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22355      */
22356     relayCmd : function(cmd, value){
22357         this.win.focus();
22358         this.execCmd(cmd, value);
22359         this.owner.fireEvent('editorevent', this);
22360         //this.updateToolbar();
22361         this.owner.deferFocus();
22362     },
22363
22364     /**
22365      * Executes a Midas editor command directly on the editor document.
22366      * For visual commands, you should use {@link #relayCmd} instead.
22367      * <b>This should only be called after the editor is initialized.</b>
22368      * @param {String} cmd The Midas command
22369      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22370      */
22371     execCmd : function(cmd, value){
22372         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22373         this.syncValue();
22374     },
22375  
22376  
22377    
22378     /**
22379      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22380      * to insert tRoo.
22381      * @param {String} text | dom node.. 
22382      */
22383     insertAtCursor : function(text)
22384     {
22385         
22386         if(!this.activated){
22387             return;
22388         }
22389         /*
22390         if(Roo.isIE){
22391             this.win.focus();
22392             var r = this.doc.selection.createRange();
22393             if(r){
22394                 r.collapse(true);
22395                 r.pasteHTML(text);
22396                 this.syncValue();
22397                 this.deferFocus();
22398             
22399             }
22400             return;
22401         }
22402         */
22403         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22404             this.win.focus();
22405             
22406             
22407             // from jquery ui (MIT licenced)
22408             var range, node;
22409             var win = this.win;
22410             
22411             if (win.getSelection && win.getSelection().getRangeAt) {
22412                 range = win.getSelection().getRangeAt(0);
22413                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22414                 range.insertNode(node);
22415             } else if (win.document.selection && win.document.selection.createRange) {
22416                 // no firefox support
22417                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22418                 win.document.selection.createRange().pasteHTML(txt);
22419             } else {
22420                 // no firefox support
22421                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22422                 this.execCmd('InsertHTML', txt);
22423             } 
22424             
22425             this.syncValue();
22426             
22427             this.deferFocus();
22428         }
22429     },
22430  // private
22431     mozKeyPress : function(e){
22432         if(e.ctrlKey){
22433             var c = e.getCharCode(), cmd;
22434           
22435             if(c > 0){
22436                 c = String.fromCharCode(c).toLowerCase();
22437                 switch(c){
22438                     case 'b':
22439                         cmd = 'bold';
22440                         break;
22441                     case 'i':
22442                         cmd = 'italic';
22443                         break;
22444                     
22445                     case 'u':
22446                         cmd = 'underline';
22447                         break;
22448                     
22449                     case 'v':
22450                         this.cleanUpPaste.defer(100, this);
22451                         return;
22452                         
22453                 }
22454                 if(cmd){
22455                     this.win.focus();
22456                     this.execCmd(cmd);
22457                     this.deferFocus();
22458                     e.preventDefault();
22459                 }
22460                 
22461             }
22462         }
22463     },
22464
22465     // private
22466     fixKeys : function(){ // load time branching for fastest keydown performance
22467         if(Roo.isIE){
22468             return function(e){
22469                 var k = e.getKey(), r;
22470                 if(k == e.TAB){
22471                     e.stopEvent();
22472                     r = this.doc.selection.createRange();
22473                     if(r){
22474                         r.collapse(true);
22475                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22476                         this.deferFocus();
22477                     }
22478                     return;
22479                 }
22480                 
22481                 if(k == e.ENTER){
22482                     r = this.doc.selection.createRange();
22483                     if(r){
22484                         var target = r.parentElement();
22485                         if(!target || target.tagName.toLowerCase() != 'li'){
22486                             e.stopEvent();
22487                             r.pasteHTML('<br />');
22488                             r.collapse(false);
22489                             r.select();
22490                         }
22491                     }
22492                 }
22493                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22494                     this.cleanUpPaste.defer(100, this);
22495                     return;
22496                 }
22497                 
22498                 
22499             };
22500         }else if(Roo.isOpera){
22501             return function(e){
22502                 var k = e.getKey();
22503                 if(k == e.TAB){
22504                     e.stopEvent();
22505                     this.win.focus();
22506                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22507                     this.deferFocus();
22508                 }
22509                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22510                     this.cleanUpPaste.defer(100, this);
22511                     return;
22512                 }
22513                 
22514             };
22515         }else if(Roo.isSafari){
22516             return function(e){
22517                 var k = e.getKey();
22518                 
22519                 if(k == e.TAB){
22520                     e.stopEvent();
22521                     this.execCmd('InsertText','\t');
22522                     this.deferFocus();
22523                     return;
22524                 }
22525                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22526                     this.cleanUpPaste.defer(100, this);
22527                     return;
22528                 }
22529                 
22530              };
22531         }
22532     }(),
22533     
22534     getAllAncestors: function()
22535     {
22536         var p = this.getSelectedNode();
22537         var a = [];
22538         if (!p) {
22539             a.push(p); // push blank onto stack..
22540             p = this.getParentElement();
22541         }
22542         
22543         
22544         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22545             a.push(p);
22546             p = p.parentNode;
22547         }
22548         a.push(this.doc.body);
22549         return a;
22550     },
22551     lastSel : false,
22552     lastSelNode : false,
22553     
22554     
22555     getSelection : function() 
22556     {
22557         this.assignDocWin();
22558         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22559     },
22560     
22561     getSelectedNode: function() 
22562     {
22563         // this may only work on Gecko!!!
22564         
22565         // should we cache this!!!!
22566         
22567         
22568         
22569          
22570         var range = this.createRange(this.getSelection()).cloneRange();
22571         
22572         if (Roo.isIE) {
22573             var parent = range.parentElement();
22574             while (true) {
22575                 var testRange = range.duplicate();
22576                 testRange.moveToElementText(parent);
22577                 if (testRange.inRange(range)) {
22578                     break;
22579                 }
22580                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22581                     break;
22582                 }
22583                 parent = parent.parentElement;
22584             }
22585             return parent;
22586         }
22587         
22588         // is ancestor a text element.
22589         var ac =  range.commonAncestorContainer;
22590         if (ac.nodeType == 3) {
22591             ac = ac.parentNode;
22592         }
22593         
22594         var ar = ac.childNodes;
22595          
22596         var nodes = [];
22597         var other_nodes = [];
22598         var has_other_nodes = false;
22599         for (var i=0;i<ar.length;i++) {
22600             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22601                 continue;
22602             }
22603             // fullly contained node.
22604             
22605             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22606                 nodes.push(ar[i]);
22607                 continue;
22608             }
22609             
22610             // probably selected..
22611             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22612                 other_nodes.push(ar[i]);
22613                 continue;
22614             }
22615             // outer..
22616             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22617                 continue;
22618             }
22619             
22620             
22621             has_other_nodes = true;
22622         }
22623         if (!nodes.length && other_nodes.length) {
22624             nodes= other_nodes;
22625         }
22626         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22627             return false;
22628         }
22629         
22630         return nodes[0];
22631     },
22632     createRange: function(sel)
22633     {
22634         // this has strange effects when using with 
22635         // top toolbar - not sure if it's a great idea.
22636         //this.editor.contentWindow.focus();
22637         if (typeof sel != "undefined") {
22638             try {
22639                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22640             } catch(e) {
22641                 return this.doc.createRange();
22642             }
22643         } else {
22644             return this.doc.createRange();
22645         }
22646     },
22647     getParentElement: function()
22648     {
22649         
22650         this.assignDocWin();
22651         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22652         
22653         var range = this.createRange(sel);
22654          
22655         try {
22656             var p = range.commonAncestorContainer;
22657             while (p.nodeType == 3) { // text node
22658                 p = p.parentNode;
22659             }
22660             return p;
22661         } catch (e) {
22662             return null;
22663         }
22664     
22665     },
22666     /***
22667      *
22668      * Range intersection.. the hard stuff...
22669      *  '-1' = before
22670      *  '0' = hits..
22671      *  '1' = after.
22672      *         [ -- selected range --- ]
22673      *   [fail]                        [fail]
22674      *
22675      *    basically..
22676      *      if end is before start or  hits it. fail.
22677      *      if start is after end or hits it fail.
22678      *
22679      *   if either hits (but other is outside. - then it's not 
22680      *   
22681      *    
22682      **/
22683     
22684     
22685     // @see http://www.thismuchiknow.co.uk/?p=64.
22686     rangeIntersectsNode : function(range, node)
22687     {
22688         var nodeRange = node.ownerDocument.createRange();
22689         try {
22690             nodeRange.selectNode(node);
22691         } catch (e) {
22692             nodeRange.selectNodeContents(node);
22693         }
22694     
22695         var rangeStartRange = range.cloneRange();
22696         rangeStartRange.collapse(true);
22697     
22698         var rangeEndRange = range.cloneRange();
22699         rangeEndRange.collapse(false);
22700     
22701         var nodeStartRange = nodeRange.cloneRange();
22702         nodeStartRange.collapse(true);
22703     
22704         var nodeEndRange = nodeRange.cloneRange();
22705         nodeEndRange.collapse(false);
22706     
22707         return rangeStartRange.compareBoundaryPoints(
22708                  Range.START_TO_START, nodeEndRange) == -1 &&
22709                rangeEndRange.compareBoundaryPoints(
22710                  Range.START_TO_START, nodeStartRange) == 1;
22711         
22712          
22713     },
22714     rangeCompareNode : function(range, node)
22715     {
22716         var nodeRange = node.ownerDocument.createRange();
22717         try {
22718             nodeRange.selectNode(node);
22719         } catch (e) {
22720             nodeRange.selectNodeContents(node);
22721         }
22722         
22723         
22724         range.collapse(true);
22725     
22726         nodeRange.collapse(true);
22727      
22728         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22729         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22730          
22731         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22732         
22733         var nodeIsBefore   =  ss == 1;
22734         var nodeIsAfter    = ee == -1;
22735         
22736         if (nodeIsBefore && nodeIsAfter) {
22737             return 0; // outer
22738         }
22739         if (!nodeIsBefore && nodeIsAfter) {
22740             return 1; //right trailed.
22741         }
22742         
22743         if (nodeIsBefore && !nodeIsAfter) {
22744             return 2;  // left trailed.
22745         }
22746         // fully contined.
22747         return 3;
22748     },
22749
22750     // private? - in a new class?
22751     cleanUpPaste :  function()
22752     {
22753         // cleans up the whole document..
22754         Roo.log('cleanuppaste');
22755         
22756         this.cleanUpChildren(this.doc.body);
22757         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22758         if (clean != this.doc.body.innerHTML) {
22759             this.doc.body.innerHTML = clean;
22760         }
22761         
22762     },
22763     
22764     cleanWordChars : function(input) {// change the chars to hex code
22765         var he = Roo.HtmlEditorCore;
22766         
22767         var output = input;
22768         Roo.each(he.swapCodes, function(sw) { 
22769             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22770             
22771             output = output.replace(swapper, sw[1]);
22772         });
22773         
22774         return output;
22775     },
22776     
22777     
22778     cleanUpChildren : function (n)
22779     {
22780         if (!n.childNodes.length) {
22781             return;
22782         }
22783         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22784            this.cleanUpChild(n.childNodes[i]);
22785         }
22786     },
22787     
22788     
22789         
22790     
22791     cleanUpChild : function (node)
22792     {
22793         var ed = this;
22794         //console.log(node);
22795         if (node.nodeName == "#text") {
22796             // clean up silly Windows -- stuff?
22797             return; 
22798         }
22799         if (node.nodeName == "#comment") {
22800             node.parentNode.removeChild(node);
22801             // clean up silly Windows -- stuff?
22802             return; 
22803         }
22804         var lcname = node.tagName.toLowerCase();
22805         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22806         // whitelist of tags..
22807         
22808         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22809             // remove node.
22810             node.parentNode.removeChild(node);
22811             return;
22812             
22813         }
22814         
22815         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22816         
22817         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22818         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22819         
22820         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22821         //    remove_keep_children = true;
22822         //}
22823         
22824         if (remove_keep_children) {
22825             this.cleanUpChildren(node);
22826             // inserts everything just before this node...
22827             while (node.childNodes.length) {
22828                 var cn = node.childNodes[0];
22829                 node.removeChild(cn);
22830                 node.parentNode.insertBefore(cn, node);
22831             }
22832             node.parentNode.removeChild(node);
22833             return;
22834         }
22835         
22836         if (!node.attributes || !node.attributes.length) {
22837             this.cleanUpChildren(node);
22838             return;
22839         }
22840         
22841         function cleanAttr(n,v)
22842         {
22843             
22844             if (v.match(/^\./) || v.match(/^\//)) {
22845                 return;
22846             }
22847             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22848                 return;
22849             }
22850             if (v.match(/^#/)) {
22851                 return;
22852             }
22853 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22854             node.removeAttribute(n);
22855             
22856         }
22857         
22858         var cwhite = this.cwhite;
22859         var cblack = this.cblack;
22860             
22861         function cleanStyle(n,v)
22862         {
22863             if (v.match(/expression/)) { //XSS?? should we even bother..
22864                 node.removeAttribute(n);
22865                 return;
22866             }
22867             
22868             var parts = v.split(/;/);
22869             var clean = [];
22870             
22871             Roo.each(parts, function(p) {
22872                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22873                 if (!p.length) {
22874                     return true;
22875                 }
22876                 var l = p.split(':').shift().replace(/\s+/g,'');
22877                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22878                 
22879                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22880 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22881                     //node.removeAttribute(n);
22882                     return true;
22883                 }
22884                 //Roo.log()
22885                 // only allow 'c whitelisted system attributes'
22886                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22887 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22888                     //node.removeAttribute(n);
22889                     return true;
22890                 }
22891                 
22892                 
22893                  
22894                 
22895                 clean.push(p);
22896                 return true;
22897             });
22898             if (clean.length) { 
22899                 node.setAttribute(n, clean.join(';'));
22900             } else {
22901                 node.removeAttribute(n);
22902             }
22903             
22904         }
22905         
22906         
22907         for (var i = node.attributes.length-1; i > -1 ; i--) {
22908             var a = node.attributes[i];
22909             //console.log(a);
22910             
22911             if (a.name.toLowerCase().substr(0,2)=='on')  {
22912                 node.removeAttribute(a.name);
22913                 continue;
22914             }
22915             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22916                 node.removeAttribute(a.name);
22917                 continue;
22918             }
22919             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22920                 cleanAttr(a.name,a.value); // fixme..
22921                 continue;
22922             }
22923             if (a.name == 'style') {
22924                 cleanStyle(a.name,a.value);
22925                 continue;
22926             }
22927             /// clean up MS crap..
22928             // tecnically this should be a list of valid class'es..
22929             
22930             
22931             if (a.name == 'class') {
22932                 if (a.value.match(/^Mso/)) {
22933                     node.className = '';
22934                 }
22935                 
22936                 if (a.value.match(/^body$/)) {
22937                     node.className = '';
22938                 }
22939                 continue;
22940             }
22941             
22942             // style cleanup!?
22943             // class cleanup?
22944             
22945         }
22946         
22947         
22948         this.cleanUpChildren(node);
22949         
22950         
22951     },
22952     
22953     /**
22954      * Clean up MS wordisms...
22955      */
22956     cleanWord : function(node)
22957     {
22958         
22959         
22960         if (!node) {
22961             this.cleanWord(this.doc.body);
22962             return;
22963         }
22964         if (node.nodeName == "#text") {
22965             // clean up silly Windows -- stuff?
22966             return; 
22967         }
22968         if (node.nodeName == "#comment") {
22969             node.parentNode.removeChild(node);
22970             // clean up silly Windows -- stuff?
22971             return; 
22972         }
22973         
22974         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22975             node.parentNode.removeChild(node);
22976             return;
22977         }
22978         
22979         // remove - but keep children..
22980         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22981             while (node.childNodes.length) {
22982                 var cn = node.childNodes[0];
22983                 node.removeChild(cn);
22984                 node.parentNode.insertBefore(cn, node);
22985             }
22986             node.parentNode.removeChild(node);
22987             this.iterateChildren(node, this.cleanWord);
22988             return;
22989         }
22990         // clean styles
22991         if (node.className.length) {
22992             
22993             var cn = node.className.split(/\W+/);
22994             var cna = [];
22995             Roo.each(cn, function(cls) {
22996                 if (cls.match(/Mso[a-zA-Z]+/)) {
22997                     return;
22998                 }
22999                 cna.push(cls);
23000             });
23001             node.className = cna.length ? cna.join(' ') : '';
23002             if (!cna.length) {
23003                 node.removeAttribute("class");
23004             }
23005         }
23006         
23007         if (node.hasAttribute("lang")) {
23008             node.removeAttribute("lang");
23009         }
23010         
23011         if (node.hasAttribute("style")) {
23012             
23013             var styles = node.getAttribute("style").split(";");
23014             var nstyle = [];
23015             Roo.each(styles, function(s) {
23016                 if (!s.match(/:/)) {
23017                     return;
23018                 }
23019                 var kv = s.split(":");
23020                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23021                     return;
23022                 }
23023                 // what ever is left... we allow.
23024                 nstyle.push(s);
23025             });
23026             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23027             if (!nstyle.length) {
23028                 node.removeAttribute('style');
23029             }
23030         }
23031         this.iterateChildren(node, this.cleanWord);
23032         
23033         
23034         
23035     },
23036     /**
23037      * iterateChildren of a Node, calling fn each time, using this as the scole..
23038      * @param {DomNode} node node to iterate children of.
23039      * @param {Function} fn method of this class to call on each item.
23040      */
23041     iterateChildren : function(node, fn)
23042     {
23043         if (!node.childNodes.length) {
23044                 return;
23045         }
23046         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23047            fn.call(this, node.childNodes[i])
23048         }
23049     },
23050     
23051     
23052     /**
23053      * cleanTableWidths.
23054      *
23055      * Quite often pasting from word etc.. results in tables with column and widths.
23056      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23057      *
23058      */
23059     cleanTableWidths : function(node)
23060     {
23061          
23062          
23063         if (!node) {
23064             this.cleanTableWidths(this.doc.body);
23065             return;
23066         }
23067         
23068         // ignore list...
23069         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23070             return; 
23071         }
23072         Roo.log(node.tagName);
23073         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23074             this.iterateChildren(node, this.cleanTableWidths);
23075             return;
23076         }
23077         if (node.hasAttribute('width')) {
23078             node.removeAttribute('width');
23079         }
23080         
23081          
23082         if (node.hasAttribute("style")) {
23083             // pretty basic...
23084             
23085             var styles = node.getAttribute("style").split(";");
23086             var nstyle = [];
23087             Roo.each(styles, function(s) {
23088                 if (!s.match(/:/)) {
23089                     return;
23090                 }
23091                 var kv = s.split(":");
23092                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23093                     return;
23094                 }
23095                 // what ever is left... we allow.
23096                 nstyle.push(s);
23097             });
23098             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23099             if (!nstyle.length) {
23100                 node.removeAttribute('style');
23101             }
23102         }
23103         
23104         this.iterateChildren(node, this.cleanTableWidths);
23105         
23106         
23107     },
23108     
23109     
23110     
23111     
23112     domToHTML : function(currentElement, depth, nopadtext) {
23113         
23114         depth = depth || 0;
23115         nopadtext = nopadtext || false;
23116     
23117         if (!currentElement) {
23118             return this.domToHTML(this.doc.body);
23119         }
23120         
23121         //Roo.log(currentElement);
23122         var j;
23123         var allText = false;
23124         var nodeName = currentElement.nodeName;
23125         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23126         
23127         if  (nodeName == '#text') {
23128             
23129             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23130         }
23131         
23132         
23133         var ret = '';
23134         if (nodeName != 'BODY') {
23135              
23136             var i = 0;
23137             // Prints the node tagName, such as <A>, <IMG>, etc
23138             if (tagName) {
23139                 var attr = [];
23140                 for(i = 0; i < currentElement.attributes.length;i++) {
23141                     // quoting?
23142                     var aname = currentElement.attributes.item(i).name;
23143                     if (!currentElement.attributes.item(i).value.length) {
23144                         continue;
23145                     }
23146                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23147                 }
23148                 
23149                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23150             } 
23151             else {
23152                 
23153                 // eack
23154             }
23155         } else {
23156             tagName = false;
23157         }
23158         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23159             return ret;
23160         }
23161         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23162             nopadtext = true;
23163         }
23164         
23165         
23166         // Traverse the tree
23167         i = 0;
23168         var currentElementChild = currentElement.childNodes.item(i);
23169         var allText = true;
23170         var innerHTML  = '';
23171         lastnode = '';
23172         while (currentElementChild) {
23173             // Formatting code (indent the tree so it looks nice on the screen)
23174             var nopad = nopadtext;
23175             if (lastnode == 'SPAN') {
23176                 nopad  = true;
23177             }
23178             // text
23179             if  (currentElementChild.nodeName == '#text') {
23180                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23181                 toadd = nopadtext ? toadd : toadd.trim();
23182                 if (!nopad && toadd.length > 80) {
23183                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23184                 }
23185                 innerHTML  += toadd;
23186                 
23187                 i++;
23188                 currentElementChild = currentElement.childNodes.item(i);
23189                 lastNode = '';
23190                 continue;
23191             }
23192             allText = false;
23193             
23194             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23195                 
23196             // Recursively traverse the tree structure of the child node
23197             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23198             lastnode = currentElementChild.nodeName;
23199             i++;
23200             currentElementChild=currentElement.childNodes.item(i);
23201         }
23202         
23203         ret += innerHTML;
23204         
23205         if (!allText) {
23206                 // The remaining code is mostly for formatting the tree
23207             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23208         }
23209         
23210         
23211         if (tagName) {
23212             ret+= "</"+tagName+">";
23213         }
23214         return ret;
23215         
23216     },
23217         
23218     applyBlacklists : function()
23219     {
23220         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23221         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23222         
23223         this.white = [];
23224         this.black = [];
23225         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23226             if (b.indexOf(tag) > -1) {
23227                 return;
23228             }
23229             this.white.push(tag);
23230             
23231         }, this);
23232         
23233         Roo.each(w, function(tag) {
23234             if (b.indexOf(tag) > -1) {
23235                 return;
23236             }
23237             if (this.white.indexOf(tag) > -1) {
23238                 return;
23239             }
23240             this.white.push(tag);
23241             
23242         }, this);
23243         
23244         
23245         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23246             if (w.indexOf(tag) > -1) {
23247                 return;
23248             }
23249             this.black.push(tag);
23250             
23251         }, this);
23252         
23253         Roo.each(b, function(tag) {
23254             if (w.indexOf(tag) > -1) {
23255                 return;
23256             }
23257             if (this.black.indexOf(tag) > -1) {
23258                 return;
23259             }
23260             this.black.push(tag);
23261             
23262         }, this);
23263         
23264         
23265         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23266         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23267         
23268         this.cwhite = [];
23269         this.cblack = [];
23270         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23271             if (b.indexOf(tag) > -1) {
23272                 return;
23273             }
23274             this.cwhite.push(tag);
23275             
23276         }, this);
23277         
23278         Roo.each(w, function(tag) {
23279             if (b.indexOf(tag) > -1) {
23280                 return;
23281             }
23282             if (this.cwhite.indexOf(tag) > -1) {
23283                 return;
23284             }
23285             this.cwhite.push(tag);
23286             
23287         }, this);
23288         
23289         
23290         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23291             if (w.indexOf(tag) > -1) {
23292                 return;
23293             }
23294             this.cblack.push(tag);
23295             
23296         }, this);
23297         
23298         Roo.each(b, function(tag) {
23299             if (w.indexOf(tag) > -1) {
23300                 return;
23301             }
23302             if (this.cblack.indexOf(tag) > -1) {
23303                 return;
23304             }
23305             this.cblack.push(tag);
23306             
23307         }, this);
23308     },
23309     
23310     setStylesheets : function(stylesheets)
23311     {
23312         if(typeof(stylesheets) == 'string'){
23313             Roo.get(this.iframe.contentDocument.head).createChild({
23314                 tag : 'link',
23315                 rel : 'stylesheet',
23316                 type : 'text/css',
23317                 href : stylesheets
23318             });
23319             
23320             return;
23321         }
23322         var _this = this;
23323      
23324         Roo.each(stylesheets, function(s) {
23325             if(!s.length){
23326                 return;
23327             }
23328             
23329             Roo.get(_this.iframe.contentDocument.head).createChild({
23330                 tag : 'link',
23331                 rel : 'stylesheet',
23332                 type : 'text/css',
23333                 href : s
23334             });
23335         });
23336
23337         
23338     },
23339     
23340     removeStylesheets : function()
23341     {
23342         var _this = this;
23343         
23344         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23345             s.remove();
23346         });
23347     },
23348     
23349     setStyle : function(style)
23350     {
23351         Roo.get(this.iframe.contentDocument.head).createChild({
23352             tag : 'style',
23353             type : 'text/css',
23354             html : style
23355         });
23356
23357         return;
23358     }
23359     
23360     // hide stuff that is not compatible
23361     /**
23362      * @event blur
23363      * @hide
23364      */
23365     /**
23366      * @event change
23367      * @hide
23368      */
23369     /**
23370      * @event focus
23371      * @hide
23372      */
23373     /**
23374      * @event specialkey
23375      * @hide
23376      */
23377     /**
23378      * @cfg {String} fieldClass @hide
23379      */
23380     /**
23381      * @cfg {String} focusClass @hide
23382      */
23383     /**
23384      * @cfg {String} autoCreate @hide
23385      */
23386     /**
23387      * @cfg {String} inputType @hide
23388      */
23389     /**
23390      * @cfg {String} invalidClass @hide
23391      */
23392     /**
23393      * @cfg {String} invalidText @hide
23394      */
23395     /**
23396      * @cfg {String} msgFx @hide
23397      */
23398     /**
23399      * @cfg {String} validateOnBlur @hide
23400      */
23401 });
23402
23403 Roo.HtmlEditorCore.white = [
23404         'area', 'br', 'img', 'input', 'hr', 'wbr',
23405         
23406        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23407        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23408        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23409        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23410        'table',   'ul',         'xmp', 
23411        
23412        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23413       'thead',   'tr', 
23414      
23415       'dir', 'menu', 'ol', 'ul', 'dl',
23416        
23417       'embed',  'object'
23418 ];
23419
23420
23421 Roo.HtmlEditorCore.black = [
23422     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23423         'applet', // 
23424         'base',   'basefont', 'bgsound', 'blink',  'body', 
23425         'frame',  'frameset', 'head',    'html',   'ilayer', 
23426         'iframe', 'layer',  'link',     'meta',    'object',   
23427         'script', 'style' ,'title',  'xml' // clean later..
23428 ];
23429 Roo.HtmlEditorCore.clean = [
23430     'script', 'style', 'title', 'xml'
23431 ];
23432 Roo.HtmlEditorCore.remove = [
23433     'font'
23434 ];
23435 // attributes..
23436
23437 Roo.HtmlEditorCore.ablack = [
23438     'on'
23439 ];
23440     
23441 Roo.HtmlEditorCore.aclean = [ 
23442     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23443 ];
23444
23445 // protocols..
23446 Roo.HtmlEditorCore.pwhite= [
23447         'http',  'https',  'mailto'
23448 ];
23449
23450 // white listed style attributes.
23451 Roo.HtmlEditorCore.cwhite= [
23452       //  'text-align', /// default is to allow most things..
23453       
23454          
23455 //        'font-size'//??
23456 ];
23457
23458 // black listed style attributes.
23459 Roo.HtmlEditorCore.cblack= [
23460       //  'font-size' -- this can be set by the project 
23461 ];
23462
23463
23464 Roo.HtmlEditorCore.swapCodes   =[ 
23465     [    8211, "--" ], 
23466     [    8212, "--" ], 
23467     [    8216,  "'" ],  
23468     [    8217, "'" ],  
23469     [    8220, '"' ],  
23470     [    8221, '"' ],  
23471     [    8226, "*" ],  
23472     [    8230, "..." ]
23473 ]; 
23474
23475     /*
23476  * - LGPL
23477  *
23478  * HtmlEditor
23479  * 
23480  */
23481
23482 /**
23483  * @class Roo.bootstrap.HtmlEditor
23484  * @extends Roo.bootstrap.TextArea
23485  * Bootstrap HtmlEditor class
23486
23487  * @constructor
23488  * Create a new HtmlEditor
23489  * @param {Object} config The config object
23490  */
23491
23492 Roo.bootstrap.HtmlEditor = function(config){
23493     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23494     if (!this.toolbars) {
23495         this.toolbars = [];
23496     }
23497     
23498     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23499     this.addEvents({
23500             /**
23501              * @event initialize
23502              * Fires when the editor is fully initialized (including the iframe)
23503              * @param {HtmlEditor} this
23504              */
23505             initialize: true,
23506             /**
23507              * @event activate
23508              * Fires when the editor is first receives the focus. Any insertion must wait
23509              * until after this event.
23510              * @param {HtmlEditor} this
23511              */
23512             activate: true,
23513              /**
23514              * @event beforesync
23515              * Fires before the textarea is updated with content from the editor iframe. Return false
23516              * to cancel the sync.
23517              * @param {HtmlEditor} this
23518              * @param {String} html
23519              */
23520             beforesync: true,
23521              /**
23522              * @event beforepush
23523              * Fires before the iframe editor is updated with content from the textarea. Return false
23524              * to cancel the push.
23525              * @param {HtmlEditor} this
23526              * @param {String} html
23527              */
23528             beforepush: true,
23529              /**
23530              * @event sync
23531              * Fires when the textarea is updated with content from the editor iframe.
23532              * @param {HtmlEditor} this
23533              * @param {String} html
23534              */
23535             sync: true,
23536              /**
23537              * @event push
23538              * Fires when the iframe editor is updated with content from the textarea.
23539              * @param {HtmlEditor} this
23540              * @param {String} html
23541              */
23542             push: true,
23543              /**
23544              * @event editmodechange
23545              * Fires when the editor switches edit modes
23546              * @param {HtmlEditor} this
23547              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23548              */
23549             editmodechange: true,
23550             /**
23551              * @event editorevent
23552              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23553              * @param {HtmlEditor} this
23554              */
23555             editorevent: true,
23556             /**
23557              * @event firstfocus
23558              * Fires when on first focus - needed by toolbars..
23559              * @param {HtmlEditor} this
23560              */
23561             firstfocus: true,
23562             /**
23563              * @event autosave
23564              * Auto save the htmlEditor value as a file into Events
23565              * @param {HtmlEditor} this
23566              */
23567             autosave: true,
23568             /**
23569              * @event savedpreview
23570              * preview the saved version of htmlEditor
23571              * @param {HtmlEditor} this
23572              */
23573             savedpreview: true
23574         });
23575 };
23576
23577
23578 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23579     
23580     
23581       /**
23582      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23583      */
23584     toolbars : false,
23585     
23586      /**
23587     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23588     */
23589     btns : [],
23590    
23591      /**
23592      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23593      *                        Roo.resizable.
23594      */
23595     resizable : false,
23596      /**
23597      * @cfg {Number} height (in pixels)
23598      */   
23599     height: 300,
23600    /**
23601      * @cfg {Number} width (in pixels)
23602      */   
23603     width: false,
23604     
23605     /**
23606      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23607      * 
23608      */
23609     stylesheets: false,
23610     
23611     // id of frame..
23612     frameId: false,
23613     
23614     // private properties
23615     validationEvent : false,
23616     deferHeight: true,
23617     initialized : false,
23618     activated : false,
23619     
23620     onFocus : Roo.emptyFn,
23621     iframePad:3,
23622     hideMode:'offsets',
23623     
23624     tbContainer : false,
23625     
23626     bodyCls : '',
23627     
23628     toolbarContainer :function() {
23629         return this.wrap.select('.x-html-editor-tb',true).first();
23630     },
23631
23632     /**
23633      * Protected method that will not generally be called directly. It
23634      * is called when the editor creates its toolbar. Override this method if you need to
23635      * add custom toolbar buttons.
23636      * @param {HtmlEditor} editor
23637      */
23638     createToolbar : function(){
23639         Roo.log('renewing');
23640         Roo.log("create toolbars");
23641         
23642         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23643         this.toolbars[0].render(this.toolbarContainer());
23644         
23645         return;
23646         
23647 //        if (!editor.toolbars || !editor.toolbars.length) {
23648 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23649 //        }
23650 //        
23651 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23652 //            editor.toolbars[i] = Roo.factory(
23653 //                    typeof(editor.toolbars[i]) == 'string' ?
23654 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23655 //                Roo.bootstrap.HtmlEditor);
23656 //            editor.toolbars[i].init(editor);
23657 //        }
23658     },
23659
23660      
23661     // private
23662     onRender : function(ct, position)
23663     {
23664        // Roo.log("Call onRender: " + this.xtype);
23665         var _t = this;
23666         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23667       
23668         this.wrap = this.inputEl().wrap({
23669             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23670         });
23671         
23672         this.editorcore.onRender(ct, position);
23673          
23674         if (this.resizable) {
23675             this.resizeEl = new Roo.Resizable(this.wrap, {
23676                 pinned : true,
23677                 wrap: true,
23678                 dynamic : true,
23679                 minHeight : this.height,
23680                 height: this.height,
23681                 handles : this.resizable,
23682                 width: this.width,
23683                 listeners : {
23684                     resize : function(r, w, h) {
23685                         _t.onResize(w,h); // -something
23686                     }
23687                 }
23688             });
23689             
23690         }
23691         this.createToolbar(this);
23692        
23693         
23694         if(!this.width && this.resizable){
23695             this.setSize(this.wrap.getSize());
23696         }
23697         if (this.resizeEl) {
23698             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23699             // should trigger onReize..
23700         }
23701         
23702     },
23703
23704     // private
23705     onResize : function(w, h)
23706     {
23707         Roo.log('resize: ' +w + ',' + h );
23708         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23709         var ew = false;
23710         var eh = false;
23711         
23712         if(this.inputEl() ){
23713             if(typeof w == 'number'){
23714                 var aw = w - this.wrap.getFrameWidth('lr');
23715                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23716                 ew = aw;
23717             }
23718             if(typeof h == 'number'){
23719                  var tbh = -11;  // fixme it needs to tool bar size!
23720                 for (var i =0; i < this.toolbars.length;i++) {
23721                     // fixme - ask toolbars for heights?
23722                     tbh += this.toolbars[i].el.getHeight();
23723                     //if (this.toolbars[i].footer) {
23724                     //    tbh += this.toolbars[i].footer.el.getHeight();
23725                     //}
23726                 }
23727               
23728                 
23729                 
23730                 
23731                 
23732                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23733                 ah -= 5; // knock a few pixes off for look..
23734                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23735                 var eh = ah;
23736             }
23737         }
23738         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23739         this.editorcore.onResize(ew,eh);
23740         
23741     },
23742
23743     /**
23744      * Toggles the editor between standard and source edit mode.
23745      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23746      */
23747     toggleSourceEdit : function(sourceEditMode)
23748     {
23749         this.editorcore.toggleSourceEdit(sourceEditMode);
23750         
23751         if(this.editorcore.sourceEditMode){
23752             Roo.log('editor - showing textarea');
23753             
23754 //            Roo.log('in');
23755 //            Roo.log(this.syncValue());
23756             this.syncValue();
23757             this.inputEl().removeClass(['hide', 'x-hidden']);
23758             this.inputEl().dom.removeAttribute('tabIndex');
23759             this.inputEl().focus();
23760         }else{
23761             Roo.log('editor - hiding textarea');
23762 //            Roo.log('out')
23763 //            Roo.log(this.pushValue()); 
23764             this.pushValue();
23765             
23766             this.inputEl().addClass(['hide', 'x-hidden']);
23767             this.inputEl().dom.setAttribute('tabIndex', -1);
23768             //this.deferFocus();
23769         }
23770          
23771         if(this.resizable){
23772             this.setSize(this.wrap.getSize());
23773         }
23774         
23775         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23776     },
23777  
23778     // private (for BoxComponent)
23779     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23780
23781     // private (for BoxComponent)
23782     getResizeEl : function(){
23783         return this.wrap;
23784     },
23785
23786     // private (for BoxComponent)
23787     getPositionEl : function(){
23788         return this.wrap;
23789     },
23790
23791     // private
23792     initEvents : function(){
23793         this.originalValue = this.getValue();
23794     },
23795
23796 //    /**
23797 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23798 //     * @method
23799 //     */
23800 //    markInvalid : Roo.emptyFn,
23801 //    /**
23802 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23803 //     * @method
23804 //     */
23805 //    clearInvalid : Roo.emptyFn,
23806
23807     setValue : function(v){
23808         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23809         this.editorcore.pushValue();
23810     },
23811
23812      
23813     // private
23814     deferFocus : function(){
23815         this.focus.defer(10, this);
23816     },
23817
23818     // doc'ed in Field
23819     focus : function(){
23820         this.editorcore.focus();
23821         
23822     },
23823       
23824
23825     // private
23826     onDestroy : function(){
23827         
23828         
23829         
23830         if(this.rendered){
23831             
23832             for (var i =0; i < this.toolbars.length;i++) {
23833                 // fixme - ask toolbars for heights?
23834                 this.toolbars[i].onDestroy();
23835             }
23836             
23837             this.wrap.dom.innerHTML = '';
23838             this.wrap.remove();
23839         }
23840     },
23841
23842     // private
23843     onFirstFocus : function(){
23844         //Roo.log("onFirstFocus");
23845         this.editorcore.onFirstFocus();
23846          for (var i =0; i < this.toolbars.length;i++) {
23847             this.toolbars[i].onFirstFocus();
23848         }
23849         
23850     },
23851     
23852     // private
23853     syncValue : function()
23854     {   
23855         this.editorcore.syncValue();
23856     },
23857     
23858     pushValue : function()
23859     {   
23860         this.editorcore.pushValue();
23861     }
23862      
23863     
23864     // hide stuff that is not compatible
23865     /**
23866      * @event blur
23867      * @hide
23868      */
23869     /**
23870      * @event change
23871      * @hide
23872      */
23873     /**
23874      * @event focus
23875      * @hide
23876      */
23877     /**
23878      * @event specialkey
23879      * @hide
23880      */
23881     /**
23882      * @cfg {String} fieldClass @hide
23883      */
23884     /**
23885      * @cfg {String} focusClass @hide
23886      */
23887     /**
23888      * @cfg {String} autoCreate @hide
23889      */
23890     /**
23891      * @cfg {String} inputType @hide
23892      */
23893     /**
23894      * @cfg {String} invalidClass @hide
23895      */
23896     /**
23897      * @cfg {String} invalidText @hide
23898      */
23899     /**
23900      * @cfg {String} msgFx @hide
23901      */
23902     /**
23903      * @cfg {String} validateOnBlur @hide
23904      */
23905 });
23906  
23907     
23908    
23909    
23910    
23911       
23912 Roo.namespace('Roo.bootstrap.htmleditor');
23913 /**
23914  * @class Roo.bootstrap.HtmlEditorToolbar1
23915  * Basic Toolbar
23916  * 
23917  * Usage:
23918  *
23919  new Roo.bootstrap.HtmlEditor({
23920     ....
23921     toolbars : [
23922         new Roo.bootstrap.HtmlEditorToolbar1({
23923             disable : { fonts: 1 , format: 1, ..., ... , ...],
23924             btns : [ .... ]
23925         })
23926     }
23927      
23928  * 
23929  * @cfg {Object} disable List of elements to disable..
23930  * @cfg {Array} btns List of additional buttons.
23931  * 
23932  * 
23933  * NEEDS Extra CSS? 
23934  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23935  */
23936  
23937 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23938 {
23939     
23940     Roo.apply(this, config);
23941     
23942     // default disabled, based on 'good practice'..
23943     this.disable = this.disable || {};
23944     Roo.applyIf(this.disable, {
23945         fontSize : true,
23946         colors : true,
23947         specialElements : true
23948     });
23949     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23950     
23951     this.editor = config.editor;
23952     this.editorcore = config.editor.editorcore;
23953     
23954     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23955     
23956     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23957     // dont call parent... till later.
23958 }
23959 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23960      
23961     bar : true,
23962     
23963     editor : false,
23964     editorcore : false,
23965     
23966     
23967     formats : [
23968         "p" ,  
23969         "h1","h2","h3","h4","h5","h6", 
23970         "pre", "code", 
23971         "abbr", "acronym", "address", "cite", "samp", "var",
23972         'div','span'
23973     ],
23974     
23975     onRender : function(ct, position)
23976     {
23977        // Roo.log("Call onRender: " + this.xtype);
23978         
23979        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23980        Roo.log(this.el);
23981        this.el.dom.style.marginBottom = '0';
23982        var _this = this;
23983        var editorcore = this.editorcore;
23984        var editor= this.editor;
23985        
23986        var children = [];
23987        var btn = function(id,cmd , toggle, handler, html){
23988        
23989             var  event = toggle ? 'toggle' : 'click';
23990        
23991             var a = {
23992                 size : 'sm',
23993                 xtype: 'Button',
23994                 xns: Roo.bootstrap,
23995                 //glyphicon : id,
23996                 fa: id,
23997                 cmd : id || cmd,
23998                 enableToggle:toggle !== false,
23999                 html : html || '',
24000                 pressed : toggle ? false : null,
24001                 listeners : {}
24002             };
24003             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24004                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24005             };
24006             children.push(a);
24007             return a;
24008        }
24009        
24010     //    var cb_box = function...
24011         
24012         var style = {
24013                 xtype: 'Button',
24014                 size : 'sm',
24015                 xns: Roo.bootstrap,
24016                 fa : 'font',
24017                 //html : 'submit'
24018                 menu : {
24019                     xtype: 'Menu',
24020                     xns: Roo.bootstrap,
24021                     items:  []
24022                 }
24023         };
24024         Roo.each(this.formats, function(f) {
24025             style.menu.items.push({
24026                 xtype :'MenuItem',
24027                 xns: Roo.bootstrap,
24028                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24029                 tagname : f,
24030                 listeners : {
24031                     click : function()
24032                     {
24033                         editorcore.insertTag(this.tagname);
24034                         editor.focus();
24035                     }
24036                 }
24037                 
24038             });
24039         });
24040         children.push(style);   
24041         
24042         btn('bold',false,true);
24043         btn('italic',false,true);
24044         btn('align-left', 'justifyleft',true);
24045         btn('align-center', 'justifycenter',true);
24046         btn('align-right' , 'justifyright',true);
24047         btn('link', false, false, function(btn) {
24048             //Roo.log("create link?");
24049             var url = prompt(this.createLinkText, this.defaultLinkValue);
24050             if(url && url != 'http:/'+'/'){
24051                 this.editorcore.relayCmd('createlink', url);
24052             }
24053         }),
24054         btn('list','insertunorderedlist',true);
24055         btn('pencil', false,true, function(btn){
24056                 Roo.log(this);
24057                 this.toggleSourceEdit(btn.pressed);
24058         });
24059         
24060         if (this.editor.btns.length > 0) {
24061             for (var i = 0; i<this.editor.btns.length; i++) {
24062                 children.push(this.editor.btns[i]);
24063             }
24064         }
24065         
24066         /*
24067         var cog = {
24068                 xtype: 'Button',
24069                 size : 'sm',
24070                 xns: Roo.bootstrap,
24071                 glyphicon : 'cog',
24072                 //html : 'submit'
24073                 menu : {
24074                     xtype: 'Menu',
24075                     xns: Roo.bootstrap,
24076                     items:  []
24077                 }
24078         };
24079         
24080         cog.menu.items.push({
24081             xtype :'MenuItem',
24082             xns: Roo.bootstrap,
24083             html : Clean styles,
24084             tagname : f,
24085             listeners : {
24086                 click : function()
24087                 {
24088                     editorcore.insertTag(this.tagname);
24089                     editor.focus();
24090                 }
24091             }
24092             
24093         });
24094        */
24095         
24096          
24097        this.xtype = 'NavSimplebar';
24098         
24099         for(var i=0;i< children.length;i++) {
24100             
24101             this.buttons.add(this.addxtypeChild(children[i]));
24102             
24103         }
24104         
24105         editor.on('editorevent', this.updateToolbar, this);
24106     },
24107     onBtnClick : function(id)
24108     {
24109        this.editorcore.relayCmd(id);
24110        this.editorcore.focus();
24111     },
24112     
24113     /**
24114      * Protected method that will not generally be called directly. It triggers
24115      * a toolbar update by reading the markup state of the current selection in the editor.
24116      */
24117     updateToolbar: function(){
24118
24119         if(!this.editorcore.activated){
24120             this.editor.onFirstFocus(); // is this neeed?
24121             return;
24122         }
24123
24124         var btns = this.buttons; 
24125         var doc = this.editorcore.doc;
24126         btns.get('bold').setActive(doc.queryCommandState('bold'));
24127         btns.get('italic').setActive(doc.queryCommandState('italic'));
24128         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24129         
24130         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24131         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24132         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24133         
24134         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24135         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24136          /*
24137         
24138         var ans = this.editorcore.getAllAncestors();
24139         if (this.formatCombo) {
24140             
24141             
24142             var store = this.formatCombo.store;
24143             this.formatCombo.setValue("");
24144             for (var i =0; i < ans.length;i++) {
24145                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24146                     // select it..
24147                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24148                     break;
24149                 }
24150             }
24151         }
24152         
24153         
24154         
24155         // hides menus... - so this cant be on a menu...
24156         Roo.bootstrap.MenuMgr.hideAll();
24157         */
24158         Roo.bootstrap.MenuMgr.hideAll();
24159         //this.editorsyncValue();
24160     },
24161     onFirstFocus: function() {
24162         this.buttons.each(function(item){
24163            item.enable();
24164         });
24165     },
24166     toggleSourceEdit : function(sourceEditMode){
24167         
24168           
24169         if(sourceEditMode){
24170             Roo.log("disabling buttons");
24171            this.buttons.each( function(item){
24172                 if(item.cmd != 'pencil'){
24173                     item.disable();
24174                 }
24175             });
24176           
24177         }else{
24178             Roo.log("enabling buttons");
24179             if(this.editorcore.initialized){
24180                 this.buttons.each( function(item){
24181                     item.enable();
24182                 });
24183             }
24184             
24185         }
24186         Roo.log("calling toggole on editor");
24187         // tell the editor that it's been pressed..
24188         this.editor.toggleSourceEdit(sourceEditMode);
24189        
24190     }
24191 });
24192
24193
24194
24195
24196
24197 /**
24198  * @class Roo.bootstrap.Table.AbstractSelectionModel
24199  * @extends Roo.util.Observable
24200  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24201  * implemented by descendant classes.  This class should not be directly instantiated.
24202  * @constructor
24203  */
24204 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24205     this.locked = false;
24206     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24207 };
24208
24209
24210 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24211     /** @ignore Called by the grid automatically. Do not call directly. */
24212     init : function(grid){
24213         this.grid = grid;
24214         this.initEvents();
24215     },
24216
24217     /**
24218      * Locks the selections.
24219      */
24220     lock : function(){
24221         this.locked = true;
24222     },
24223
24224     /**
24225      * Unlocks the selections.
24226      */
24227     unlock : function(){
24228         this.locked = false;
24229     },
24230
24231     /**
24232      * Returns true if the selections are locked.
24233      * @return {Boolean}
24234      */
24235     isLocked : function(){
24236         return this.locked;
24237     }
24238 });
24239 /**
24240  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24241  * @class Roo.bootstrap.Table.RowSelectionModel
24242  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24243  * It supports multiple selections and keyboard selection/navigation. 
24244  * @constructor
24245  * @param {Object} config
24246  */
24247
24248 Roo.bootstrap.Table.RowSelectionModel = function(config){
24249     Roo.apply(this, config);
24250     this.selections = new Roo.util.MixedCollection(false, function(o){
24251         return o.id;
24252     });
24253
24254     this.last = false;
24255     this.lastActive = false;
24256
24257     this.addEvents({
24258         /**
24259              * @event selectionchange
24260              * Fires when the selection changes
24261              * @param {SelectionModel} this
24262              */
24263             "selectionchange" : true,
24264         /**
24265              * @event afterselectionchange
24266              * Fires after the selection changes (eg. by key press or clicking)
24267              * @param {SelectionModel} this
24268              */
24269             "afterselectionchange" : true,
24270         /**
24271              * @event beforerowselect
24272              * Fires when a row is selected being selected, return false to cancel.
24273              * @param {SelectionModel} this
24274              * @param {Number} rowIndex The selected index
24275              * @param {Boolean} keepExisting False if other selections will be cleared
24276              */
24277             "beforerowselect" : true,
24278         /**
24279              * @event rowselect
24280              * Fires when a row is selected.
24281              * @param {SelectionModel} this
24282              * @param {Number} rowIndex The selected index
24283              * @param {Roo.data.Record} r The record
24284              */
24285             "rowselect" : true,
24286         /**
24287              * @event rowdeselect
24288              * Fires when a row is deselected.
24289              * @param {SelectionModel} this
24290              * @param {Number} rowIndex The selected index
24291              */
24292         "rowdeselect" : true
24293     });
24294     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24295     this.locked = false;
24296  };
24297
24298 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24299     /**
24300      * @cfg {Boolean} singleSelect
24301      * True to allow selection of only one row at a time (defaults to false)
24302      */
24303     singleSelect : false,
24304
24305     // private
24306     initEvents : function()
24307     {
24308
24309         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24310         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24311         //}else{ // allow click to work like normal
24312          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24313         //}
24314         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24315         this.grid.on("rowclick", this.handleMouseDown, this);
24316         
24317         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24318             "up" : function(e){
24319                 if(!e.shiftKey){
24320                     this.selectPrevious(e.shiftKey);
24321                 }else if(this.last !== false && this.lastActive !== false){
24322                     var last = this.last;
24323                     this.selectRange(this.last,  this.lastActive-1);
24324                     this.grid.getView().focusRow(this.lastActive);
24325                     if(last !== false){
24326                         this.last = last;
24327                     }
24328                 }else{
24329                     this.selectFirstRow();
24330                 }
24331                 this.fireEvent("afterselectionchange", this);
24332             },
24333             "down" : function(e){
24334                 if(!e.shiftKey){
24335                     this.selectNext(e.shiftKey);
24336                 }else if(this.last !== false && this.lastActive !== false){
24337                     var last = this.last;
24338                     this.selectRange(this.last,  this.lastActive+1);
24339                     this.grid.getView().focusRow(this.lastActive);
24340                     if(last !== false){
24341                         this.last = last;
24342                     }
24343                 }else{
24344                     this.selectFirstRow();
24345                 }
24346                 this.fireEvent("afterselectionchange", this);
24347             },
24348             scope: this
24349         });
24350         this.grid.store.on('load', function(){
24351             this.selections.clear();
24352         },this);
24353         /*
24354         var view = this.grid.view;
24355         view.on("refresh", this.onRefresh, this);
24356         view.on("rowupdated", this.onRowUpdated, this);
24357         view.on("rowremoved", this.onRemove, this);
24358         */
24359     },
24360
24361     // private
24362     onRefresh : function()
24363     {
24364         var ds = this.grid.store, i, v = this.grid.view;
24365         var s = this.selections;
24366         s.each(function(r){
24367             if((i = ds.indexOfId(r.id)) != -1){
24368                 v.onRowSelect(i);
24369             }else{
24370                 s.remove(r);
24371             }
24372         });
24373     },
24374
24375     // private
24376     onRemove : function(v, index, r){
24377         this.selections.remove(r);
24378     },
24379
24380     // private
24381     onRowUpdated : function(v, index, r){
24382         if(this.isSelected(r)){
24383             v.onRowSelect(index);
24384         }
24385     },
24386
24387     /**
24388      * Select records.
24389      * @param {Array} records The records to select
24390      * @param {Boolean} keepExisting (optional) True to keep existing selections
24391      */
24392     selectRecords : function(records, keepExisting)
24393     {
24394         if(!keepExisting){
24395             this.clearSelections();
24396         }
24397             var ds = this.grid.store;
24398         for(var i = 0, len = records.length; i < len; i++){
24399             this.selectRow(ds.indexOf(records[i]), true);
24400         }
24401     },
24402
24403     /**
24404      * Gets the number of selected rows.
24405      * @return {Number}
24406      */
24407     getCount : function(){
24408         return this.selections.length;
24409     },
24410
24411     /**
24412      * Selects the first row in the grid.
24413      */
24414     selectFirstRow : function(){
24415         this.selectRow(0);
24416     },
24417
24418     /**
24419      * Select the last row.
24420      * @param {Boolean} keepExisting (optional) True to keep existing selections
24421      */
24422     selectLastRow : function(keepExisting){
24423         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24424         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24425     },
24426
24427     /**
24428      * Selects the row immediately following the last selected row.
24429      * @param {Boolean} keepExisting (optional) True to keep existing selections
24430      */
24431     selectNext : function(keepExisting)
24432     {
24433             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24434             this.selectRow(this.last+1, keepExisting);
24435             this.grid.getView().focusRow(this.last);
24436         }
24437     },
24438
24439     /**
24440      * Selects the row that precedes the last selected row.
24441      * @param {Boolean} keepExisting (optional) True to keep existing selections
24442      */
24443     selectPrevious : function(keepExisting){
24444         if(this.last){
24445             this.selectRow(this.last-1, keepExisting);
24446             this.grid.getView().focusRow(this.last);
24447         }
24448     },
24449
24450     /**
24451      * Returns the selected records
24452      * @return {Array} Array of selected records
24453      */
24454     getSelections : function(){
24455         return [].concat(this.selections.items);
24456     },
24457
24458     /**
24459      * Returns the first selected record.
24460      * @return {Record}
24461      */
24462     getSelected : function(){
24463         return this.selections.itemAt(0);
24464     },
24465
24466
24467     /**
24468      * Clears all selections.
24469      */
24470     clearSelections : function(fast)
24471     {
24472         if(this.locked) {
24473             return;
24474         }
24475         if(fast !== true){
24476                 var ds = this.grid.store;
24477             var s = this.selections;
24478             s.each(function(r){
24479                 this.deselectRow(ds.indexOfId(r.id));
24480             }, this);
24481             s.clear();
24482         }else{
24483             this.selections.clear();
24484         }
24485         this.last = false;
24486     },
24487
24488
24489     /**
24490      * Selects all rows.
24491      */
24492     selectAll : function(){
24493         if(this.locked) {
24494             return;
24495         }
24496         this.selections.clear();
24497         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24498             this.selectRow(i, true);
24499         }
24500     },
24501
24502     /**
24503      * Returns True if there is a selection.
24504      * @return {Boolean}
24505      */
24506     hasSelection : function(){
24507         return this.selections.length > 0;
24508     },
24509
24510     /**
24511      * Returns True if the specified row is selected.
24512      * @param {Number/Record} record The record or index of the record to check
24513      * @return {Boolean}
24514      */
24515     isSelected : function(index){
24516             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24517         return (r && this.selections.key(r.id) ? true : false);
24518     },
24519
24520     /**
24521      * Returns True if the specified record id is selected.
24522      * @param {String} id The id of record to check
24523      * @return {Boolean}
24524      */
24525     isIdSelected : function(id){
24526         return (this.selections.key(id) ? true : false);
24527     },
24528
24529
24530     // private
24531     handleMouseDBClick : function(e, t){
24532         
24533     },
24534     // private
24535     handleMouseDown : function(e, t)
24536     {
24537             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24538         if(this.isLocked() || rowIndex < 0 ){
24539             return;
24540         };
24541         if(e.shiftKey && this.last !== false){
24542             var last = this.last;
24543             this.selectRange(last, rowIndex, e.ctrlKey);
24544             this.last = last; // reset the last
24545             t.focus();
24546     
24547         }else{
24548             var isSelected = this.isSelected(rowIndex);
24549             //Roo.log("select row:" + rowIndex);
24550             if(isSelected){
24551                 this.deselectRow(rowIndex);
24552             } else {
24553                         this.selectRow(rowIndex, true);
24554             }
24555     
24556             /*
24557                 if(e.button !== 0 && isSelected){
24558                 alert('rowIndex 2: ' + rowIndex);
24559                     view.focusRow(rowIndex);
24560                 }else if(e.ctrlKey && isSelected){
24561                     this.deselectRow(rowIndex);
24562                 }else if(!isSelected){
24563                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24564                     view.focusRow(rowIndex);
24565                 }
24566             */
24567         }
24568         this.fireEvent("afterselectionchange", this);
24569     },
24570     // private
24571     handleDragableRowClick :  function(grid, rowIndex, e) 
24572     {
24573         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24574             this.selectRow(rowIndex, false);
24575             grid.view.focusRow(rowIndex);
24576              this.fireEvent("afterselectionchange", this);
24577         }
24578     },
24579     
24580     /**
24581      * Selects multiple rows.
24582      * @param {Array} rows Array of the indexes of the row to select
24583      * @param {Boolean} keepExisting (optional) True to keep existing selections
24584      */
24585     selectRows : function(rows, keepExisting){
24586         if(!keepExisting){
24587             this.clearSelections();
24588         }
24589         for(var i = 0, len = rows.length; i < len; i++){
24590             this.selectRow(rows[i], true);
24591         }
24592     },
24593
24594     /**
24595      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24596      * @param {Number} startRow The index of the first row in the range
24597      * @param {Number} endRow The index of the last row in the range
24598      * @param {Boolean} keepExisting (optional) True to retain existing selections
24599      */
24600     selectRange : function(startRow, endRow, keepExisting){
24601         if(this.locked) {
24602             return;
24603         }
24604         if(!keepExisting){
24605             this.clearSelections();
24606         }
24607         if(startRow <= endRow){
24608             for(var i = startRow; i <= endRow; i++){
24609                 this.selectRow(i, true);
24610             }
24611         }else{
24612             for(var i = startRow; i >= endRow; i--){
24613                 this.selectRow(i, true);
24614             }
24615         }
24616     },
24617
24618     /**
24619      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24620      * @param {Number} startRow The index of the first row in the range
24621      * @param {Number} endRow The index of the last row in the range
24622      */
24623     deselectRange : function(startRow, endRow, preventViewNotify){
24624         if(this.locked) {
24625             return;
24626         }
24627         for(var i = startRow; i <= endRow; i++){
24628             this.deselectRow(i, preventViewNotify);
24629         }
24630     },
24631
24632     /**
24633      * Selects a row.
24634      * @param {Number} row The index of the row to select
24635      * @param {Boolean} keepExisting (optional) True to keep existing selections
24636      */
24637     selectRow : function(index, keepExisting, preventViewNotify)
24638     {
24639             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24640             return;
24641         }
24642         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24643             if(!keepExisting || this.singleSelect){
24644                 this.clearSelections();
24645             }
24646             
24647             var r = this.grid.store.getAt(index);
24648             //console.log('selectRow - record id :' + r.id);
24649             
24650             this.selections.add(r);
24651             this.last = this.lastActive = index;
24652             if(!preventViewNotify){
24653                 var proxy = new Roo.Element(
24654                                 this.grid.getRowDom(index)
24655                 );
24656                 proxy.addClass('bg-info info');
24657             }
24658             this.fireEvent("rowselect", this, index, r);
24659             this.fireEvent("selectionchange", this);
24660         }
24661     },
24662
24663     /**
24664      * Deselects a row.
24665      * @param {Number} row The index of the row to deselect
24666      */
24667     deselectRow : function(index, preventViewNotify)
24668     {
24669         if(this.locked) {
24670             return;
24671         }
24672         if(this.last == index){
24673             this.last = false;
24674         }
24675         if(this.lastActive == index){
24676             this.lastActive = false;
24677         }
24678         
24679         var r = this.grid.store.getAt(index);
24680         if (!r) {
24681             return;
24682         }
24683         
24684         this.selections.remove(r);
24685         //.console.log('deselectRow - record id :' + r.id);
24686         if(!preventViewNotify){
24687         
24688             var proxy = new Roo.Element(
24689                 this.grid.getRowDom(index)
24690             );
24691             proxy.removeClass('bg-info info');
24692         }
24693         this.fireEvent("rowdeselect", this, index);
24694         this.fireEvent("selectionchange", this);
24695     },
24696
24697     // private
24698     restoreLast : function(){
24699         if(this._last){
24700             this.last = this._last;
24701         }
24702     },
24703
24704     // private
24705     acceptsNav : function(row, col, cm){
24706         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24707     },
24708
24709     // private
24710     onEditorKey : function(field, e){
24711         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24712         if(k == e.TAB){
24713             e.stopEvent();
24714             ed.completeEdit();
24715             if(e.shiftKey){
24716                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24717             }else{
24718                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24719             }
24720         }else if(k == e.ENTER && !e.ctrlKey){
24721             e.stopEvent();
24722             ed.completeEdit();
24723             if(e.shiftKey){
24724                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24725             }else{
24726                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24727             }
24728         }else if(k == e.ESC){
24729             ed.cancelEdit();
24730         }
24731         if(newCell){
24732             g.startEditing(newCell[0], newCell[1]);
24733         }
24734     }
24735 });
24736 /*
24737  * Based on:
24738  * Ext JS Library 1.1.1
24739  * Copyright(c) 2006-2007, Ext JS, LLC.
24740  *
24741  * Originally Released Under LGPL - original licence link has changed is not relivant.
24742  *
24743  * Fork - LGPL
24744  * <script type="text/javascript">
24745  */
24746  
24747 /**
24748  * @class Roo.bootstrap.PagingToolbar
24749  * @extends Roo.bootstrap.NavSimplebar
24750  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24751  * @constructor
24752  * Create a new PagingToolbar
24753  * @param {Object} config The config object
24754  * @param {Roo.data.Store} store
24755  */
24756 Roo.bootstrap.PagingToolbar = function(config)
24757 {
24758     // old args format still supported... - xtype is prefered..
24759         // created from xtype...
24760     
24761     this.ds = config.dataSource;
24762     
24763     if (config.store && !this.ds) {
24764         this.store= Roo.factory(config.store, Roo.data);
24765         this.ds = this.store;
24766         this.ds.xmodule = this.xmodule || false;
24767     }
24768     
24769     this.toolbarItems = [];
24770     if (config.items) {
24771         this.toolbarItems = config.items;
24772     }
24773     
24774     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24775     
24776     this.cursor = 0;
24777     
24778     if (this.ds) { 
24779         this.bind(this.ds);
24780     }
24781     
24782     if (Roo.bootstrap.version == 4) {
24783         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24784     } else {
24785         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24786     }
24787     
24788 };
24789
24790 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24791     /**
24792      * @cfg {Roo.data.Store} dataSource
24793      * The underlying data store providing the paged data
24794      */
24795     /**
24796      * @cfg {String/HTMLElement/Element} container
24797      * container The id or element that will contain the toolbar
24798      */
24799     /**
24800      * @cfg {Boolean} displayInfo
24801      * True to display the displayMsg (defaults to false)
24802      */
24803     /**
24804      * @cfg {Number} pageSize
24805      * The number of records to display per page (defaults to 20)
24806      */
24807     pageSize: 20,
24808     /**
24809      * @cfg {String} displayMsg
24810      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24811      */
24812     displayMsg : 'Displaying {0} - {1} of {2}',
24813     /**
24814      * @cfg {String} emptyMsg
24815      * The message to display when no records are found (defaults to "No data to display")
24816      */
24817     emptyMsg : 'No data to display',
24818     /**
24819      * Customizable piece of the default paging text (defaults to "Page")
24820      * @type String
24821      */
24822     beforePageText : "Page",
24823     /**
24824      * Customizable piece of the default paging text (defaults to "of %0")
24825      * @type String
24826      */
24827     afterPageText : "of {0}",
24828     /**
24829      * Customizable piece of the default paging text (defaults to "First Page")
24830      * @type String
24831      */
24832     firstText : "First Page",
24833     /**
24834      * Customizable piece of the default paging text (defaults to "Previous Page")
24835      * @type String
24836      */
24837     prevText : "Previous Page",
24838     /**
24839      * Customizable piece of the default paging text (defaults to "Next Page")
24840      * @type String
24841      */
24842     nextText : "Next Page",
24843     /**
24844      * Customizable piece of the default paging text (defaults to "Last Page")
24845      * @type String
24846      */
24847     lastText : "Last Page",
24848     /**
24849      * Customizable piece of the default paging text (defaults to "Refresh")
24850      * @type String
24851      */
24852     refreshText : "Refresh",
24853
24854     buttons : false,
24855     // private
24856     onRender : function(ct, position) 
24857     {
24858         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24859         this.navgroup.parentId = this.id;
24860         this.navgroup.onRender(this.el, null);
24861         // add the buttons to the navgroup
24862         
24863         if(this.displayInfo){
24864             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24865             this.displayEl = this.el.select('.x-paging-info', true).first();
24866 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24867 //            this.displayEl = navel.el.select('span',true).first();
24868         }
24869         
24870         var _this = this;
24871         
24872         if(this.buttons){
24873             Roo.each(_this.buttons, function(e){ // this might need to use render????
24874                Roo.factory(e).render(_this.el);
24875             });
24876         }
24877             
24878         Roo.each(_this.toolbarItems, function(e) {
24879             _this.navgroup.addItem(e);
24880         });
24881         
24882         
24883         this.first = this.navgroup.addItem({
24884             tooltip: this.firstText,
24885             cls: "prev btn-outline-secondary",
24886             html : ' <i class="fa fa-step-backward"></i>',
24887             disabled: true,
24888             preventDefault: true,
24889             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24890         });
24891         
24892         this.prev =  this.navgroup.addItem({
24893             tooltip: this.prevText,
24894             cls: "prev btn-outline-secondary",
24895             html : ' <i class="fa fa-backward"></i>',
24896             disabled: true,
24897             preventDefault: true,
24898             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24899         });
24900     //this.addSeparator();
24901         
24902         
24903         var field = this.navgroup.addItem( {
24904             tagtype : 'span',
24905             cls : 'x-paging-position  btn-outline-secondary',
24906              disabled: true,
24907             html : this.beforePageText  +
24908                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24909                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24910          } ); //?? escaped?
24911         
24912         this.field = field.el.select('input', true).first();
24913         this.field.on("keydown", this.onPagingKeydown, this);
24914         this.field.on("focus", function(){this.dom.select();});
24915     
24916     
24917         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24918         //this.field.setHeight(18);
24919         //this.addSeparator();
24920         this.next = this.navgroup.addItem({
24921             tooltip: this.nextText,
24922             cls: "next btn-outline-secondary",
24923             html : ' <i class="fa fa-forward"></i>',
24924             disabled: true,
24925             preventDefault: true,
24926             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24927         });
24928         this.last = this.navgroup.addItem({
24929             tooltip: this.lastText,
24930             html : ' <i class="fa fa-step-forward"></i>',
24931             cls: "next btn-outline-secondary",
24932             disabled: true,
24933             preventDefault: true,
24934             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24935         });
24936     //this.addSeparator();
24937         this.loading = this.navgroup.addItem({
24938             tooltip: this.refreshText,
24939             cls: "btn-outline-secondary",
24940             html : ' <i class="fa fa-refresh"></i>',
24941             preventDefault: true,
24942             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24943         });
24944         
24945     },
24946
24947     // private
24948     updateInfo : function(){
24949         if(this.displayEl){
24950             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24951             var msg = count == 0 ?
24952                 this.emptyMsg :
24953                 String.format(
24954                     this.displayMsg,
24955                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24956                 );
24957             this.displayEl.update(msg);
24958         }
24959     },
24960
24961     // private
24962     onLoad : function(ds, r, o)
24963     {
24964         this.cursor = o.params.start ? o.params.start : 0;
24965         
24966         var d = this.getPageData(),
24967             ap = d.activePage,
24968             ps = d.pages;
24969         
24970         
24971         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24972         this.field.dom.value = ap;
24973         this.first.setDisabled(ap == 1);
24974         this.prev.setDisabled(ap == 1);
24975         this.next.setDisabled(ap == ps);
24976         this.last.setDisabled(ap == ps);
24977         this.loading.enable();
24978         this.updateInfo();
24979     },
24980
24981     // private
24982     getPageData : function(){
24983         var total = this.ds.getTotalCount();
24984         return {
24985             total : total,
24986             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24987             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24988         };
24989     },
24990
24991     // private
24992     onLoadError : function(){
24993         this.loading.enable();
24994     },
24995
24996     // private
24997     onPagingKeydown : function(e){
24998         var k = e.getKey();
24999         var d = this.getPageData();
25000         if(k == e.RETURN){
25001             var v = this.field.dom.value, pageNum;
25002             if(!v || isNaN(pageNum = parseInt(v, 10))){
25003                 this.field.dom.value = d.activePage;
25004                 return;
25005             }
25006             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25007             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25008             e.stopEvent();
25009         }
25010         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))
25011         {
25012           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25013           this.field.dom.value = pageNum;
25014           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25015           e.stopEvent();
25016         }
25017         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25018         {
25019           var v = this.field.dom.value, pageNum; 
25020           var increment = (e.shiftKey) ? 10 : 1;
25021           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25022                 increment *= -1;
25023           }
25024           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25025             this.field.dom.value = d.activePage;
25026             return;
25027           }
25028           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25029           {
25030             this.field.dom.value = parseInt(v, 10) + increment;
25031             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25032             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25033           }
25034           e.stopEvent();
25035         }
25036     },
25037
25038     // private
25039     beforeLoad : function(){
25040         if(this.loading){
25041             this.loading.disable();
25042         }
25043     },
25044
25045     // private
25046     onClick : function(which){
25047         
25048         var ds = this.ds;
25049         if (!ds) {
25050             return;
25051         }
25052         
25053         switch(which){
25054             case "first":
25055                 ds.load({params:{start: 0, limit: this.pageSize}});
25056             break;
25057             case "prev":
25058                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25059             break;
25060             case "next":
25061                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25062             break;
25063             case "last":
25064                 var total = ds.getTotalCount();
25065                 var extra = total % this.pageSize;
25066                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25067                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25068             break;
25069             case "refresh":
25070                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25071             break;
25072         }
25073     },
25074
25075     /**
25076      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25077      * @param {Roo.data.Store} store The data store to unbind
25078      */
25079     unbind : function(ds){
25080         ds.un("beforeload", this.beforeLoad, this);
25081         ds.un("load", this.onLoad, this);
25082         ds.un("loadexception", this.onLoadError, this);
25083         ds.un("remove", this.updateInfo, this);
25084         ds.un("add", this.updateInfo, this);
25085         this.ds = undefined;
25086     },
25087
25088     /**
25089      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25090      * @param {Roo.data.Store} store The data store to bind
25091      */
25092     bind : function(ds){
25093         ds.on("beforeload", this.beforeLoad, this);
25094         ds.on("load", this.onLoad, this);
25095         ds.on("loadexception", this.onLoadError, this);
25096         ds.on("remove", this.updateInfo, this);
25097         ds.on("add", this.updateInfo, this);
25098         this.ds = ds;
25099     }
25100 });/*
25101  * - LGPL
25102  *
25103  * element
25104  * 
25105  */
25106
25107 /**
25108  * @class Roo.bootstrap.MessageBar
25109  * @extends Roo.bootstrap.Component
25110  * Bootstrap MessageBar class
25111  * @cfg {String} html contents of the MessageBar
25112  * @cfg {String} weight (info | success | warning | danger) default info
25113  * @cfg {String} beforeClass insert the bar before the given class
25114  * @cfg {Boolean} closable (true | false) default false
25115  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25116  * 
25117  * @constructor
25118  * Create a new Element
25119  * @param {Object} config The config object
25120  */
25121
25122 Roo.bootstrap.MessageBar = function(config){
25123     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25124 };
25125
25126 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25127     
25128     html: '',
25129     weight: 'info',
25130     closable: false,
25131     fixed: false,
25132     beforeClass: 'bootstrap-sticky-wrap',
25133     
25134     getAutoCreate : function(){
25135         
25136         var cfg = {
25137             tag: 'div',
25138             cls: 'alert alert-dismissable alert-' + this.weight,
25139             cn: [
25140                 {
25141                     tag: 'span',
25142                     cls: 'message',
25143                     html: this.html || ''
25144                 }
25145             ]
25146         };
25147         
25148         if(this.fixed){
25149             cfg.cls += ' alert-messages-fixed';
25150         }
25151         
25152         if(this.closable){
25153             cfg.cn.push({
25154                 tag: 'button',
25155                 cls: 'close',
25156                 html: 'x'
25157             });
25158         }
25159         
25160         return cfg;
25161     },
25162     
25163     onRender : function(ct, position)
25164     {
25165         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25166         
25167         if(!this.el){
25168             var cfg = Roo.apply({},  this.getAutoCreate());
25169             cfg.id = Roo.id();
25170             
25171             if (this.cls) {
25172                 cfg.cls += ' ' + this.cls;
25173             }
25174             if (this.style) {
25175                 cfg.style = this.style;
25176             }
25177             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25178             
25179             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25180         }
25181         
25182         this.el.select('>button.close').on('click', this.hide, this);
25183         
25184     },
25185     
25186     show : function()
25187     {
25188         if (!this.rendered) {
25189             this.render();
25190         }
25191         
25192         this.el.show();
25193         
25194         this.fireEvent('show', this);
25195         
25196     },
25197     
25198     hide : function()
25199     {
25200         if (!this.rendered) {
25201             this.render();
25202         }
25203         
25204         this.el.hide();
25205         
25206         this.fireEvent('hide', this);
25207     },
25208     
25209     update : function()
25210     {
25211 //        var e = this.el.dom.firstChild;
25212 //        
25213 //        if(this.closable){
25214 //            e = e.nextSibling;
25215 //        }
25216 //        
25217 //        e.data = this.html || '';
25218
25219         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25220     }
25221    
25222 });
25223
25224  
25225
25226      /*
25227  * - LGPL
25228  *
25229  * Graph
25230  * 
25231  */
25232
25233
25234 /**
25235  * @class Roo.bootstrap.Graph
25236  * @extends Roo.bootstrap.Component
25237  * Bootstrap Graph class
25238 > Prameters
25239  -sm {number} sm 4
25240  -md {number} md 5
25241  @cfg {String} graphtype  bar | vbar | pie
25242  @cfg {number} g_x coodinator | centre x (pie)
25243  @cfg {number} g_y coodinator | centre y (pie)
25244  @cfg {number} g_r radius (pie)
25245  @cfg {number} g_height height of the chart (respected by all elements in the set)
25246  @cfg {number} g_width width of the chart (respected by all elements in the set)
25247  @cfg {Object} title The title of the chart
25248     
25249  -{Array}  values
25250  -opts (object) options for the chart 
25251      o {
25252      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25253      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25254      o vgutter (number)
25255      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.
25256      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25257      o to
25258      o stretch (boolean)
25259      o }
25260  -opts (object) options for the pie
25261      o{
25262      o cut
25263      o startAngle (number)
25264      o endAngle (number)
25265      } 
25266  *
25267  * @constructor
25268  * Create a new Input
25269  * @param {Object} config The config object
25270  */
25271
25272 Roo.bootstrap.Graph = function(config){
25273     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25274     
25275     this.addEvents({
25276         // img events
25277         /**
25278          * @event click
25279          * The img click event for the img.
25280          * @param {Roo.EventObject} e
25281          */
25282         "click" : true
25283     });
25284 };
25285
25286 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25287     
25288     sm: 4,
25289     md: 5,
25290     graphtype: 'bar',
25291     g_height: 250,
25292     g_width: 400,
25293     g_x: 50,
25294     g_y: 50,
25295     g_r: 30,
25296     opts:{
25297         //g_colors: this.colors,
25298         g_type: 'soft',
25299         g_gutter: '20%'
25300
25301     },
25302     title : false,
25303
25304     getAutoCreate : function(){
25305         
25306         var cfg = {
25307             tag: 'div',
25308             html : null
25309         };
25310         
25311         
25312         return  cfg;
25313     },
25314
25315     onRender : function(ct,position){
25316         
25317         
25318         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25319         
25320         if (typeof(Raphael) == 'undefined') {
25321             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25322             return;
25323         }
25324         
25325         this.raphael = Raphael(this.el.dom);
25326         
25327                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25328                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25329                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25330                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25331                 /*
25332                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25333                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25334                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25335                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25336                 
25337                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25338                 r.barchart(330, 10, 300, 220, data1);
25339                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25340                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25341                 */
25342                 
25343                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25344                 // r.barchart(30, 30, 560, 250,  xdata, {
25345                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25346                 //     axis : "0 0 1 1",
25347                 //     axisxlabels :  xdata
25348                 //     //yvalues : cols,
25349                    
25350                 // });
25351 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25352 //        
25353 //        this.load(null,xdata,{
25354 //                axis : "0 0 1 1",
25355 //                axisxlabels :  xdata
25356 //                });
25357
25358     },
25359
25360     load : function(graphtype,xdata,opts)
25361     {
25362         this.raphael.clear();
25363         if(!graphtype) {
25364             graphtype = this.graphtype;
25365         }
25366         if(!opts){
25367             opts = this.opts;
25368         }
25369         var r = this.raphael,
25370             fin = function () {
25371                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25372             },
25373             fout = function () {
25374                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25375             },
25376             pfin = function() {
25377                 this.sector.stop();
25378                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25379
25380                 if (this.label) {
25381                     this.label[0].stop();
25382                     this.label[0].attr({ r: 7.5 });
25383                     this.label[1].attr({ "font-weight": 800 });
25384                 }
25385             },
25386             pfout = function() {
25387                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25388
25389                 if (this.label) {
25390                     this.label[0].animate({ r: 5 }, 500, "bounce");
25391                     this.label[1].attr({ "font-weight": 400 });
25392                 }
25393             };
25394
25395         switch(graphtype){
25396             case 'bar':
25397                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25398                 break;
25399             case 'hbar':
25400                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25401                 break;
25402             case 'pie':
25403 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25404 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25405 //            
25406                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25407                 
25408                 break;
25409
25410         }
25411         
25412         if(this.title){
25413             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25414         }
25415         
25416     },
25417     
25418     setTitle: function(o)
25419     {
25420         this.title = o;
25421     },
25422     
25423     initEvents: function() {
25424         
25425         if(!this.href){
25426             this.el.on('click', this.onClick, this);
25427         }
25428     },
25429     
25430     onClick : function(e)
25431     {
25432         Roo.log('img onclick');
25433         this.fireEvent('click', this, e);
25434     }
25435    
25436 });
25437
25438  
25439 /*
25440  * - LGPL
25441  *
25442  * numberBox
25443  * 
25444  */
25445 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25446
25447 /**
25448  * @class Roo.bootstrap.dash.NumberBox
25449  * @extends Roo.bootstrap.Component
25450  * Bootstrap NumberBox class
25451  * @cfg {String} headline Box headline
25452  * @cfg {String} content Box content
25453  * @cfg {String} icon Box icon
25454  * @cfg {String} footer Footer text
25455  * @cfg {String} fhref Footer href
25456  * 
25457  * @constructor
25458  * Create a new NumberBox
25459  * @param {Object} config The config object
25460  */
25461
25462
25463 Roo.bootstrap.dash.NumberBox = function(config){
25464     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25465     
25466 };
25467
25468 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25469     
25470     headline : '',
25471     content : '',
25472     icon : '',
25473     footer : '',
25474     fhref : '',
25475     ficon : '',
25476     
25477     getAutoCreate : function(){
25478         
25479         var cfg = {
25480             tag : 'div',
25481             cls : 'small-box ',
25482             cn : [
25483                 {
25484                     tag : 'div',
25485                     cls : 'inner',
25486                     cn :[
25487                         {
25488                             tag : 'h3',
25489                             cls : 'roo-headline',
25490                             html : this.headline
25491                         },
25492                         {
25493                             tag : 'p',
25494                             cls : 'roo-content',
25495                             html : this.content
25496                         }
25497                     ]
25498                 }
25499             ]
25500         };
25501         
25502         if(this.icon){
25503             cfg.cn.push({
25504                 tag : 'div',
25505                 cls : 'icon',
25506                 cn :[
25507                     {
25508                         tag : 'i',
25509                         cls : 'ion ' + this.icon
25510                     }
25511                 ]
25512             });
25513         }
25514         
25515         if(this.footer){
25516             var footer = {
25517                 tag : 'a',
25518                 cls : 'small-box-footer',
25519                 href : this.fhref || '#',
25520                 html : this.footer
25521             };
25522             
25523             cfg.cn.push(footer);
25524             
25525         }
25526         
25527         return  cfg;
25528     },
25529
25530     onRender : function(ct,position){
25531         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25532
25533
25534        
25535                 
25536     },
25537
25538     setHeadline: function (value)
25539     {
25540         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25541     },
25542     
25543     setFooter: function (value, href)
25544     {
25545         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25546         
25547         if(href){
25548             this.el.select('a.small-box-footer',true).first().attr('href', href);
25549         }
25550         
25551     },
25552
25553     setContent: function (value)
25554     {
25555         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25556     },
25557
25558     initEvents: function() 
25559     {   
25560         
25561     }
25562     
25563 });
25564
25565  
25566 /*
25567  * - LGPL
25568  *
25569  * TabBox
25570  * 
25571  */
25572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25573
25574 /**
25575  * @class Roo.bootstrap.dash.TabBox
25576  * @extends Roo.bootstrap.Component
25577  * Bootstrap TabBox class
25578  * @cfg {String} title Title of the TabBox
25579  * @cfg {String} icon Icon of the TabBox
25580  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25581  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25582  * 
25583  * @constructor
25584  * Create a new TabBox
25585  * @param {Object} config The config object
25586  */
25587
25588
25589 Roo.bootstrap.dash.TabBox = function(config){
25590     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25591     this.addEvents({
25592         // raw events
25593         /**
25594          * @event addpane
25595          * When a pane is added
25596          * @param {Roo.bootstrap.dash.TabPane} pane
25597          */
25598         "addpane" : true,
25599         /**
25600          * @event activatepane
25601          * When a pane is activated
25602          * @param {Roo.bootstrap.dash.TabPane} pane
25603          */
25604         "activatepane" : true
25605         
25606          
25607     });
25608     
25609     this.panes = [];
25610 };
25611
25612 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25613
25614     title : '',
25615     icon : false,
25616     showtabs : true,
25617     tabScrollable : false,
25618     
25619     getChildContainer : function()
25620     {
25621         return this.el.select('.tab-content', true).first();
25622     },
25623     
25624     getAutoCreate : function(){
25625         
25626         var header = {
25627             tag: 'li',
25628             cls: 'pull-left header',
25629             html: this.title,
25630             cn : []
25631         };
25632         
25633         if(this.icon){
25634             header.cn.push({
25635                 tag: 'i',
25636                 cls: 'fa ' + this.icon
25637             });
25638         }
25639         
25640         var h = {
25641             tag: 'ul',
25642             cls: 'nav nav-tabs pull-right',
25643             cn: [
25644                 header
25645             ]
25646         };
25647         
25648         if(this.tabScrollable){
25649             h = {
25650                 tag: 'div',
25651                 cls: 'tab-header',
25652                 cn: [
25653                     {
25654                         tag: 'ul',
25655                         cls: 'nav nav-tabs pull-right',
25656                         cn: [
25657                             header
25658                         ]
25659                     }
25660                 ]
25661             };
25662         }
25663         
25664         var cfg = {
25665             tag: 'div',
25666             cls: 'nav-tabs-custom',
25667             cn: [
25668                 h,
25669                 {
25670                     tag: 'div',
25671                     cls: 'tab-content no-padding',
25672                     cn: []
25673                 }
25674             ]
25675         };
25676
25677         return  cfg;
25678     },
25679     initEvents : function()
25680     {
25681         //Roo.log('add add pane handler');
25682         this.on('addpane', this.onAddPane, this);
25683     },
25684      /**
25685      * Updates the box title
25686      * @param {String} html to set the title to.
25687      */
25688     setTitle : function(value)
25689     {
25690         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25691     },
25692     onAddPane : function(pane)
25693     {
25694         this.panes.push(pane);
25695         //Roo.log('addpane');
25696         //Roo.log(pane);
25697         // tabs are rendere left to right..
25698         if(!this.showtabs){
25699             return;
25700         }
25701         
25702         var ctr = this.el.select('.nav-tabs', true).first();
25703          
25704          
25705         var existing = ctr.select('.nav-tab',true);
25706         var qty = existing.getCount();;
25707         
25708         
25709         var tab = ctr.createChild({
25710             tag : 'li',
25711             cls : 'nav-tab' + (qty ? '' : ' active'),
25712             cn : [
25713                 {
25714                     tag : 'a',
25715                     href:'#',
25716                     html : pane.title
25717                 }
25718             ]
25719         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25720         pane.tab = tab;
25721         
25722         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25723         if (!qty) {
25724             pane.el.addClass('active');
25725         }
25726         
25727                 
25728     },
25729     onTabClick : function(ev,un,ob,pane)
25730     {
25731         //Roo.log('tab - prev default');
25732         ev.preventDefault();
25733         
25734         
25735         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25736         pane.tab.addClass('active');
25737         //Roo.log(pane.title);
25738         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25739         // technically we should have a deactivate event.. but maybe add later.
25740         // and it should not de-activate the selected tab...
25741         this.fireEvent('activatepane', pane);
25742         pane.el.addClass('active');
25743         pane.fireEvent('activate');
25744         
25745         
25746     },
25747     
25748     getActivePane : function()
25749     {
25750         var r = false;
25751         Roo.each(this.panes, function(p) {
25752             if(p.el.hasClass('active')){
25753                 r = p;
25754                 return false;
25755             }
25756             
25757             return;
25758         });
25759         
25760         return r;
25761     }
25762     
25763     
25764 });
25765
25766  
25767 /*
25768  * - LGPL
25769  *
25770  * Tab pane
25771  * 
25772  */
25773 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25774 /**
25775  * @class Roo.bootstrap.TabPane
25776  * @extends Roo.bootstrap.Component
25777  * Bootstrap TabPane class
25778  * @cfg {Boolean} active (false | true) Default false
25779  * @cfg {String} title title of panel
25780
25781  * 
25782  * @constructor
25783  * Create a new TabPane
25784  * @param {Object} config The config object
25785  */
25786
25787 Roo.bootstrap.dash.TabPane = function(config){
25788     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25789     
25790     this.addEvents({
25791         // raw events
25792         /**
25793          * @event activate
25794          * When a pane is activated
25795          * @param {Roo.bootstrap.dash.TabPane} pane
25796          */
25797         "activate" : true
25798          
25799     });
25800 };
25801
25802 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25803     
25804     active : false,
25805     title : '',
25806     
25807     // the tabBox that this is attached to.
25808     tab : false,
25809      
25810     getAutoCreate : function() 
25811     {
25812         var cfg = {
25813             tag: 'div',
25814             cls: 'tab-pane'
25815         };
25816         
25817         if(this.active){
25818             cfg.cls += ' active';
25819         }
25820         
25821         return cfg;
25822     },
25823     initEvents  : function()
25824     {
25825         //Roo.log('trigger add pane handler');
25826         this.parent().fireEvent('addpane', this)
25827     },
25828     
25829      /**
25830      * Updates the tab title 
25831      * @param {String} html to set the title to.
25832      */
25833     setTitle: function(str)
25834     {
25835         if (!this.tab) {
25836             return;
25837         }
25838         this.title = str;
25839         this.tab.select('a', true).first().dom.innerHTML = str;
25840         
25841     }
25842     
25843     
25844     
25845 });
25846
25847  
25848
25849
25850  /*
25851  * - LGPL
25852  *
25853  * menu
25854  * 
25855  */
25856 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25857
25858 /**
25859  * @class Roo.bootstrap.menu.Menu
25860  * @extends Roo.bootstrap.Component
25861  * Bootstrap Menu class - container for Menu
25862  * @cfg {String} html Text of the menu
25863  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25864  * @cfg {String} icon Font awesome icon
25865  * @cfg {String} pos Menu align to (top | bottom) default bottom
25866  * 
25867  * 
25868  * @constructor
25869  * Create a new Menu
25870  * @param {Object} config The config object
25871  */
25872
25873
25874 Roo.bootstrap.menu.Menu = function(config){
25875     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25876     
25877     this.addEvents({
25878         /**
25879          * @event beforeshow
25880          * Fires before this menu is displayed
25881          * @param {Roo.bootstrap.menu.Menu} this
25882          */
25883         beforeshow : true,
25884         /**
25885          * @event beforehide
25886          * Fires before this menu is hidden
25887          * @param {Roo.bootstrap.menu.Menu} this
25888          */
25889         beforehide : true,
25890         /**
25891          * @event show
25892          * Fires after this menu is displayed
25893          * @param {Roo.bootstrap.menu.Menu} this
25894          */
25895         show : true,
25896         /**
25897          * @event hide
25898          * Fires after this menu is hidden
25899          * @param {Roo.bootstrap.menu.Menu} this
25900          */
25901         hide : true,
25902         /**
25903          * @event click
25904          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25905          * @param {Roo.bootstrap.menu.Menu} this
25906          * @param {Roo.EventObject} e
25907          */
25908         click : true
25909     });
25910     
25911 };
25912
25913 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25914     
25915     submenu : false,
25916     html : '',
25917     weight : 'default',
25918     icon : false,
25919     pos : 'bottom',
25920     
25921     
25922     getChildContainer : function() {
25923         if(this.isSubMenu){
25924             return this.el;
25925         }
25926         
25927         return this.el.select('ul.dropdown-menu', true).first();  
25928     },
25929     
25930     getAutoCreate : function()
25931     {
25932         var text = [
25933             {
25934                 tag : 'span',
25935                 cls : 'roo-menu-text',
25936                 html : this.html
25937             }
25938         ];
25939         
25940         if(this.icon){
25941             text.unshift({
25942                 tag : 'i',
25943                 cls : 'fa ' + this.icon
25944             })
25945         }
25946         
25947         
25948         var cfg = {
25949             tag : 'div',
25950             cls : 'btn-group',
25951             cn : [
25952                 {
25953                     tag : 'button',
25954                     cls : 'dropdown-button btn btn-' + this.weight,
25955                     cn : text
25956                 },
25957                 {
25958                     tag : 'button',
25959                     cls : 'dropdown-toggle btn btn-' + this.weight,
25960                     cn : [
25961                         {
25962                             tag : 'span',
25963                             cls : 'caret'
25964                         }
25965                     ]
25966                 },
25967                 {
25968                     tag : 'ul',
25969                     cls : 'dropdown-menu'
25970                 }
25971             ]
25972             
25973         };
25974         
25975         if(this.pos == 'top'){
25976             cfg.cls += ' dropup';
25977         }
25978         
25979         if(this.isSubMenu){
25980             cfg = {
25981                 tag : 'ul',
25982                 cls : 'dropdown-menu'
25983             }
25984         }
25985         
25986         return cfg;
25987     },
25988     
25989     onRender : function(ct, position)
25990     {
25991         this.isSubMenu = ct.hasClass('dropdown-submenu');
25992         
25993         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25994     },
25995     
25996     initEvents : function() 
25997     {
25998         if(this.isSubMenu){
25999             return;
26000         }
26001         
26002         this.hidden = true;
26003         
26004         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26005         this.triggerEl.on('click', this.onTriggerPress, this);
26006         
26007         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26008         this.buttonEl.on('click', this.onClick, this);
26009         
26010     },
26011     
26012     list : function()
26013     {
26014         if(this.isSubMenu){
26015             return this.el;
26016         }
26017         
26018         return this.el.select('ul.dropdown-menu', true).first();
26019     },
26020     
26021     onClick : function(e)
26022     {
26023         this.fireEvent("click", this, e);
26024     },
26025     
26026     onTriggerPress  : function(e)
26027     {   
26028         if (this.isVisible()) {
26029             this.hide();
26030         } else {
26031             this.show();
26032         }
26033     },
26034     
26035     isVisible : function(){
26036         return !this.hidden;
26037     },
26038     
26039     show : function()
26040     {
26041         this.fireEvent("beforeshow", this);
26042         
26043         this.hidden = false;
26044         this.el.addClass('open');
26045         
26046         Roo.get(document).on("mouseup", this.onMouseUp, this);
26047         
26048         this.fireEvent("show", this);
26049         
26050         
26051     },
26052     
26053     hide : function()
26054     {
26055         this.fireEvent("beforehide", this);
26056         
26057         this.hidden = true;
26058         this.el.removeClass('open');
26059         
26060         Roo.get(document).un("mouseup", this.onMouseUp);
26061         
26062         this.fireEvent("hide", this);
26063     },
26064     
26065     onMouseUp : function()
26066     {
26067         this.hide();
26068     }
26069     
26070 });
26071
26072  
26073  /*
26074  * - LGPL
26075  *
26076  * menu item
26077  * 
26078  */
26079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26080
26081 /**
26082  * @class Roo.bootstrap.menu.Item
26083  * @extends Roo.bootstrap.Component
26084  * Bootstrap MenuItem class
26085  * @cfg {Boolean} submenu (true | false) default false
26086  * @cfg {String} html text of the item
26087  * @cfg {String} href the link
26088  * @cfg {Boolean} disable (true | false) default false
26089  * @cfg {Boolean} preventDefault (true | false) default true
26090  * @cfg {String} icon Font awesome icon
26091  * @cfg {String} pos Submenu align to (left | right) default right 
26092  * 
26093  * 
26094  * @constructor
26095  * Create a new Item
26096  * @param {Object} config The config object
26097  */
26098
26099
26100 Roo.bootstrap.menu.Item = function(config){
26101     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26102     this.addEvents({
26103         /**
26104          * @event mouseover
26105          * Fires when the mouse is hovering over this menu
26106          * @param {Roo.bootstrap.menu.Item} this
26107          * @param {Roo.EventObject} e
26108          */
26109         mouseover : true,
26110         /**
26111          * @event mouseout
26112          * Fires when the mouse exits this menu
26113          * @param {Roo.bootstrap.menu.Item} this
26114          * @param {Roo.EventObject} e
26115          */
26116         mouseout : true,
26117         // raw events
26118         /**
26119          * @event click
26120          * The raw click event for the entire grid.
26121          * @param {Roo.EventObject} e
26122          */
26123         click : true
26124     });
26125 };
26126
26127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26128     
26129     submenu : false,
26130     href : '',
26131     html : '',
26132     preventDefault: true,
26133     disable : false,
26134     icon : false,
26135     pos : 'right',
26136     
26137     getAutoCreate : function()
26138     {
26139         var text = [
26140             {
26141                 tag : 'span',
26142                 cls : 'roo-menu-item-text',
26143                 html : this.html
26144             }
26145         ];
26146         
26147         if(this.icon){
26148             text.unshift({
26149                 tag : 'i',
26150                 cls : 'fa ' + this.icon
26151             })
26152         }
26153         
26154         var cfg = {
26155             tag : 'li',
26156             cn : [
26157                 {
26158                     tag : 'a',
26159                     href : this.href || '#',
26160                     cn : text
26161                 }
26162             ]
26163         };
26164         
26165         if(this.disable){
26166             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26167         }
26168         
26169         if(this.submenu){
26170             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26171             
26172             if(this.pos == 'left'){
26173                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26174             }
26175         }
26176         
26177         return cfg;
26178     },
26179     
26180     initEvents : function() 
26181     {
26182         this.el.on('mouseover', this.onMouseOver, this);
26183         this.el.on('mouseout', this.onMouseOut, this);
26184         
26185         this.el.select('a', true).first().on('click', this.onClick, this);
26186         
26187     },
26188     
26189     onClick : function(e)
26190     {
26191         if(this.preventDefault){
26192             e.preventDefault();
26193         }
26194         
26195         this.fireEvent("click", this, e);
26196     },
26197     
26198     onMouseOver : function(e)
26199     {
26200         if(this.submenu && this.pos == 'left'){
26201             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26202         }
26203         
26204         this.fireEvent("mouseover", this, e);
26205     },
26206     
26207     onMouseOut : function(e)
26208     {
26209         this.fireEvent("mouseout", this, e);
26210     }
26211 });
26212
26213  
26214
26215  /*
26216  * - LGPL
26217  *
26218  * menu separator
26219  * 
26220  */
26221 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26222
26223 /**
26224  * @class Roo.bootstrap.menu.Separator
26225  * @extends Roo.bootstrap.Component
26226  * Bootstrap Separator class
26227  * 
26228  * @constructor
26229  * Create a new Separator
26230  * @param {Object} config The config object
26231  */
26232
26233
26234 Roo.bootstrap.menu.Separator = function(config){
26235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26236 };
26237
26238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26239     
26240     getAutoCreate : function(){
26241         var cfg = {
26242             tag : 'li',
26243             cls: 'divider'
26244         };
26245         
26246         return cfg;
26247     }
26248    
26249 });
26250
26251  
26252
26253  /*
26254  * - LGPL
26255  *
26256  * Tooltip
26257  * 
26258  */
26259
26260 /**
26261  * @class Roo.bootstrap.Tooltip
26262  * Bootstrap Tooltip class
26263  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26264  * to determine which dom element triggers the tooltip.
26265  * 
26266  * It needs to add support for additional attributes like tooltip-position
26267  * 
26268  * @constructor
26269  * Create a new Toolti
26270  * @param {Object} config The config object
26271  */
26272
26273 Roo.bootstrap.Tooltip = function(config){
26274     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26275     
26276     this.alignment = Roo.bootstrap.Tooltip.alignment;
26277     
26278     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26279         this.alignment = config.alignment;
26280     }
26281     
26282 };
26283
26284 Roo.apply(Roo.bootstrap.Tooltip, {
26285     /**
26286      * @function init initialize tooltip monitoring.
26287      * @static
26288      */
26289     currentEl : false,
26290     currentTip : false,
26291     currentRegion : false,
26292     
26293     //  init : delay?
26294     
26295     init : function()
26296     {
26297         Roo.get(document).on('mouseover', this.enter ,this);
26298         Roo.get(document).on('mouseout', this.leave, this);
26299          
26300         
26301         this.currentTip = new Roo.bootstrap.Tooltip();
26302     },
26303     
26304     enter : function(ev)
26305     {
26306         var dom = ev.getTarget();
26307         
26308         //Roo.log(['enter',dom]);
26309         var el = Roo.fly(dom);
26310         if (this.currentEl) {
26311             //Roo.log(dom);
26312             //Roo.log(this.currentEl);
26313             //Roo.log(this.currentEl.contains(dom));
26314             if (this.currentEl == el) {
26315                 return;
26316             }
26317             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26318                 return;
26319             }
26320
26321         }
26322         
26323         if (this.currentTip.el) {
26324             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26325         }    
26326         //Roo.log(ev);
26327         
26328         if(!el || el.dom == document){
26329             return;
26330         }
26331         
26332         var bindEl = el;
26333         
26334         // you can not look for children, as if el is the body.. then everythign is the child..
26335         if (!el.attr('tooltip')) { //
26336             if (!el.select("[tooltip]").elements.length) {
26337                 return;
26338             }
26339             // is the mouse over this child...?
26340             bindEl = el.select("[tooltip]").first();
26341             var xy = ev.getXY();
26342             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26343                 //Roo.log("not in region.");
26344                 return;
26345             }
26346             //Roo.log("child element over..");
26347             
26348         }
26349         this.currentEl = bindEl;
26350         this.currentTip.bind(bindEl);
26351         this.currentRegion = Roo.lib.Region.getRegion(dom);
26352         this.currentTip.enter();
26353         
26354     },
26355     leave : function(ev)
26356     {
26357         var dom = ev.getTarget();
26358         //Roo.log(['leave',dom]);
26359         if (!this.currentEl) {
26360             return;
26361         }
26362         
26363         
26364         if (dom != this.currentEl.dom) {
26365             return;
26366         }
26367         var xy = ev.getXY();
26368         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26369             return;
26370         }
26371         // only activate leave if mouse cursor is outside... bounding box..
26372         
26373         
26374         
26375         
26376         if (this.currentTip) {
26377             this.currentTip.leave();
26378         }
26379         //Roo.log('clear currentEl');
26380         this.currentEl = false;
26381         
26382         
26383     },
26384     alignment : {
26385         'left' : ['r-l', [-2,0], 'right'],
26386         'right' : ['l-r', [2,0], 'left'],
26387         'bottom' : ['t-b', [0,2], 'top'],
26388         'top' : [ 'b-t', [0,-2], 'bottom']
26389     }
26390     
26391 });
26392
26393
26394 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26395     
26396     
26397     bindEl : false,
26398     
26399     delay : null, // can be { show : 300 , hide: 500}
26400     
26401     timeout : null,
26402     
26403     hoverState : null, //???
26404     
26405     placement : 'bottom', 
26406     
26407     alignment : false,
26408     
26409     getAutoCreate : function(){
26410     
26411         var cfg = {
26412            cls : 'tooltip',
26413            role : 'tooltip',
26414            cn : [
26415                 {
26416                     cls : 'tooltip-arrow'
26417                 },
26418                 {
26419                     cls : 'tooltip-inner'
26420                 }
26421            ]
26422         };
26423         
26424         return cfg;
26425     },
26426     bind : function(el)
26427     {
26428         this.bindEl = el;
26429     },
26430       
26431     
26432     enter : function () {
26433        
26434         if (this.timeout != null) {
26435             clearTimeout(this.timeout);
26436         }
26437         
26438         this.hoverState = 'in';
26439          //Roo.log("enter - show");
26440         if (!this.delay || !this.delay.show) {
26441             this.show();
26442             return;
26443         }
26444         var _t = this;
26445         this.timeout = setTimeout(function () {
26446             if (_t.hoverState == 'in') {
26447                 _t.show();
26448             }
26449         }, this.delay.show);
26450     },
26451     leave : function()
26452     {
26453         clearTimeout(this.timeout);
26454     
26455         this.hoverState = 'out';
26456          if (!this.delay || !this.delay.hide) {
26457             this.hide();
26458             return;
26459         }
26460        
26461         var _t = this;
26462         this.timeout = setTimeout(function () {
26463             //Roo.log("leave - timeout");
26464             
26465             if (_t.hoverState == 'out') {
26466                 _t.hide();
26467                 Roo.bootstrap.Tooltip.currentEl = false;
26468             }
26469         }, delay);
26470     },
26471     
26472     show : function (msg)
26473     {
26474         if (!this.el) {
26475             this.render(document.body);
26476         }
26477         // set content.
26478         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26479         
26480         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26481         
26482         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26483         
26484         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26485         
26486         var placement = typeof this.placement == 'function' ?
26487             this.placement.call(this, this.el, on_el) :
26488             this.placement;
26489             
26490         var autoToken = /\s?auto?\s?/i;
26491         var autoPlace = autoToken.test(placement);
26492         if (autoPlace) {
26493             placement = placement.replace(autoToken, '') || 'top';
26494         }
26495         
26496         //this.el.detach()
26497         //this.el.setXY([0,0]);
26498         this.el.show();
26499         //this.el.dom.style.display='block';
26500         
26501         //this.el.appendTo(on_el);
26502         
26503         var p = this.getPosition();
26504         var box = this.el.getBox();
26505         
26506         if (autoPlace) {
26507             // fixme..
26508         }
26509         
26510         var align = this.alignment[placement];
26511         
26512         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26513         
26514         if(placement == 'top' || placement == 'bottom'){
26515             if(xy[0] < 0){
26516                 placement = 'right';
26517             }
26518             
26519             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26520                 placement = 'left';
26521             }
26522             
26523             var scroll = Roo.select('body', true).first().getScroll();
26524             
26525             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26526                 placement = 'top';
26527             }
26528             
26529             align = this.alignment[placement];
26530         }
26531         
26532         this.el.alignTo(this.bindEl, align[0],align[1]);
26533         //var arrow = this.el.select('.arrow',true).first();
26534         //arrow.set(align[2], 
26535         
26536         this.el.addClass(placement);
26537         
26538         this.el.addClass('in fade');
26539         
26540         this.hoverState = null;
26541         
26542         if (this.el.hasClass('fade')) {
26543             // fade it?
26544         }
26545         
26546     },
26547     hide : function()
26548     {
26549          
26550         if (!this.el) {
26551             return;
26552         }
26553         //this.el.setXY([0,0]);
26554         this.el.removeClass('in');
26555         //this.el.hide();
26556         
26557     }
26558     
26559 });
26560  
26561
26562  /*
26563  * - LGPL
26564  *
26565  * Location Picker
26566  * 
26567  */
26568
26569 /**
26570  * @class Roo.bootstrap.LocationPicker
26571  * @extends Roo.bootstrap.Component
26572  * Bootstrap LocationPicker class
26573  * @cfg {Number} latitude Position when init default 0
26574  * @cfg {Number} longitude Position when init default 0
26575  * @cfg {Number} zoom default 15
26576  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26577  * @cfg {Boolean} mapTypeControl default false
26578  * @cfg {Boolean} disableDoubleClickZoom default false
26579  * @cfg {Boolean} scrollwheel default true
26580  * @cfg {Boolean} streetViewControl default false
26581  * @cfg {Number} radius default 0
26582  * @cfg {String} locationName
26583  * @cfg {Boolean} draggable default true
26584  * @cfg {Boolean} enableAutocomplete default false
26585  * @cfg {Boolean} enableReverseGeocode default true
26586  * @cfg {String} markerTitle
26587  * 
26588  * @constructor
26589  * Create a new LocationPicker
26590  * @param {Object} config The config object
26591  */
26592
26593
26594 Roo.bootstrap.LocationPicker = function(config){
26595     
26596     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26597     
26598     this.addEvents({
26599         /**
26600          * @event initial
26601          * Fires when the picker initialized.
26602          * @param {Roo.bootstrap.LocationPicker} this
26603          * @param {Google Location} location
26604          */
26605         initial : true,
26606         /**
26607          * @event positionchanged
26608          * Fires when the picker position changed.
26609          * @param {Roo.bootstrap.LocationPicker} this
26610          * @param {Google Location} location
26611          */
26612         positionchanged : true,
26613         /**
26614          * @event resize
26615          * Fires when the map resize.
26616          * @param {Roo.bootstrap.LocationPicker} this
26617          */
26618         resize : true,
26619         /**
26620          * @event show
26621          * Fires when the map show.
26622          * @param {Roo.bootstrap.LocationPicker} this
26623          */
26624         show : true,
26625         /**
26626          * @event hide
26627          * Fires when the map hide.
26628          * @param {Roo.bootstrap.LocationPicker} this
26629          */
26630         hide : true,
26631         /**
26632          * @event mapClick
26633          * Fires when click the map.
26634          * @param {Roo.bootstrap.LocationPicker} this
26635          * @param {Map event} e
26636          */
26637         mapClick : true,
26638         /**
26639          * @event mapRightClick
26640          * Fires when right click the map.
26641          * @param {Roo.bootstrap.LocationPicker} this
26642          * @param {Map event} e
26643          */
26644         mapRightClick : true,
26645         /**
26646          * @event markerClick
26647          * Fires when click the marker.
26648          * @param {Roo.bootstrap.LocationPicker} this
26649          * @param {Map event} e
26650          */
26651         markerClick : true,
26652         /**
26653          * @event markerRightClick
26654          * Fires when right click the marker.
26655          * @param {Roo.bootstrap.LocationPicker} this
26656          * @param {Map event} e
26657          */
26658         markerRightClick : true,
26659         /**
26660          * @event OverlayViewDraw
26661          * Fires when OverlayView Draw
26662          * @param {Roo.bootstrap.LocationPicker} this
26663          */
26664         OverlayViewDraw : true,
26665         /**
26666          * @event OverlayViewOnAdd
26667          * Fires when OverlayView Draw
26668          * @param {Roo.bootstrap.LocationPicker} this
26669          */
26670         OverlayViewOnAdd : true,
26671         /**
26672          * @event OverlayViewOnRemove
26673          * Fires when OverlayView Draw
26674          * @param {Roo.bootstrap.LocationPicker} this
26675          */
26676         OverlayViewOnRemove : true,
26677         /**
26678          * @event OverlayViewShow
26679          * Fires when OverlayView Draw
26680          * @param {Roo.bootstrap.LocationPicker} this
26681          * @param {Pixel} cpx
26682          */
26683         OverlayViewShow : true,
26684         /**
26685          * @event OverlayViewHide
26686          * Fires when OverlayView Draw
26687          * @param {Roo.bootstrap.LocationPicker} this
26688          */
26689         OverlayViewHide : true,
26690         /**
26691          * @event loadexception
26692          * Fires when load google lib failed.
26693          * @param {Roo.bootstrap.LocationPicker} this
26694          */
26695         loadexception : true
26696     });
26697         
26698 };
26699
26700 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26701     
26702     gMapContext: false,
26703     
26704     latitude: 0,
26705     longitude: 0,
26706     zoom: 15,
26707     mapTypeId: false,
26708     mapTypeControl: false,
26709     disableDoubleClickZoom: false,
26710     scrollwheel: true,
26711     streetViewControl: false,
26712     radius: 0,
26713     locationName: '',
26714     draggable: true,
26715     enableAutocomplete: false,
26716     enableReverseGeocode: true,
26717     markerTitle: '',
26718     
26719     getAutoCreate: function()
26720     {
26721
26722         var cfg = {
26723             tag: 'div',
26724             cls: 'roo-location-picker'
26725         };
26726         
26727         return cfg
26728     },
26729     
26730     initEvents: function(ct, position)
26731     {       
26732         if(!this.el.getWidth() || this.isApplied()){
26733             return;
26734         }
26735         
26736         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26737         
26738         this.initial();
26739     },
26740     
26741     initial: function()
26742     {
26743         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26744             this.fireEvent('loadexception', this);
26745             return;
26746         }
26747         
26748         if(!this.mapTypeId){
26749             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26750         }
26751         
26752         this.gMapContext = this.GMapContext();
26753         
26754         this.initOverlayView();
26755         
26756         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26757         
26758         var _this = this;
26759                 
26760         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26761             _this.setPosition(_this.gMapContext.marker.position);
26762         });
26763         
26764         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26765             _this.fireEvent('mapClick', this, event);
26766             
26767         });
26768
26769         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26770             _this.fireEvent('mapRightClick', this, event);
26771             
26772         });
26773         
26774         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26775             _this.fireEvent('markerClick', this, event);
26776             
26777         });
26778
26779         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26780             _this.fireEvent('markerRightClick', this, event);
26781             
26782         });
26783         
26784         this.setPosition(this.gMapContext.location);
26785         
26786         this.fireEvent('initial', this, this.gMapContext.location);
26787     },
26788     
26789     initOverlayView: function()
26790     {
26791         var _this = this;
26792         
26793         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26794             
26795             draw: function()
26796             {
26797                 _this.fireEvent('OverlayViewDraw', _this);
26798             },
26799             
26800             onAdd: function()
26801             {
26802                 _this.fireEvent('OverlayViewOnAdd', _this);
26803             },
26804             
26805             onRemove: function()
26806             {
26807                 _this.fireEvent('OverlayViewOnRemove', _this);
26808             },
26809             
26810             show: function(cpx)
26811             {
26812                 _this.fireEvent('OverlayViewShow', _this, cpx);
26813             },
26814             
26815             hide: function()
26816             {
26817                 _this.fireEvent('OverlayViewHide', _this);
26818             }
26819             
26820         });
26821     },
26822     
26823     fromLatLngToContainerPixel: function(event)
26824     {
26825         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26826     },
26827     
26828     isApplied: function() 
26829     {
26830         return this.getGmapContext() == false ? false : true;
26831     },
26832     
26833     getGmapContext: function() 
26834     {
26835         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26836     },
26837     
26838     GMapContext: function() 
26839     {
26840         var position = new google.maps.LatLng(this.latitude, this.longitude);
26841         
26842         var _map = new google.maps.Map(this.el.dom, {
26843             center: position,
26844             zoom: this.zoom,
26845             mapTypeId: this.mapTypeId,
26846             mapTypeControl: this.mapTypeControl,
26847             disableDoubleClickZoom: this.disableDoubleClickZoom,
26848             scrollwheel: this.scrollwheel,
26849             streetViewControl: this.streetViewControl,
26850             locationName: this.locationName,
26851             draggable: this.draggable,
26852             enableAutocomplete: this.enableAutocomplete,
26853             enableReverseGeocode: this.enableReverseGeocode
26854         });
26855         
26856         var _marker = new google.maps.Marker({
26857             position: position,
26858             map: _map,
26859             title: this.markerTitle,
26860             draggable: this.draggable
26861         });
26862         
26863         return {
26864             map: _map,
26865             marker: _marker,
26866             circle: null,
26867             location: position,
26868             radius: this.radius,
26869             locationName: this.locationName,
26870             addressComponents: {
26871                 formatted_address: null,
26872                 addressLine1: null,
26873                 addressLine2: null,
26874                 streetName: null,
26875                 streetNumber: null,
26876                 city: null,
26877                 district: null,
26878                 state: null,
26879                 stateOrProvince: null
26880             },
26881             settings: this,
26882             domContainer: this.el.dom,
26883             geodecoder: new google.maps.Geocoder()
26884         };
26885     },
26886     
26887     drawCircle: function(center, radius, options) 
26888     {
26889         if (this.gMapContext.circle != null) {
26890             this.gMapContext.circle.setMap(null);
26891         }
26892         if (radius > 0) {
26893             radius *= 1;
26894             options = Roo.apply({}, options, {
26895                 strokeColor: "#0000FF",
26896                 strokeOpacity: .35,
26897                 strokeWeight: 2,
26898                 fillColor: "#0000FF",
26899                 fillOpacity: .2
26900             });
26901             
26902             options.map = this.gMapContext.map;
26903             options.radius = radius;
26904             options.center = center;
26905             this.gMapContext.circle = new google.maps.Circle(options);
26906             return this.gMapContext.circle;
26907         }
26908         
26909         return null;
26910     },
26911     
26912     setPosition: function(location) 
26913     {
26914         this.gMapContext.location = location;
26915         this.gMapContext.marker.setPosition(location);
26916         this.gMapContext.map.panTo(location);
26917         this.drawCircle(location, this.gMapContext.radius, {});
26918         
26919         var _this = this;
26920         
26921         if (this.gMapContext.settings.enableReverseGeocode) {
26922             this.gMapContext.geodecoder.geocode({
26923                 latLng: this.gMapContext.location
26924             }, function(results, status) {
26925                 
26926                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26927                     _this.gMapContext.locationName = results[0].formatted_address;
26928                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26929                     
26930                     _this.fireEvent('positionchanged', this, location);
26931                 }
26932             });
26933             
26934             return;
26935         }
26936         
26937         this.fireEvent('positionchanged', this, location);
26938     },
26939     
26940     resize: function()
26941     {
26942         google.maps.event.trigger(this.gMapContext.map, "resize");
26943         
26944         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26945         
26946         this.fireEvent('resize', this);
26947     },
26948     
26949     setPositionByLatLng: function(latitude, longitude)
26950     {
26951         this.setPosition(new google.maps.LatLng(latitude, longitude));
26952     },
26953     
26954     getCurrentPosition: function() 
26955     {
26956         return {
26957             latitude: this.gMapContext.location.lat(),
26958             longitude: this.gMapContext.location.lng()
26959         };
26960     },
26961     
26962     getAddressName: function() 
26963     {
26964         return this.gMapContext.locationName;
26965     },
26966     
26967     getAddressComponents: function() 
26968     {
26969         return this.gMapContext.addressComponents;
26970     },
26971     
26972     address_component_from_google_geocode: function(address_components) 
26973     {
26974         var result = {};
26975         
26976         for (var i = 0; i < address_components.length; i++) {
26977             var component = address_components[i];
26978             if (component.types.indexOf("postal_code") >= 0) {
26979                 result.postalCode = component.short_name;
26980             } else if (component.types.indexOf("street_number") >= 0) {
26981                 result.streetNumber = component.short_name;
26982             } else if (component.types.indexOf("route") >= 0) {
26983                 result.streetName = component.short_name;
26984             } else if (component.types.indexOf("neighborhood") >= 0) {
26985                 result.city = component.short_name;
26986             } else if (component.types.indexOf("locality") >= 0) {
26987                 result.city = component.short_name;
26988             } else if (component.types.indexOf("sublocality") >= 0) {
26989                 result.district = component.short_name;
26990             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26991                 result.stateOrProvince = component.short_name;
26992             } else if (component.types.indexOf("country") >= 0) {
26993                 result.country = component.short_name;
26994             }
26995         }
26996         
26997         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26998         result.addressLine2 = "";
26999         return result;
27000     },
27001     
27002     setZoomLevel: function(zoom)
27003     {
27004         this.gMapContext.map.setZoom(zoom);
27005     },
27006     
27007     show: function()
27008     {
27009         if(!this.el){
27010             return;
27011         }
27012         
27013         this.el.show();
27014         
27015         this.resize();
27016         
27017         this.fireEvent('show', this);
27018     },
27019     
27020     hide: function()
27021     {
27022         if(!this.el){
27023             return;
27024         }
27025         
27026         this.el.hide();
27027         
27028         this.fireEvent('hide', this);
27029     }
27030     
27031 });
27032
27033 Roo.apply(Roo.bootstrap.LocationPicker, {
27034     
27035     OverlayView : function(map, options)
27036     {
27037         options = options || {};
27038         
27039         this.setMap(map);
27040     }
27041     
27042     
27043 });/*
27044  * - LGPL
27045  *
27046  * Alert
27047  * 
27048  */
27049
27050 /**
27051  * @class Roo.bootstrap.Alert
27052  * @extends Roo.bootstrap.Component
27053  * Bootstrap Alert class
27054  * @cfg {String} title The title of alert
27055  * @cfg {String} html The content of alert
27056  * @cfg {String} weight (  success | info | warning | danger )
27057  * @cfg {String} faicon font-awesomeicon
27058  * 
27059  * @constructor
27060  * Create a new alert
27061  * @param {Object} config The config object
27062  */
27063
27064
27065 Roo.bootstrap.Alert = function(config){
27066     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27067     
27068 };
27069
27070 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27071     
27072     title: '',
27073     html: '',
27074     weight: false,
27075     faicon: false,
27076     
27077     getAutoCreate : function()
27078     {
27079         
27080         var cfg = {
27081             tag : 'div',
27082             cls : 'alert',
27083             cn : [
27084                 {
27085                     tag : 'i',
27086                     cls : 'roo-alert-icon'
27087                     
27088                 },
27089                 {
27090                     tag : 'b',
27091                     cls : 'roo-alert-title',
27092                     html : this.title
27093                 },
27094                 {
27095                     tag : 'span',
27096                     cls : 'roo-alert-text',
27097                     html : this.html
27098                 }
27099             ]
27100         };
27101         
27102         if(this.faicon){
27103             cfg.cn[0].cls += ' fa ' + this.faicon;
27104         }
27105         
27106         if(this.weight){
27107             cfg.cls += ' alert-' + this.weight;
27108         }
27109         
27110         return cfg;
27111     },
27112     
27113     initEvents: function() 
27114     {
27115         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27116     },
27117     
27118     setTitle : function(str)
27119     {
27120         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27121     },
27122     
27123     setText : function(str)
27124     {
27125         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27126     },
27127     
27128     setWeight : function(weight)
27129     {
27130         if(this.weight){
27131             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27132         }
27133         
27134         this.weight = weight;
27135         
27136         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27137     },
27138     
27139     setIcon : function(icon)
27140     {
27141         if(this.faicon){
27142             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27143         }
27144         
27145         this.faicon = icon;
27146         
27147         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27148     },
27149     
27150     hide: function() 
27151     {
27152         this.el.hide();   
27153     },
27154     
27155     show: function() 
27156     {  
27157         this.el.show();   
27158     }
27159     
27160 });
27161
27162  
27163 /*
27164 * Licence: LGPL
27165 */
27166
27167 /**
27168  * @class Roo.bootstrap.UploadCropbox
27169  * @extends Roo.bootstrap.Component
27170  * Bootstrap UploadCropbox class
27171  * @cfg {String} emptyText show when image has been loaded
27172  * @cfg {String} rotateNotify show when image too small to rotate
27173  * @cfg {Number} errorTimeout default 3000
27174  * @cfg {Number} minWidth default 300
27175  * @cfg {Number} minHeight default 300
27176  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27177  * @cfg {Boolean} isDocument (true|false) default false
27178  * @cfg {String} url action url
27179  * @cfg {String} paramName default 'imageUpload'
27180  * @cfg {String} method default POST
27181  * @cfg {Boolean} loadMask (true|false) default true
27182  * @cfg {Boolean} loadingText default 'Loading...'
27183  * 
27184  * @constructor
27185  * Create a new UploadCropbox
27186  * @param {Object} config The config object
27187  */
27188
27189 Roo.bootstrap.UploadCropbox = function(config){
27190     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27191     
27192     this.addEvents({
27193         /**
27194          * @event beforeselectfile
27195          * Fire before select file
27196          * @param {Roo.bootstrap.UploadCropbox} this
27197          */
27198         "beforeselectfile" : true,
27199         /**
27200          * @event initial
27201          * Fire after initEvent
27202          * @param {Roo.bootstrap.UploadCropbox} this
27203          */
27204         "initial" : true,
27205         /**
27206          * @event crop
27207          * Fire after initEvent
27208          * @param {Roo.bootstrap.UploadCropbox} this
27209          * @param {String} data
27210          */
27211         "crop" : true,
27212         /**
27213          * @event prepare
27214          * Fire when preparing the file data
27215          * @param {Roo.bootstrap.UploadCropbox} this
27216          * @param {Object} file
27217          */
27218         "prepare" : true,
27219         /**
27220          * @event exception
27221          * Fire when get exception
27222          * @param {Roo.bootstrap.UploadCropbox} this
27223          * @param {XMLHttpRequest} xhr
27224          */
27225         "exception" : true,
27226         /**
27227          * @event beforeloadcanvas
27228          * Fire before load the canvas
27229          * @param {Roo.bootstrap.UploadCropbox} this
27230          * @param {String} src
27231          */
27232         "beforeloadcanvas" : true,
27233         /**
27234          * @event trash
27235          * Fire when trash image
27236          * @param {Roo.bootstrap.UploadCropbox} this
27237          */
27238         "trash" : true,
27239         /**
27240          * @event download
27241          * Fire when download the image
27242          * @param {Roo.bootstrap.UploadCropbox} this
27243          */
27244         "download" : true,
27245         /**
27246          * @event footerbuttonclick
27247          * Fire when footerbuttonclick
27248          * @param {Roo.bootstrap.UploadCropbox} this
27249          * @param {String} type
27250          */
27251         "footerbuttonclick" : true,
27252         /**
27253          * @event resize
27254          * Fire when resize
27255          * @param {Roo.bootstrap.UploadCropbox} this
27256          */
27257         "resize" : true,
27258         /**
27259          * @event rotate
27260          * Fire when rotate the image
27261          * @param {Roo.bootstrap.UploadCropbox} this
27262          * @param {String} pos
27263          */
27264         "rotate" : true,
27265         /**
27266          * @event inspect
27267          * Fire when inspect the file
27268          * @param {Roo.bootstrap.UploadCropbox} this
27269          * @param {Object} file
27270          */
27271         "inspect" : true,
27272         /**
27273          * @event upload
27274          * Fire when xhr upload the file
27275          * @param {Roo.bootstrap.UploadCropbox} this
27276          * @param {Object} data
27277          */
27278         "upload" : true,
27279         /**
27280          * @event arrange
27281          * Fire when arrange the file data
27282          * @param {Roo.bootstrap.UploadCropbox} this
27283          * @param {Object} formData
27284          */
27285         "arrange" : true
27286     });
27287     
27288     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27289 };
27290
27291 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27292     
27293     emptyText : 'Click to upload image',
27294     rotateNotify : 'Image is too small to rotate',
27295     errorTimeout : 3000,
27296     scale : 0,
27297     baseScale : 1,
27298     rotate : 0,
27299     dragable : false,
27300     pinching : false,
27301     mouseX : 0,
27302     mouseY : 0,
27303     cropData : false,
27304     minWidth : 300,
27305     minHeight : 300,
27306     file : false,
27307     exif : {},
27308     baseRotate : 1,
27309     cropType : 'image/jpeg',
27310     buttons : false,
27311     canvasLoaded : false,
27312     isDocument : false,
27313     method : 'POST',
27314     paramName : 'imageUpload',
27315     loadMask : true,
27316     loadingText : 'Loading...',
27317     maskEl : false,
27318     
27319     getAutoCreate : function()
27320     {
27321         var cfg = {
27322             tag : 'div',
27323             cls : 'roo-upload-cropbox',
27324             cn : [
27325                 {
27326                     tag : 'input',
27327                     cls : 'roo-upload-cropbox-selector',
27328                     type : 'file'
27329                 },
27330                 {
27331                     tag : 'div',
27332                     cls : 'roo-upload-cropbox-body',
27333                     style : 'cursor:pointer',
27334                     cn : [
27335                         {
27336                             tag : 'div',
27337                             cls : 'roo-upload-cropbox-preview'
27338                         },
27339                         {
27340                             tag : 'div',
27341                             cls : 'roo-upload-cropbox-thumb'
27342                         },
27343                         {
27344                             tag : 'div',
27345                             cls : 'roo-upload-cropbox-empty-notify',
27346                             html : this.emptyText
27347                         },
27348                         {
27349                             tag : 'div',
27350                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27351                             html : this.rotateNotify
27352                         }
27353                     ]
27354                 },
27355                 {
27356                     tag : 'div',
27357                     cls : 'roo-upload-cropbox-footer',
27358                     cn : {
27359                         tag : 'div',
27360                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27361                         cn : []
27362                     }
27363                 }
27364             ]
27365         };
27366         
27367         return cfg;
27368     },
27369     
27370     onRender : function(ct, position)
27371     {
27372         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27373         
27374         if (this.buttons.length) {
27375             
27376             Roo.each(this.buttons, function(bb) {
27377                 
27378                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27379                 
27380                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27381                 
27382             }, this);
27383         }
27384         
27385         if(this.loadMask){
27386             this.maskEl = this.el;
27387         }
27388     },
27389     
27390     initEvents : function()
27391     {
27392         this.urlAPI = (window.createObjectURL && window) || 
27393                                 (window.URL && URL.revokeObjectURL && URL) || 
27394                                 (window.webkitURL && webkitURL);
27395                         
27396         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27397         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27398         
27399         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27400         this.selectorEl.hide();
27401         
27402         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27403         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27404         
27405         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27406         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27407         this.thumbEl.hide();
27408         
27409         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27410         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27411         
27412         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27413         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27414         this.errorEl.hide();
27415         
27416         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27417         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27418         this.footerEl.hide();
27419         
27420         this.setThumbBoxSize();
27421         
27422         this.bind();
27423         
27424         this.resize();
27425         
27426         this.fireEvent('initial', this);
27427     },
27428
27429     bind : function()
27430     {
27431         var _this = this;
27432         
27433         window.addEventListener("resize", function() { _this.resize(); } );
27434         
27435         this.bodyEl.on('click', this.beforeSelectFile, this);
27436         
27437         if(Roo.isTouch){
27438             this.bodyEl.on('touchstart', this.onTouchStart, this);
27439             this.bodyEl.on('touchmove', this.onTouchMove, this);
27440             this.bodyEl.on('touchend', this.onTouchEnd, this);
27441         }
27442         
27443         if(!Roo.isTouch){
27444             this.bodyEl.on('mousedown', this.onMouseDown, this);
27445             this.bodyEl.on('mousemove', this.onMouseMove, this);
27446             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27447             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27448             Roo.get(document).on('mouseup', this.onMouseUp, this);
27449         }
27450         
27451         this.selectorEl.on('change', this.onFileSelected, this);
27452     },
27453     
27454     reset : function()
27455     {    
27456         this.scale = 0;
27457         this.baseScale = 1;
27458         this.rotate = 0;
27459         this.baseRotate = 1;
27460         this.dragable = false;
27461         this.pinching = false;
27462         this.mouseX = 0;
27463         this.mouseY = 0;
27464         this.cropData = false;
27465         this.notifyEl.dom.innerHTML = this.emptyText;
27466         
27467         this.selectorEl.dom.value = '';
27468         
27469     },
27470     
27471     resize : function()
27472     {
27473         if(this.fireEvent('resize', this) != false){
27474             this.setThumbBoxPosition();
27475             this.setCanvasPosition();
27476         }
27477     },
27478     
27479     onFooterButtonClick : function(e, el, o, type)
27480     {
27481         switch (type) {
27482             case 'rotate-left' :
27483                 this.onRotateLeft(e);
27484                 break;
27485             case 'rotate-right' :
27486                 this.onRotateRight(e);
27487                 break;
27488             case 'picture' :
27489                 this.beforeSelectFile(e);
27490                 break;
27491             case 'trash' :
27492                 this.trash(e);
27493                 break;
27494             case 'crop' :
27495                 this.crop(e);
27496                 break;
27497             case 'download' :
27498                 this.download(e);
27499                 break;
27500             default :
27501                 break;
27502         }
27503         
27504         this.fireEvent('footerbuttonclick', this, type);
27505     },
27506     
27507     beforeSelectFile : function(e)
27508     {
27509         e.preventDefault();
27510         
27511         if(this.fireEvent('beforeselectfile', this) != false){
27512             this.selectorEl.dom.click();
27513         }
27514     },
27515     
27516     onFileSelected : function(e)
27517     {
27518         e.preventDefault();
27519         
27520         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27521             return;
27522         }
27523         
27524         var file = this.selectorEl.dom.files[0];
27525         
27526         if(this.fireEvent('inspect', this, file) != false){
27527             this.prepare(file);
27528         }
27529         
27530     },
27531     
27532     trash : function(e)
27533     {
27534         this.fireEvent('trash', this);
27535     },
27536     
27537     download : function(e)
27538     {
27539         this.fireEvent('download', this);
27540     },
27541     
27542     loadCanvas : function(src)
27543     {   
27544         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27545             
27546             this.reset();
27547             
27548             this.imageEl = document.createElement('img');
27549             
27550             var _this = this;
27551             
27552             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27553             
27554             this.imageEl.src = src;
27555         }
27556     },
27557     
27558     onLoadCanvas : function()
27559     {   
27560         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27561         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27562         
27563         this.bodyEl.un('click', this.beforeSelectFile, this);
27564         
27565         this.notifyEl.hide();
27566         this.thumbEl.show();
27567         this.footerEl.show();
27568         
27569         this.baseRotateLevel();
27570         
27571         if(this.isDocument){
27572             this.setThumbBoxSize();
27573         }
27574         
27575         this.setThumbBoxPosition();
27576         
27577         this.baseScaleLevel();
27578         
27579         this.draw();
27580         
27581         this.resize();
27582         
27583         this.canvasLoaded = true;
27584         
27585         if(this.loadMask){
27586             this.maskEl.unmask();
27587         }
27588         
27589     },
27590     
27591     setCanvasPosition : function()
27592     {   
27593         if(!this.canvasEl){
27594             return;
27595         }
27596         
27597         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27598         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27599         
27600         this.previewEl.setLeft(pw);
27601         this.previewEl.setTop(ph);
27602         
27603     },
27604     
27605     onMouseDown : function(e)
27606     {   
27607         e.stopEvent();
27608         
27609         this.dragable = true;
27610         this.pinching = false;
27611         
27612         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27613             this.dragable = false;
27614             return;
27615         }
27616         
27617         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27618         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27619         
27620     },
27621     
27622     onMouseMove : function(e)
27623     {   
27624         e.stopEvent();
27625         
27626         if(!this.canvasLoaded){
27627             return;
27628         }
27629         
27630         if (!this.dragable){
27631             return;
27632         }
27633         
27634         var minX = Math.ceil(this.thumbEl.getLeft(true));
27635         var minY = Math.ceil(this.thumbEl.getTop(true));
27636         
27637         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27638         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27639         
27640         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27641         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27642         
27643         x = x - this.mouseX;
27644         y = y - this.mouseY;
27645         
27646         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27647         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27648         
27649         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27650         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27651         
27652         this.previewEl.setLeft(bgX);
27653         this.previewEl.setTop(bgY);
27654         
27655         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27656         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27657     },
27658     
27659     onMouseUp : function(e)
27660     {   
27661         e.stopEvent();
27662         
27663         this.dragable = false;
27664     },
27665     
27666     onMouseWheel : function(e)
27667     {   
27668         e.stopEvent();
27669         
27670         this.startScale = this.scale;
27671         
27672         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27673         
27674         if(!this.zoomable()){
27675             this.scale = this.startScale;
27676             return;
27677         }
27678         
27679         this.draw();
27680         
27681         return;
27682     },
27683     
27684     zoomable : function()
27685     {
27686         var minScale = this.thumbEl.getWidth() / this.minWidth;
27687         
27688         if(this.minWidth < this.minHeight){
27689             minScale = this.thumbEl.getHeight() / this.minHeight;
27690         }
27691         
27692         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27693         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27694         
27695         if(
27696                 this.isDocument &&
27697                 (this.rotate == 0 || this.rotate == 180) && 
27698                 (
27699                     width > this.imageEl.OriginWidth || 
27700                     height > this.imageEl.OriginHeight ||
27701                     (width < this.minWidth && height < this.minHeight)
27702                 )
27703         ){
27704             return false;
27705         }
27706         
27707         if(
27708                 this.isDocument &&
27709                 (this.rotate == 90 || this.rotate == 270) && 
27710                 (
27711                     width > this.imageEl.OriginWidth || 
27712                     height > this.imageEl.OriginHeight ||
27713                     (width < this.minHeight && height < this.minWidth)
27714                 )
27715         ){
27716             return false;
27717         }
27718         
27719         if(
27720                 !this.isDocument &&
27721                 (this.rotate == 0 || this.rotate == 180) && 
27722                 (
27723                     width < this.minWidth || 
27724                     width > this.imageEl.OriginWidth || 
27725                     height < this.minHeight || 
27726                     height > this.imageEl.OriginHeight
27727                 )
27728         ){
27729             return false;
27730         }
27731         
27732         if(
27733                 !this.isDocument &&
27734                 (this.rotate == 90 || this.rotate == 270) && 
27735                 (
27736                     width < this.minHeight || 
27737                     width > this.imageEl.OriginWidth || 
27738                     height < this.minWidth || 
27739                     height > this.imageEl.OriginHeight
27740                 )
27741         ){
27742             return false;
27743         }
27744         
27745         return true;
27746         
27747     },
27748     
27749     onRotateLeft : function(e)
27750     {   
27751         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27752             
27753             var minScale = this.thumbEl.getWidth() / this.minWidth;
27754             
27755             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27756             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27757             
27758             this.startScale = this.scale;
27759             
27760             while (this.getScaleLevel() < minScale){
27761             
27762                 this.scale = this.scale + 1;
27763                 
27764                 if(!this.zoomable()){
27765                     break;
27766                 }
27767                 
27768                 if(
27769                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27770                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27771                 ){
27772                     continue;
27773                 }
27774                 
27775                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27776
27777                 this.draw();
27778                 
27779                 return;
27780             }
27781             
27782             this.scale = this.startScale;
27783             
27784             this.onRotateFail();
27785             
27786             return false;
27787         }
27788         
27789         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27790
27791         if(this.isDocument){
27792             this.setThumbBoxSize();
27793             this.setThumbBoxPosition();
27794             this.setCanvasPosition();
27795         }
27796         
27797         this.draw();
27798         
27799         this.fireEvent('rotate', this, 'left');
27800         
27801     },
27802     
27803     onRotateRight : function(e)
27804     {
27805         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27806             
27807             var minScale = this.thumbEl.getWidth() / this.minWidth;
27808         
27809             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27810             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27811             
27812             this.startScale = this.scale;
27813             
27814             while (this.getScaleLevel() < minScale){
27815             
27816                 this.scale = this.scale + 1;
27817                 
27818                 if(!this.zoomable()){
27819                     break;
27820                 }
27821                 
27822                 if(
27823                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27824                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27825                 ){
27826                     continue;
27827                 }
27828                 
27829                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27830
27831                 this.draw();
27832                 
27833                 return;
27834             }
27835             
27836             this.scale = this.startScale;
27837             
27838             this.onRotateFail();
27839             
27840             return false;
27841         }
27842         
27843         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27844
27845         if(this.isDocument){
27846             this.setThumbBoxSize();
27847             this.setThumbBoxPosition();
27848             this.setCanvasPosition();
27849         }
27850         
27851         this.draw();
27852         
27853         this.fireEvent('rotate', this, 'right');
27854     },
27855     
27856     onRotateFail : function()
27857     {
27858         this.errorEl.show(true);
27859         
27860         var _this = this;
27861         
27862         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27863     },
27864     
27865     draw : function()
27866     {
27867         this.previewEl.dom.innerHTML = '';
27868         
27869         var canvasEl = document.createElement("canvas");
27870         
27871         var contextEl = canvasEl.getContext("2d");
27872         
27873         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27874         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27875         var center = this.imageEl.OriginWidth / 2;
27876         
27877         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27878             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27879             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27880             center = this.imageEl.OriginHeight / 2;
27881         }
27882         
27883         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27884         
27885         contextEl.translate(center, center);
27886         contextEl.rotate(this.rotate * Math.PI / 180);
27887
27888         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27889         
27890         this.canvasEl = document.createElement("canvas");
27891         
27892         this.contextEl = this.canvasEl.getContext("2d");
27893         
27894         switch (this.rotate) {
27895             case 0 :
27896                 
27897                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27898                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27899                 
27900                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27901                 
27902                 break;
27903             case 90 : 
27904                 
27905                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27906                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27907                 
27908                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27909                     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);
27910                     break;
27911                 }
27912                 
27913                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27914                 
27915                 break;
27916             case 180 :
27917                 
27918                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27919                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27920                 
27921                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27922                     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);
27923                     break;
27924                 }
27925                 
27926                 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);
27927                 
27928                 break;
27929             case 270 :
27930                 
27931                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27932                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27933         
27934                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27935                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27936                     break;
27937                 }
27938                 
27939                 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);
27940                 
27941                 break;
27942             default : 
27943                 break;
27944         }
27945         
27946         this.previewEl.appendChild(this.canvasEl);
27947         
27948         this.setCanvasPosition();
27949     },
27950     
27951     crop : function()
27952     {
27953         if(!this.canvasLoaded){
27954             return;
27955         }
27956         
27957         var imageCanvas = document.createElement("canvas");
27958         
27959         var imageContext = imageCanvas.getContext("2d");
27960         
27961         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27962         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27963         
27964         var center = imageCanvas.width / 2;
27965         
27966         imageContext.translate(center, center);
27967         
27968         imageContext.rotate(this.rotate * Math.PI / 180);
27969         
27970         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27971         
27972         var canvas = document.createElement("canvas");
27973         
27974         var context = canvas.getContext("2d");
27975                 
27976         canvas.width = this.minWidth;
27977         canvas.height = this.minHeight;
27978
27979         switch (this.rotate) {
27980             case 0 :
27981                 
27982                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27983                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27984                 
27985                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27986                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27987                 
27988                 var targetWidth = this.minWidth - 2 * x;
27989                 var targetHeight = this.minHeight - 2 * y;
27990                 
27991                 var scale = 1;
27992                 
27993                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27994                     scale = targetWidth / width;
27995                 }
27996                 
27997                 if(x > 0 && y == 0){
27998                     scale = targetHeight / height;
27999                 }
28000                 
28001                 if(x > 0 && y > 0){
28002                     scale = targetWidth / width;
28003                     
28004                     if(width < height){
28005                         scale = targetHeight / height;
28006                     }
28007                 }
28008                 
28009                 context.scale(scale, scale);
28010                 
28011                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28012                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28013
28014                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28015                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28016
28017                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28018                 
28019                 break;
28020             case 90 : 
28021                 
28022                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28023                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28024                 
28025                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28026                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28027                 
28028                 var targetWidth = this.minWidth - 2 * x;
28029                 var targetHeight = this.minHeight - 2 * y;
28030                 
28031                 var scale = 1;
28032                 
28033                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28034                     scale = targetWidth / width;
28035                 }
28036                 
28037                 if(x > 0 && y == 0){
28038                     scale = targetHeight / height;
28039                 }
28040                 
28041                 if(x > 0 && y > 0){
28042                     scale = targetWidth / width;
28043                     
28044                     if(width < height){
28045                         scale = targetHeight / height;
28046                     }
28047                 }
28048                 
28049                 context.scale(scale, scale);
28050                 
28051                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28052                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28053
28054                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28055                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28056                 
28057                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28058                 
28059                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28060                 
28061                 break;
28062             case 180 :
28063                 
28064                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28065                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28066                 
28067                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28068                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28069                 
28070                 var targetWidth = this.minWidth - 2 * x;
28071                 var targetHeight = this.minHeight - 2 * y;
28072                 
28073                 var scale = 1;
28074                 
28075                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28076                     scale = targetWidth / width;
28077                 }
28078                 
28079                 if(x > 0 && y == 0){
28080                     scale = targetHeight / height;
28081                 }
28082                 
28083                 if(x > 0 && y > 0){
28084                     scale = targetWidth / width;
28085                     
28086                     if(width < height){
28087                         scale = targetHeight / height;
28088                     }
28089                 }
28090                 
28091                 context.scale(scale, scale);
28092                 
28093                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28094                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28095
28096                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28097                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28098
28099                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28100                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28101                 
28102                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28103                 
28104                 break;
28105             case 270 :
28106                 
28107                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28108                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28109                 
28110                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28111                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28112                 
28113                 var targetWidth = this.minWidth - 2 * x;
28114                 var targetHeight = this.minHeight - 2 * y;
28115                 
28116                 var scale = 1;
28117                 
28118                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28119                     scale = targetWidth / width;
28120                 }
28121                 
28122                 if(x > 0 && y == 0){
28123                     scale = targetHeight / height;
28124                 }
28125                 
28126                 if(x > 0 && y > 0){
28127                     scale = targetWidth / width;
28128                     
28129                     if(width < height){
28130                         scale = targetHeight / height;
28131                     }
28132                 }
28133                 
28134                 context.scale(scale, scale);
28135                 
28136                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28137                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28138
28139                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28140                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28141                 
28142                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28143                 
28144                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28145                 
28146                 break;
28147             default : 
28148                 break;
28149         }
28150         
28151         this.cropData = canvas.toDataURL(this.cropType);
28152         
28153         if(this.fireEvent('crop', this, this.cropData) !== false){
28154             this.process(this.file, this.cropData);
28155         }
28156         
28157         return;
28158         
28159     },
28160     
28161     setThumbBoxSize : function()
28162     {
28163         var width, height;
28164         
28165         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28166             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28167             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28168             
28169             this.minWidth = width;
28170             this.minHeight = height;
28171             
28172             if(this.rotate == 90 || this.rotate == 270){
28173                 this.minWidth = height;
28174                 this.minHeight = width;
28175             }
28176         }
28177         
28178         height = 300;
28179         width = Math.ceil(this.minWidth * height / this.minHeight);
28180         
28181         if(this.minWidth > this.minHeight){
28182             width = 300;
28183             height = Math.ceil(this.minHeight * width / this.minWidth);
28184         }
28185         
28186         this.thumbEl.setStyle({
28187             width : width + 'px',
28188             height : height + 'px'
28189         });
28190
28191         return;
28192             
28193     },
28194     
28195     setThumbBoxPosition : function()
28196     {
28197         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28198         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28199         
28200         this.thumbEl.setLeft(x);
28201         this.thumbEl.setTop(y);
28202         
28203     },
28204     
28205     baseRotateLevel : function()
28206     {
28207         this.baseRotate = 1;
28208         
28209         if(
28210                 typeof(this.exif) != 'undefined' &&
28211                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28212                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28213         ){
28214             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28215         }
28216         
28217         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28218         
28219     },
28220     
28221     baseScaleLevel : function()
28222     {
28223         var width, height;
28224         
28225         if(this.isDocument){
28226             
28227             if(this.baseRotate == 6 || this.baseRotate == 8){
28228             
28229                 height = this.thumbEl.getHeight();
28230                 this.baseScale = height / this.imageEl.OriginWidth;
28231
28232                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28233                     width = this.thumbEl.getWidth();
28234                     this.baseScale = width / this.imageEl.OriginHeight;
28235                 }
28236
28237                 return;
28238             }
28239
28240             height = this.thumbEl.getHeight();
28241             this.baseScale = height / this.imageEl.OriginHeight;
28242
28243             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28244                 width = this.thumbEl.getWidth();
28245                 this.baseScale = width / this.imageEl.OriginWidth;
28246             }
28247
28248             return;
28249         }
28250         
28251         if(this.baseRotate == 6 || this.baseRotate == 8){
28252             
28253             width = this.thumbEl.getHeight();
28254             this.baseScale = width / this.imageEl.OriginHeight;
28255             
28256             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28257                 height = this.thumbEl.getWidth();
28258                 this.baseScale = height / this.imageEl.OriginHeight;
28259             }
28260             
28261             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28262                 height = this.thumbEl.getWidth();
28263                 this.baseScale = height / this.imageEl.OriginHeight;
28264                 
28265                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28266                     width = this.thumbEl.getHeight();
28267                     this.baseScale = width / this.imageEl.OriginWidth;
28268                 }
28269             }
28270             
28271             return;
28272         }
28273         
28274         width = this.thumbEl.getWidth();
28275         this.baseScale = width / this.imageEl.OriginWidth;
28276         
28277         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28278             height = this.thumbEl.getHeight();
28279             this.baseScale = height / this.imageEl.OriginHeight;
28280         }
28281         
28282         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28283             
28284             height = this.thumbEl.getHeight();
28285             this.baseScale = height / this.imageEl.OriginHeight;
28286             
28287             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28288                 width = this.thumbEl.getWidth();
28289                 this.baseScale = width / this.imageEl.OriginWidth;
28290             }
28291             
28292         }
28293         
28294         return;
28295     },
28296     
28297     getScaleLevel : function()
28298     {
28299         return this.baseScale * Math.pow(1.1, this.scale);
28300     },
28301     
28302     onTouchStart : function(e)
28303     {
28304         if(!this.canvasLoaded){
28305             this.beforeSelectFile(e);
28306             return;
28307         }
28308         
28309         var touches = e.browserEvent.touches;
28310         
28311         if(!touches){
28312             return;
28313         }
28314         
28315         if(touches.length == 1){
28316             this.onMouseDown(e);
28317             return;
28318         }
28319         
28320         if(touches.length != 2){
28321             return;
28322         }
28323         
28324         var coords = [];
28325         
28326         for(var i = 0, finger; finger = touches[i]; i++){
28327             coords.push(finger.pageX, finger.pageY);
28328         }
28329         
28330         var x = Math.pow(coords[0] - coords[2], 2);
28331         var y = Math.pow(coords[1] - coords[3], 2);
28332         
28333         this.startDistance = Math.sqrt(x + y);
28334         
28335         this.startScale = this.scale;
28336         
28337         this.pinching = true;
28338         this.dragable = false;
28339         
28340     },
28341     
28342     onTouchMove : function(e)
28343     {
28344         if(!this.pinching && !this.dragable){
28345             return;
28346         }
28347         
28348         var touches = e.browserEvent.touches;
28349         
28350         if(!touches){
28351             return;
28352         }
28353         
28354         if(this.dragable){
28355             this.onMouseMove(e);
28356             return;
28357         }
28358         
28359         var coords = [];
28360         
28361         for(var i = 0, finger; finger = touches[i]; i++){
28362             coords.push(finger.pageX, finger.pageY);
28363         }
28364         
28365         var x = Math.pow(coords[0] - coords[2], 2);
28366         var y = Math.pow(coords[1] - coords[3], 2);
28367         
28368         this.endDistance = Math.sqrt(x + y);
28369         
28370         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28371         
28372         if(!this.zoomable()){
28373             this.scale = this.startScale;
28374             return;
28375         }
28376         
28377         this.draw();
28378         
28379     },
28380     
28381     onTouchEnd : function(e)
28382     {
28383         this.pinching = false;
28384         this.dragable = false;
28385         
28386     },
28387     
28388     process : function(file, crop)
28389     {
28390         if(this.loadMask){
28391             this.maskEl.mask(this.loadingText);
28392         }
28393         
28394         this.xhr = new XMLHttpRequest();
28395         
28396         file.xhr = this.xhr;
28397
28398         this.xhr.open(this.method, this.url, true);
28399         
28400         var headers = {
28401             "Accept": "application/json",
28402             "Cache-Control": "no-cache",
28403             "X-Requested-With": "XMLHttpRequest"
28404         };
28405         
28406         for (var headerName in headers) {
28407             var headerValue = headers[headerName];
28408             if (headerValue) {
28409                 this.xhr.setRequestHeader(headerName, headerValue);
28410             }
28411         }
28412         
28413         var _this = this;
28414         
28415         this.xhr.onload = function()
28416         {
28417             _this.xhrOnLoad(_this.xhr);
28418         }
28419         
28420         this.xhr.onerror = function()
28421         {
28422             _this.xhrOnError(_this.xhr);
28423         }
28424         
28425         var formData = new FormData();
28426
28427         formData.append('returnHTML', 'NO');
28428         
28429         if(crop){
28430             formData.append('crop', crop);
28431         }
28432         
28433         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28434             formData.append(this.paramName, file, file.name);
28435         }
28436         
28437         if(typeof(file.filename) != 'undefined'){
28438             formData.append('filename', file.filename);
28439         }
28440         
28441         if(typeof(file.mimetype) != 'undefined'){
28442             formData.append('mimetype', file.mimetype);
28443         }
28444         
28445         if(this.fireEvent('arrange', this, formData) != false){
28446             this.xhr.send(formData);
28447         };
28448     },
28449     
28450     xhrOnLoad : function(xhr)
28451     {
28452         if(this.loadMask){
28453             this.maskEl.unmask();
28454         }
28455         
28456         if (xhr.readyState !== 4) {
28457             this.fireEvent('exception', this, xhr);
28458             return;
28459         }
28460
28461         var response = Roo.decode(xhr.responseText);
28462         
28463         if(!response.success){
28464             this.fireEvent('exception', this, xhr);
28465             return;
28466         }
28467         
28468         var response = Roo.decode(xhr.responseText);
28469         
28470         this.fireEvent('upload', this, response);
28471         
28472     },
28473     
28474     xhrOnError : function()
28475     {
28476         if(this.loadMask){
28477             this.maskEl.unmask();
28478         }
28479         
28480         Roo.log('xhr on error');
28481         
28482         var response = Roo.decode(xhr.responseText);
28483           
28484         Roo.log(response);
28485         
28486     },
28487     
28488     prepare : function(file)
28489     {   
28490         if(this.loadMask){
28491             this.maskEl.mask(this.loadingText);
28492         }
28493         
28494         this.file = false;
28495         this.exif = {};
28496         
28497         if(typeof(file) === 'string'){
28498             this.loadCanvas(file);
28499             return;
28500         }
28501         
28502         if(!file || !this.urlAPI){
28503             return;
28504         }
28505         
28506         this.file = file;
28507         this.cropType = file.type;
28508         
28509         var _this = this;
28510         
28511         if(this.fireEvent('prepare', this, this.file) != false){
28512             
28513             var reader = new FileReader();
28514             
28515             reader.onload = function (e) {
28516                 if (e.target.error) {
28517                     Roo.log(e.target.error);
28518                     return;
28519                 }
28520                 
28521                 var buffer = e.target.result,
28522                     dataView = new DataView(buffer),
28523                     offset = 2,
28524                     maxOffset = dataView.byteLength - 4,
28525                     markerBytes,
28526                     markerLength;
28527                 
28528                 if (dataView.getUint16(0) === 0xffd8) {
28529                     while (offset < maxOffset) {
28530                         markerBytes = dataView.getUint16(offset);
28531                         
28532                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28533                             markerLength = dataView.getUint16(offset + 2) + 2;
28534                             if (offset + markerLength > dataView.byteLength) {
28535                                 Roo.log('Invalid meta data: Invalid segment size.');
28536                                 break;
28537                             }
28538                             
28539                             if(markerBytes == 0xffe1){
28540                                 _this.parseExifData(
28541                                     dataView,
28542                                     offset,
28543                                     markerLength
28544                                 );
28545                             }
28546                             
28547                             offset += markerLength;
28548                             
28549                             continue;
28550                         }
28551                         
28552                         break;
28553                     }
28554                     
28555                 }
28556                 
28557                 var url = _this.urlAPI.createObjectURL(_this.file);
28558                 
28559                 _this.loadCanvas(url);
28560                 
28561                 return;
28562             }
28563             
28564             reader.readAsArrayBuffer(this.file);
28565             
28566         }
28567         
28568     },
28569     
28570     parseExifData : function(dataView, offset, length)
28571     {
28572         var tiffOffset = offset + 10,
28573             littleEndian,
28574             dirOffset;
28575     
28576         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28577             // No Exif data, might be XMP data instead
28578             return;
28579         }
28580         
28581         // Check for the ASCII code for "Exif" (0x45786966):
28582         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28583             // No Exif data, might be XMP data instead
28584             return;
28585         }
28586         if (tiffOffset + 8 > dataView.byteLength) {
28587             Roo.log('Invalid Exif data: Invalid segment size.');
28588             return;
28589         }
28590         // Check for the two null bytes:
28591         if (dataView.getUint16(offset + 8) !== 0x0000) {
28592             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28593             return;
28594         }
28595         // Check the byte alignment:
28596         switch (dataView.getUint16(tiffOffset)) {
28597         case 0x4949:
28598             littleEndian = true;
28599             break;
28600         case 0x4D4D:
28601             littleEndian = false;
28602             break;
28603         default:
28604             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28605             return;
28606         }
28607         // Check for the TIFF tag marker (0x002A):
28608         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28609             Roo.log('Invalid Exif data: Missing TIFF marker.');
28610             return;
28611         }
28612         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28613         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28614         
28615         this.parseExifTags(
28616             dataView,
28617             tiffOffset,
28618             tiffOffset + dirOffset,
28619             littleEndian
28620         );
28621     },
28622     
28623     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28624     {
28625         var tagsNumber,
28626             dirEndOffset,
28627             i;
28628         if (dirOffset + 6 > dataView.byteLength) {
28629             Roo.log('Invalid Exif data: Invalid directory offset.');
28630             return;
28631         }
28632         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28633         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28634         if (dirEndOffset + 4 > dataView.byteLength) {
28635             Roo.log('Invalid Exif data: Invalid directory size.');
28636             return;
28637         }
28638         for (i = 0; i < tagsNumber; i += 1) {
28639             this.parseExifTag(
28640                 dataView,
28641                 tiffOffset,
28642                 dirOffset + 2 + 12 * i, // tag offset
28643                 littleEndian
28644             );
28645         }
28646         // Return the offset to the next directory:
28647         return dataView.getUint32(dirEndOffset, littleEndian);
28648     },
28649     
28650     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28651     {
28652         var tag = dataView.getUint16(offset, littleEndian);
28653         
28654         this.exif[tag] = this.getExifValue(
28655             dataView,
28656             tiffOffset,
28657             offset,
28658             dataView.getUint16(offset + 2, littleEndian), // tag type
28659             dataView.getUint32(offset + 4, littleEndian), // tag length
28660             littleEndian
28661         );
28662     },
28663     
28664     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28665     {
28666         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28667             tagSize,
28668             dataOffset,
28669             values,
28670             i,
28671             str,
28672             c;
28673     
28674         if (!tagType) {
28675             Roo.log('Invalid Exif data: Invalid tag type.');
28676             return;
28677         }
28678         
28679         tagSize = tagType.size * length;
28680         // Determine if the value is contained in the dataOffset bytes,
28681         // or if the value at the dataOffset is a pointer to the actual data:
28682         dataOffset = tagSize > 4 ?
28683                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28684         if (dataOffset + tagSize > dataView.byteLength) {
28685             Roo.log('Invalid Exif data: Invalid data offset.');
28686             return;
28687         }
28688         if (length === 1) {
28689             return tagType.getValue(dataView, dataOffset, littleEndian);
28690         }
28691         values = [];
28692         for (i = 0; i < length; i += 1) {
28693             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28694         }
28695         
28696         if (tagType.ascii) {
28697             str = '';
28698             // Concatenate the chars:
28699             for (i = 0; i < values.length; i += 1) {
28700                 c = values[i];
28701                 // Ignore the terminating NULL byte(s):
28702                 if (c === '\u0000') {
28703                     break;
28704                 }
28705                 str += c;
28706             }
28707             return str;
28708         }
28709         return values;
28710     }
28711     
28712 });
28713
28714 Roo.apply(Roo.bootstrap.UploadCropbox, {
28715     tags : {
28716         'Orientation': 0x0112
28717     },
28718     
28719     Orientation: {
28720             1: 0, //'top-left',
28721 //            2: 'top-right',
28722             3: 180, //'bottom-right',
28723 //            4: 'bottom-left',
28724 //            5: 'left-top',
28725             6: 90, //'right-top',
28726 //            7: 'right-bottom',
28727             8: 270 //'left-bottom'
28728     },
28729     
28730     exifTagTypes : {
28731         // byte, 8-bit unsigned int:
28732         1: {
28733             getValue: function (dataView, dataOffset) {
28734                 return dataView.getUint8(dataOffset);
28735             },
28736             size: 1
28737         },
28738         // ascii, 8-bit byte:
28739         2: {
28740             getValue: function (dataView, dataOffset) {
28741                 return String.fromCharCode(dataView.getUint8(dataOffset));
28742             },
28743             size: 1,
28744             ascii: true
28745         },
28746         // short, 16 bit int:
28747         3: {
28748             getValue: function (dataView, dataOffset, littleEndian) {
28749                 return dataView.getUint16(dataOffset, littleEndian);
28750             },
28751             size: 2
28752         },
28753         // long, 32 bit int:
28754         4: {
28755             getValue: function (dataView, dataOffset, littleEndian) {
28756                 return dataView.getUint32(dataOffset, littleEndian);
28757             },
28758             size: 4
28759         },
28760         // rational = two long values, first is numerator, second is denominator:
28761         5: {
28762             getValue: function (dataView, dataOffset, littleEndian) {
28763                 return dataView.getUint32(dataOffset, littleEndian) /
28764                     dataView.getUint32(dataOffset + 4, littleEndian);
28765             },
28766             size: 8
28767         },
28768         // slong, 32 bit signed int:
28769         9: {
28770             getValue: function (dataView, dataOffset, littleEndian) {
28771                 return dataView.getInt32(dataOffset, littleEndian);
28772             },
28773             size: 4
28774         },
28775         // srational, two slongs, first is numerator, second is denominator:
28776         10: {
28777             getValue: function (dataView, dataOffset, littleEndian) {
28778                 return dataView.getInt32(dataOffset, littleEndian) /
28779                     dataView.getInt32(dataOffset + 4, littleEndian);
28780             },
28781             size: 8
28782         }
28783     },
28784     
28785     footer : {
28786         STANDARD : [
28787             {
28788                 tag : 'div',
28789                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28790                 action : 'rotate-left',
28791                 cn : [
28792                     {
28793                         tag : 'button',
28794                         cls : 'btn btn-default',
28795                         html : '<i class="fa fa-undo"></i>'
28796                     }
28797                 ]
28798             },
28799             {
28800                 tag : 'div',
28801                 cls : 'btn-group roo-upload-cropbox-picture',
28802                 action : 'picture',
28803                 cn : [
28804                     {
28805                         tag : 'button',
28806                         cls : 'btn btn-default',
28807                         html : '<i class="fa fa-picture-o"></i>'
28808                     }
28809                 ]
28810             },
28811             {
28812                 tag : 'div',
28813                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28814                 action : 'rotate-right',
28815                 cn : [
28816                     {
28817                         tag : 'button',
28818                         cls : 'btn btn-default',
28819                         html : '<i class="fa fa-repeat"></i>'
28820                     }
28821                 ]
28822             }
28823         ],
28824         DOCUMENT : [
28825             {
28826                 tag : 'div',
28827                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28828                 action : 'rotate-left',
28829                 cn : [
28830                     {
28831                         tag : 'button',
28832                         cls : 'btn btn-default',
28833                         html : '<i class="fa fa-undo"></i>'
28834                     }
28835                 ]
28836             },
28837             {
28838                 tag : 'div',
28839                 cls : 'btn-group roo-upload-cropbox-download',
28840                 action : 'download',
28841                 cn : [
28842                     {
28843                         tag : 'button',
28844                         cls : 'btn btn-default',
28845                         html : '<i class="fa fa-download"></i>'
28846                     }
28847                 ]
28848             },
28849             {
28850                 tag : 'div',
28851                 cls : 'btn-group roo-upload-cropbox-crop',
28852                 action : 'crop',
28853                 cn : [
28854                     {
28855                         tag : 'button',
28856                         cls : 'btn btn-default',
28857                         html : '<i class="fa fa-crop"></i>'
28858                     }
28859                 ]
28860             },
28861             {
28862                 tag : 'div',
28863                 cls : 'btn-group roo-upload-cropbox-trash',
28864                 action : 'trash',
28865                 cn : [
28866                     {
28867                         tag : 'button',
28868                         cls : 'btn btn-default',
28869                         html : '<i class="fa fa-trash"></i>'
28870                     }
28871                 ]
28872             },
28873             {
28874                 tag : 'div',
28875                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28876                 action : 'rotate-right',
28877                 cn : [
28878                     {
28879                         tag : 'button',
28880                         cls : 'btn btn-default',
28881                         html : '<i class="fa fa-repeat"></i>'
28882                     }
28883                 ]
28884             }
28885         ],
28886         ROTATOR : [
28887             {
28888                 tag : 'div',
28889                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28890                 action : 'rotate-left',
28891                 cn : [
28892                     {
28893                         tag : 'button',
28894                         cls : 'btn btn-default',
28895                         html : '<i class="fa fa-undo"></i>'
28896                     }
28897                 ]
28898             },
28899             {
28900                 tag : 'div',
28901                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28902                 action : 'rotate-right',
28903                 cn : [
28904                     {
28905                         tag : 'button',
28906                         cls : 'btn btn-default',
28907                         html : '<i class="fa fa-repeat"></i>'
28908                     }
28909                 ]
28910             }
28911         ]
28912     }
28913 });
28914
28915 /*
28916 * Licence: LGPL
28917 */
28918
28919 /**
28920  * @class Roo.bootstrap.DocumentManager
28921  * @extends Roo.bootstrap.Component
28922  * Bootstrap DocumentManager class
28923  * @cfg {String} paramName default 'imageUpload'
28924  * @cfg {String} toolTipName default 'filename'
28925  * @cfg {String} method default POST
28926  * @cfg {String} url action url
28927  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28928  * @cfg {Boolean} multiple multiple upload default true
28929  * @cfg {Number} thumbSize default 300
28930  * @cfg {String} fieldLabel
28931  * @cfg {Number} labelWidth default 4
28932  * @cfg {String} labelAlign (left|top) default left
28933  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28934 * @cfg {Number} labellg set the width of label (1-12)
28935  * @cfg {Number} labelmd set the width of label (1-12)
28936  * @cfg {Number} labelsm set the width of label (1-12)
28937  * @cfg {Number} labelxs set the width of label (1-12)
28938  * 
28939  * @constructor
28940  * Create a new DocumentManager
28941  * @param {Object} config The config object
28942  */
28943
28944 Roo.bootstrap.DocumentManager = function(config){
28945     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28946     
28947     this.files = [];
28948     this.delegates = [];
28949     
28950     this.addEvents({
28951         /**
28952          * @event initial
28953          * Fire when initial the DocumentManager
28954          * @param {Roo.bootstrap.DocumentManager} this
28955          */
28956         "initial" : true,
28957         /**
28958          * @event inspect
28959          * inspect selected file
28960          * @param {Roo.bootstrap.DocumentManager} this
28961          * @param {File} file
28962          */
28963         "inspect" : true,
28964         /**
28965          * @event exception
28966          * Fire when xhr load exception
28967          * @param {Roo.bootstrap.DocumentManager} this
28968          * @param {XMLHttpRequest} xhr
28969          */
28970         "exception" : true,
28971         /**
28972          * @event afterupload
28973          * Fire when xhr load exception
28974          * @param {Roo.bootstrap.DocumentManager} this
28975          * @param {XMLHttpRequest} xhr
28976          */
28977         "afterupload" : true,
28978         /**
28979          * @event prepare
28980          * prepare the form data
28981          * @param {Roo.bootstrap.DocumentManager} this
28982          * @param {Object} formData
28983          */
28984         "prepare" : true,
28985         /**
28986          * @event remove
28987          * Fire when remove the file
28988          * @param {Roo.bootstrap.DocumentManager} this
28989          * @param {Object} file
28990          */
28991         "remove" : true,
28992         /**
28993          * @event refresh
28994          * Fire after refresh the file
28995          * @param {Roo.bootstrap.DocumentManager} this
28996          */
28997         "refresh" : true,
28998         /**
28999          * @event click
29000          * Fire after click the image
29001          * @param {Roo.bootstrap.DocumentManager} this
29002          * @param {Object} file
29003          */
29004         "click" : true,
29005         /**
29006          * @event edit
29007          * Fire when upload a image and editable set to true
29008          * @param {Roo.bootstrap.DocumentManager} this
29009          * @param {Object} file
29010          */
29011         "edit" : true,
29012         /**
29013          * @event beforeselectfile
29014          * Fire before select file
29015          * @param {Roo.bootstrap.DocumentManager} this
29016          */
29017         "beforeselectfile" : true,
29018         /**
29019          * @event process
29020          * Fire before process file
29021          * @param {Roo.bootstrap.DocumentManager} this
29022          * @param {Object} file
29023          */
29024         "process" : true,
29025         /**
29026          * @event previewrendered
29027          * Fire when preview rendered
29028          * @param {Roo.bootstrap.DocumentManager} this
29029          * @param {Object} file
29030          */
29031         "previewrendered" : true,
29032         /**
29033          */
29034         "previewResize" : true
29035         
29036     });
29037 };
29038
29039 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29040     
29041     boxes : 0,
29042     inputName : '',
29043     thumbSize : 300,
29044     multiple : true,
29045     files : false,
29046     method : 'POST',
29047     url : '',
29048     paramName : 'imageUpload',
29049     toolTipName : 'filename',
29050     fieldLabel : '',
29051     labelWidth : 4,
29052     labelAlign : 'left',
29053     editable : true,
29054     delegates : false,
29055     xhr : false, 
29056     
29057     labellg : 0,
29058     labelmd : 0,
29059     labelsm : 0,
29060     labelxs : 0,
29061     
29062     getAutoCreate : function()
29063     {   
29064         var managerWidget = {
29065             tag : 'div',
29066             cls : 'roo-document-manager',
29067             cn : [
29068                 {
29069                     tag : 'input',
29070                     cls : 'roo-document-manager-selector',
29071                     type : 'file'
29072                 },
29073                 {
29074                     tag : 'div',
29075                     cls : 'roo-document-manager-uploader',
29076                     cn : [
29077                         {
29078                             tag : 'div',
29079                             cls : 'roo-document-manager-upload-btn',
29080                             html : '<i class="fa fa-plus"></i>'
29081                         }
29082                     ]
29083                     
29084                 }
29085             ]
29086         };
29087         
29088         var content = [
29089             {
29090                 tag : 'div',
29091                 cls : 'column col-md-12',
29092                 cn : managerWidget
29093             }
29094         ];
29095         
29096         if(this.fieldLabel.length){
29097             
29098             content = [
29099                 {
29100                     tag : 'div',
29101                     cls : 'column col-md-12',
29102                     html : this.fieldLabel
29103                 },
29104                 {
29105                     tag : 'div',
29106                     cls : 'column col-md-12',
29107                     cn : managerWidget
29108                 }
29109             ];
29110
29111             if(this.labelAlign == 'left'){
29112                 content = [
29113                     {
29114                         tag : 'div',
29115                         cls : 'column',
29116                         html : this.fieldLabel
29117                     },
29118                     {
29119                         tag : 'div',
29120                         cls : 'column',
29121                         cn : managerWidget
29122                     }
29123                 ];
29124                 
29125                 if(this.labelWidth > 12){
29126                     content[0].style = "width: " + this.labelWidth + 'px';
29127                 }
29128
29129                 if(this.labelWidth < 13 && this.labelmd == 0){
29130                     this.labelmd = this.labelWidth;
29131                 }
29132
29133                 if(this.labellg > 0){
29134                     content[0].cls += ' col-lg-' + this.labellg;
29135                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29136                 }
29137
29138                 if(this.labelmd > 0){
29139                     content[0].cls += ' col-md-' + this.labelmd;
29140                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29141                 }
29142
29143                 if(this.labelsm > 0){
29144                     content[0].cls += ' col-sm-' + this.labelsm;
29145                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29146                 }
29147
29148                 if(this.labelxs > 0){
29149                     content[0].cls += ' col-xs-' + this.labelxs;
29150                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29151                 }
29152                 
29153             }
29154         }
29155         
29156         var cfg = {
29157             tag : 'div',
29158             cls : 'row clearfix',
29159             cn : content
29160         };
29161         
29162         return cfg;
29163         
29164     },
29165     
29166     initEvents : function()
29167     {
29168         this.managerEl = this.el.select('.roo-document-manager', true).first();
29169         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29170         
29171         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29172         this.selectorEl.hide();
29173         
29174         if(this.multiple){
29175             this.selectorEl.attr('multiple', 'multiple');
29176         }
29177         
29178         this.selectorEl.on('change', this.onFileSelected, this);
29179         
29180         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29181         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29182         
29183         this.uploader.on('click', this.onUploaderClick, this);
29184         
29185         this.renderProgressDialog();
29186         
29187         var _this = this;
29188         
29189         window.addEventListener("resize", function() { _this.refresh(); } );
29190         
29191         this.fireEvent('initial', this);
29192     },
29193     
29194     renderProgressDialog : function()
29195     {
29196         var _this = this;
29197         
29198         this.progressDialog = new Roo.bootstrap.Modal({
29199             cls : 'roo-document-manager-progress-dialog',
29200             allow_close : false,
29201             animate : false,
29202             title : '',
29203             buttons : [
29204                 {
29205                     name  :'cancel',
29206                     weight : 'danger',
29207                     html : 'Cancel'
29208                 }
29209             ], 
29210             listeners : { 
29211                 btnclick : function() {
29212                     _this.uploadCancel();
29213                     this.hide();
29214                 }
29215             }
29216         });
29217          
29218         this.progressDialog.render(Roo.get(document.body));
29219          
29220         this.progress = new Roo.bootstrap.Progress({
29221             cls : 'roo-document-manager-progress',
29222             active : true,
29223             striped : true
29224         });
29225         
29226         this.progress.render(this.progressDialog.getChildContainer());
29227         
29228         this.progressBar = new Roo.bootstrap.ProgressBar({
29229             cls : 'roo-document-manager-progress-bar',
29230             aria_valuenow : 0,
29231             aria_valuemin : 0,
29232             aria_valuemax : 12,
29233             panel : 'success'
29234         });
29235         
29236         this.progressBar.render(this.progress.getChildContainer());
29237     },
29238     
29239     onUploaderClick : function(e)
29240     {
29241         e.preventDefault();
29242      
29243         if(this.fireEvent('beforeselectfile', this) != false){
29244             this.selectorEl.dom.click();
29245         }
29246         
29247     },
29248     
29249     onFileSelected : function(e)
29250     {
29251         e.preventDefault();
29252         
29253         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29254             return;
29255         }
29256         
29257         Roo.each(this.selectorEl.dom.files, function(file){
29258             if(this.fireEvent('inspect', this, file) != false){
29259                 this.files.push(file);
29260             }
29261         }, this);
29262         
29263         this.queue();
29264         
29265     },
29266     
29267     queue : function()
29268     {
29269         this.selectorEl.dom.value = '';
29270         
29271         if(!this.files || !this.files.length){
29272             return;
29273         }
29274         
29275         if(this.boxes > 0 && this.files.length > this.boxes){
29276             this.files = this.files.slice(0, this.boxes);
29277         }
29278         
29279         this.uploader.show();
29280         
29281         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29282             this.uploader.hide();
29283         }
29284         
29285         var _this = this;
29286         
29287         var files = [];
29288         
29289         var docs = [];
29290         
29291         Roo.each(this.files, function(file){
29292             
29293             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29294                 var f = this.renderPreview(file);
29295                 files.push(f);
29296                 return;
29297             }
29298             
29299             if(file.type.indexOf('image') != -1){
29300                 this.delegates.push(
29301                     (function(){
29302                         _this.process(file);
29303                     }).createDelegate(this)
29304                 );
29305         
29306                 return;
29307             }
29308             
29309             docs.push(
29310                 (function(){
29311                     _this.process(file);
29312                 }).createDelegate(this)
29313             );
29314             
29315         }, this);
29316         
29317         this.files = files;
29318         
29319         this.delegates = this.delegates.concat(docs);
29320         
29321         if(!this.delegates.length){
29322             this.refresh();
29323             return;
29324         }
29325         
29326         this.progressBar.aria_valuemax = this.delegates.length;
29327         
29328         this.arrange();
29329         
29330         return;
29331     },
29332     
29333     arrange : function()
29334     {
29335         if(!this.delegates.length){
29336             this.progressDialog.hide();
29337             this.refresh();
29338             return;
29339         }
29340         
29341         var delegate = this.delegates.shift();
29342         
29343         this.progressDialog.show();
29344         
29345         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29346         
29347         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29348         
29349         delegate();
29350     },
29351     
29352     refresh : function()
29353     {
29354         this.uploader.show();
29355         
29356         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29357             this.uploader.hide();
29358         }
29359         
29360         Roo.isTouch ? this.closable(false) : this.closable(true);
29361         
29362         this.fireEvent('refresh', this);
29363     },
29364     
29365     onRemove : function(e, el, o)
29366     {
29367         e.preventDefault();
29368         
29369         this.fireEvent('remove', this, o);
29370         
29371     },
29372     
29373     remove : function(o)
29374     {
29375         var files = [];
29376         
29377         Roo.each(this.files, function(file){
29378             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29379                 files.push(file);
29380                 return;
29381             }
29382
29383             o.target.remove();
29384
29385         }, this);
29386         
29387         this.files = files;
29388         
29389         this.refresh();
29390     },
29391     
29392     clear : function()
29393     {
29394         Roo.each(this.files, function(file){
29395             if(!file.target){
29396                 return;
29397             }
29398             
29399             file.target.remove();
29400
29401         }, this);
29402         
29403         this.files = [];
29404         
29405         this.refresh();
29406     },
29407     
29408     onClick : function(e, el, o)
29409     {
29410         e.preventDefault();
29411         
29412         this.fireEvent('click', this, o);
29413         
29414     },
29415     
29416     closable : function(closable)
29417     {
29418         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29419             
29420             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29421             
29422             if(closable){
29423                 el.show();
29424                 return;
29425             }
29426             
29427             el.hide();
29428             
29429         }, this);
29430     },
29431     
29432     xhrOnLoad : function(xhr)
29433     {
29434         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29435             el.remove();
29436         }, this);
29437         
29438         if (xhr.readyState !== 4) {
29439             this.arrange();
29440             this.fireEvent('exception', this, xhr);
29441             return;
29442         }
29443
29444         var response = Roo.decode(xhr.responseText);
29445         
29446         if(!response.success){
29447             this.arrange();
29448             this.fireEvent('exception', this, xhr);
29449             return;
29450         }
29451         
29452         var file = this.renderPreview(response.data);
29453         
29454         this.files.push(file);
29455         
29456         this.arrange();
29457         
29458         this.fireEvent('afterupload', this, xhr);
29459         
29460     },
29461     
29462     xhrOnError : function(xhr)
29463     {
29464         Roo.log('xhr on error');
29465         
29466         var response = Roo.decode(xhr.responseText);
29467           
29468         Roo.log(response);
29469         
29470         this.arrange();
29471     },
29472     
29473     process : function(file)
29474     {
29475         if(this.fireEvent('process', this, file) !== false){
29476             if(this.editable && file.type.indexOf('image') != -1){
29477                 this.fireEvent('edit', this, file);
29478                 return;
29479             }
29480
29481             this.uploadStart(file, false);
29482
29483             return;
29484         }
29485         
29486     },
29487     
29488     uploadStart : function(file, crop)
29489     {
29490         this.xhr = new XMLHttpRequest();
29491         
29492         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29493             this.arrange();
29494             return;
29495         }
29496         
29497         file.xhr = this.xhr;
29498             
29499         this.managerEl.createChild({
29500             tag : 'div',
29501             cls : 'roo-document-manager-loading',
29502             cn : [
29503                 {
29504                     tag : 'div',
29505                     tooltip : file.name,
29506                     cls : 'roo-document-manager-thumb',
29507                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29508                 }
29509             ]
29510
29511         });
29512
29513         this.xhr.open(this.method, this.url, true);
29514         
29515         var headers = {
29516             "Accept": "application/json",
29517             "Cache-Control": "no-cache",
29518             "X-Requested-With": "XMLHttpRequest"
29519         };
29520         
29521         for (var headerName in headers) {
29522             var headerValue = headers[headerName];
29523             if (headerValue) {
29524                 this.xhr.setRequestHeader(headerName, headerValue);
29525             }
29526         }
29527         
29528         var _this = this;
29529         
29530         this.xhr.onload = function()
29531         {
29532             _this.xhrOnLoad(_this.xhr);
29533         }
29534         
29535         this.xhr.onerror = function()
29536         {
29537             _this.xhrOnError(_this.xhr);
29538         }
29539         
29540         var formData = new FormData();
29541
29542         formData.append('returnHTML', 'NO');
29543         
29544         if(crop){
29545             formData.append('crop', crop);
29546         }
29547         
29548         formData.append(this.paramName, file, file.name);
29549         
29550         var options = {
29551             file : file, 
29552             manually : false
29553         };
29554         
29555         if(this.fireEvent('prepare', this, formData, options) != false){
29556             
29557             if(options.manually){
29558                 return;
29559             }
29560             
29561             this.xhr.send(formData);
29562             return;
29563         };
29564         
29565         this.uploadCancel();
29566     },
29567     
29568     uploadCancel : function()
29569     {
29570         if (this.xhr) {
29571             this.xhr.abort();
29572         }
29573         
29574         this.delegates = [];
29575         
29576         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29577             el.remove();
29578         }, this);
29579         
29580         this.arrange();
29581     },
29582     
29583     renderPreview : function(file)
29584     {
29585         if(typeof(file.target) != 'undefined' && file.target){
29586             return file;
29587         }
29588         
29589         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29590         
29591         var previewEl = this.managerEl.createChild({
29592             tag : 'div',
29593             cls : 'roo-document-manager-preview',
29594             cn : [
29595                 {
29596                     tag : 'div',
29597                     tooltip : file[this.toolTipName],
29598                     cls : 'roo-document-manager-thumb',
29599                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29600                 },
29601                 {
29602                     tag : 'button',
29603                     cls : 'close',
29604                     html : '<i class="fa fa-times-circle"></i>'
29605                 }
29606             ]
29607         });
29608
29609         var close = previewEl.select('button.close', true).first();
29610
29611         close.on('click', this.onRemove, this, file);
29612
29613         file.target = previewEl;
29614
29615         var image = previewEl.select('img', true).first();
29616         
29617         var _this = this;
29618         
29619         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29620         
29621         image.on('click', this.onClick, this, file);
29622         
29623         this.fireEvent('previewrendered', this, file);
29624         
29625         return file;
29626         
29627     },
29628     
29629     onPreviewLoad : function(file, image)
29630     {
29631         if(typeof(file.target) == 'undefined' || !file.target){
29632             return;
29633         }
29634         
29635         var width = image.dom.naturalWidth || image.dom.width;
29636         var height = image.dom.naturalHeight || image.dom.height;
29637         
29638         if(!this.previewResize) {
29639             return;
29640         }
29641         
29642         if(width > height){
29643             file.target.addClass('wide');
29644             return;
29645         }
29646         
29647         file.target.addClass('tall');
29648         return;
29649         
29650     },
29651     
29652     uploadFromSource : function(file, crop)
29653     {
29654         this.xhr = new XMLHttpRequest();
29655         
29656         this.managerEl.createChild({
29657             tag : 'div',
29658             cls : 'roo-document-manager-loading',
29659             cn : [
29660                 {
29661                     tag : 'div',
29662                     tooltip : file.name,
29663                     cls : 'roo-document-manager-thumb',
29664                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29665                 }
29666             ]
29667
29668         });
29669
29670         this.xhr.open(this.method, this.url, true);
29671         
29672         var headers = {
29673             "Accept": "application/json",
29674             "Cache-Control": "no-cache",
29675             "X-Requested-With": "XMLHttpRequest"
29676         };
29677         
29678         for (var headerName in headers) {
29679             var headerValue = headers[headerName];
29680             if (headerValue) {
29681                 this.xhr.setRequestHeader(headerName, headerValue);
29682             }
29683         }
29684         
29685         var _this = this;
29686         
29687         this.xhr.onload = function()
29688         {
29689             _this.xhrOnLoad(_this.xhr);
29690         }
29691         
29692         this.xhr.onerror = function()
29693         {
29694             _this.xhrOnError(_this.xhr);
29695         }
29696         
29697         var formData = new FormData();
29698
29699         formData.append('returnHTML', 'NO');
29700         
29701         formData.append('crop', crop);
29702         
29703         if(typeof(file.filename) != 'undefined'){
29704             formData.append('filename', file.filename);
29705         }
29706         
29707         if(typeof(file.mimetype) != 'undefined'){
29708             formData.append('mimetype', file.mimetype);
29709         }
29710         
29711         Roo.log(formData);
29712         
29713         if(this.fireEvent('prepare', this, formData) != false){
29714             this.xhr.send(formData);
29715         };
29716     }
29717 });
29718
29719 /*
29720 * Licence: LGPL
29721 */
29722
29723 /**
29724  * @class Roo.bootstrap.DocumentViewer
29725  * @extends Roo.bootstrap.Component
29726  * Bootstrap DocumentViewer class
29727  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29728  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29729  * 
29730  * @constructor
29731  * Create a new DocumentViewer
29732  * @param {Object} config The config object
29733  */
29734
29735 Roo.bootstrap.DocumentViewer = function(config){
29736     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29737     
29738     this.addEvents({
29739         /**
29740          * @event initial
29741          * Fire after initEvent
29742          * @param {Roo.bootstrap.DocumentViewer} this
29743          */
29744         "initial" : true,
29745         /**
29746          * @event click
29747          * Fire after click
29748          * @param {Roo.bootstrap.DocumentViewer} this
29749          */
29750         "click" : true,
29751         /**
29752          * @event download
29753          * Fire after download button
29754          * @param {Roo.bootstrap.DocumentViewer} this
29755          */
29756         "download" : true,
29757         /**
29758          * @event trash
29759          * Fire after trash button
29760          * @param {Roo.bootstrap.DocumentViewer} this
29761          */
29762         "trash" : true
29763         
29764     });
29765 };
29766
29767 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29768     
29769     showDownload : true,
29770     
29771     showTrash : true,
29772     
29773     getAutoCreate : function()
29774     {
29775         var cfg = {
29776             tag : 'div',
29777             cls : 'roo-document-viewer',
29778             cn : [
29779                 {
29780                     tag : 'div',
29781                     cls : 'roo-document-viewer-body',
29782                     cn : [
29783                         {
29784                             tag : 'div',
29785                             cls : 'roo-document-viewer-thumb',
29786                             cn : [
29787                                 {
29788                                     tag : 'img',
29789                                     cls : 'roo-document-viewer-image'
29790                                 }
29791                             ]
29792                         }
29793                     ]
29794                 },
29795                 {
29796                     tag : 'div',
29797                     cls : 'roo-document-viewer-footer',
29798                     cn : {
29799                         tag : 'div',
29800                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29801                         cn : [
29802                             {
29803                                 tag : 'div',
29804                                 cls : 'btn-group roo-document-viewer-download',
29805                                 cn : [
29806                                     {
29807                                         tag : 'button',
29808                                         cls : 'btn btn-default',
29809                                         html : '<i class="fa fa-download"></i>'
29810                                     }
29811                                 ]
29812                             },
29813                             {
29814                                 tag : 'div',
29815                                 cls : 'btn-group roo-document-viewer-trash',
29816                                 cn : [
29817                                     {
29818                                         tag : 'button',
29819                                         cls : 'btn btn-default',
29820                                         html : '<i class="fa fa-trash"></i>'
29821                                     }
29822                                 ]
29823                             }
29824                         ]
29825                     }
29826                 }
29827             ]
29828         };
29829         
29830         return cfg;
29831     },
29832     
29833     initEvents : function()
29834     {
29835         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29836         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29837         
29838         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29839         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29840         
29841         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29842         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29843         
29844         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29845         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29846         
29847         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29848         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29849         
29850         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29851         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29852         
29853         this.bodyEl.on('click', this.onClick, this);
29854         this.downloadBtn.on('click', this.onDownload, this);
29855         this.trashBtn.on('click', this.onTrash, this);
29856         
29857         this.downloadBtn.hide();
29858         this.trashBtn.hide();
29859         
29860         if(this.showDownload){
29861             this.downloadBtn.show();
29862         }
29863         
29864         if(this.showTrash){
29865             this.trashBtn.show();
29866         }
29867         
29868         if(!this.showDownload && !this.showTrash) {
29869             this.footerEl.hide();
29870         }
29871         
29872     },
29873     
29874     initial : function()
29875     {
29876         this.fireEvent('initial', this);
29877         
29878     },
29879     
29880     onClick : function(e)
29881     {
29882         e.preventDefault();
29883         
29884         this.fireEvent('click', this);
29885     },
29886     
29887     onDownload : function(e)
29888     {
29889         e.preventDefault();
29890         
29891         this.fireEvent('download', this);
29892     },
29893     
29894     onTrash : function(e)
29895     {
29896         e.preventDefault();
29897         
29898         this.fireEvent('trash', this);
29899     }
29900     
29901 });
29902 /*
29903  * - LGPL
29904  *
29905  * nav progress bar
29906  * 
29907  */
29908
29909 /**
29910  * @class Roo.bootstrap.NavProgressBar
29911  * @extends Roo.bootstrap.Component
29912  * Bootstrap NavProgressBar class
29913  * 
29914  * @constructor
29915  * Create a new nav progress bar
29916  * @param {Object} config The config object
29917  */
29918
29919 Roo.bootstrap.NavProgressBar = function(config){
29920     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29921
29922     this.bullets = this.bullets || [];
29923    
29924 //    Roo.bootstrap.NavProgressBar.register(this);
29925      this.addEvents({
29926         /**
29927              * @event changed
29928              * Fires when the active item changes
29929              * @param {Roo.bootstrap.NavProgressBar} this
29930              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29931              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29932          */
29933         'changed': true
29934      });
29935     
29936 };
29937
29938 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29939     
29940     bullets : [],
29941     barItems : [],
29942     
29943     getAutoCreate : function()
29944     {
29945         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29946         
29947         cfg = {
29948             tag : 'div',
29949             cls : 'roo-navigation-bar-group',
29950             cn : [
29951                 {
29952                     tag : 'div',
29953                     cls : 'roo-navigation-top-bar'
29954                 },
29955                 {
29956                     tag : 'div',
29957                     cls : 'roo-navigation-bullets-bar',
29958                     cn : [
29959                         {
29960                             tag : 'ul',
29961                             cls : 'roo-navigation-bar'
29962                         }
29963                     ]
29964                 },
29965                 
29966                 {
29967                     tag : 'div',
29968                     cls : 'roo-navigation-bottom-bar'
29969                 }
29970             ]
29971             
29972         };
29973         
29974         return cfg;
29975         
29976     },
29977     
29978     initEvents: function() 
29979     {
29980         
29981     },
29982     
29983     onRender : function(ct, position) 
29984     {
29985         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29986         
29987         if(this.bullets.length){
29988             Roo.each(this.bullets, function(b){
29989                this.addItem(b);
29990             }, this);
29991         }
29992         
29993         this.format();
29994         
29995     },
29996     
29997     addItem : function(cfg)
29998     {
29999         var item = new Roo.bootstrap.NavProgressItem(cfg);
30000         
30001         item.parentId = this.id;
30002         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30003         
30004         if(cfg.html){
30005             var top = new Roo.bootstrap.Element({
30006                 tag : 'div',
30007                 cls : 'roo-navigation-bar-text'
30008             });
30009             
30010             var bottom = new Roo.bootstrap.Element({
30011                 tag : 'div',
30012                 cls : 'roo-navigation-bar-text'
30013             });
30014             
30015             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30016             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30017             
30018             var topText = new Roo.bootstrap.Element({
30019                 tag : 'span',
30020                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30021             });
30022             
30023             var bottomText = new Roo.bootstrap.Element({
30024                 tag : 'span',
30025                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30026             });
30027             
30028             topText.onRender(top.el, null);
30029             bottomText.onRender(bottom.el, null);
30030             
30031             item.topEl = top;
30032             item.bottomEl = bottom;
30033         }
30034         
30035         this.barItems.push(item);
30036         
30037         return item;
30038     },
30039     
30040     getActive : function()
30041     {
30042         var active = false;
30043         
30044         Roo.each(this.barItems, function(v){
30045             
30046             if (!v.isActive()) {
30047                 return;
30048             }
30049             
30050             active = v;
30051             return false;
30052             
30053         });
30054         
30055         return active;
30056     },
30057     
30058     setActiveItem : function(item)
30059     {
30060         var prev = false;
30061         
30062         Roo.each(this.barItems, function(v){
30063             if (v.rid == item.rid) {
30064                 return ;
30065             }
30066             
30067             if (v.isActive()) {
30068                 v.setActive(false);
30069                 prev = v;
30070             }
30071         });
30072
30073         item.setActive(true);
30074         
30075         this.fireEvent('changed', this, item, prev);
30076     },
30077     
30078     getBarItem: function(rid)
30079     {
30080         var ret = false;
30081         
30082         Roo.each(this.barItems, function(e) {
30083             if (e.rid != rid) {
30084                 return;
30085             }
30086             
30087             ret =  e;
30088             return false;
30089         });
30090         
30091         return ret;
30092     },
30093     
30094     indexOfItem : function(item)
30095     {
30096         var index = false;
30097         
30098         Roo.each(this.barItems, function(v, i){
30099             
30100             if (v.rid != item.rid) {
30101                 return;
30102             }
30103             
30104             index = i;
30105             return false
30106         });
30107         
30108         return index;
30109     },
30110     
30111     setActiveNext : function()
30112     {
30113         var i = this.indexOfItem(this.getActive());
30114         
30115         if (i > this.barItems.length) {
30116             return;
30117         }
30118         
30119         this.setActiveItem(this.barItems[i+1]);
30120     },
30121     
30122     setActivePrev : function()
30123     {
30124         var i = this.indexOfItem(this.getActive());
30125         
30126         if (i  < 1) {
30127             return;
30128         }
30129         
30130         this.setActiveItem(this.barItems[i-1]);
30131     },
30132     
30133     format : function()
30134     {
30135         if(!this.barItems.length){
30136             return;
30137         }
30138      
30139         var width = 100 / this.barItems.length;
30140         
30141         Roo.each(this.barItems, function(i){
30142             i.el.setStyle('width', width + '%');
30143             i.topEl.el.setStyle('width', width + '%');
30144             i.bottomEl.el.setStyle('width', width + '%');
30145         }, this);
30146         
30147     }
30148     
30149 });
30150 /*
30151  * - LGPL
30152  *
30153  * Nav Progress Item
30154  * 
30155  */
30156
30157 /**
30158  * @class Roo.bootstrap.NavProgressItem
30159  * @extends Roo.bootstrap.Component
30160  * Bootstrap NavProgressItem class
30161  * @cfg {String} rid the reference id
30162  * @cfg {Boolean} active (true|false) Is item active default false
30163  * @cfg {Boolean} disabled (true|false) Is item active default false
30164  * @cfg {String} html
30165  * @cfg {String} position (top|bottom) text position default bottom
30166  * @cfg {String} icon show icon instead of number
30167  * 
30168  * @constructor
30169  * Create a new NavProgressItem
30170  * @param {Object} config The config object
30171  */
30172 Roo.bootstrap.NavProgressItem = function(config){
30173     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30174     this.addEvents({
30175         // raw events
30176         /**
30177          * @event click
30178          * The raw click event for the entire grid.
30179          * @param {Roo.bootstrap.NavProgressItem} this
30180          * @param {Roo.EventObject} e
30181          */
30182         "click" : true
30183     });
30184    
30185 };
30186
30187 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30188     
30189     rid : '',
30190     active : false,
30191     disabled : false,
30192     html : '',
30193     position : 'bottom',
30194     icon : false,
30195     
30196     getAutoCreate : function()
30197     {
30198         var iconCls = 'roo-navigation-bar-item-icon';
30199         
30200         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30201         
30202         var cfg = {
30203             tag: 'li',
30204             cls: 'roo-navigation-bar-item',
30205             cn : [
30206                 {
30207                     tag : 'i',
30208                     cls : iconCls
30209                 }
30210             ]
30211         };
30212         
30213         if(this.active){
30214             cfg.cls += ' active';
30215         }
30216         if(this.disabled){
30217             cfg.cls += ' disabled';
30218         }
30219         
30220         return cfg;
30221     },
30222     
30223     disable : function()
30224     {
30225         this.setDisabled(true);
30226     },
30227     
30228     enable : function()
30229     {
30230         this.setDisabled(false);
30231     },
30232     
30233     initEvents: function() 
30234     {
30235         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30236         
30237         this.iconEl.on('click', this.onClick, this);
30238     },
30239     
30240     onClick : function(e)
30241     {
30242         e.preventDefault();
30243         
30244         if(this.disabled){
30245             return;
30246         }
30247         
30248         if(this.fireEvent('click', this, e) === false){
30249             return;
30250         };
30251         
30252         this.parent().setActiveItem(this);
30253     },
30254     
30255     isActive: function () 
30256     {
30257         return this.active;
30258     },
30259     
30260     setActive : function(state)
30261     {
30262         if(this.active == state){
30263             return;
30264         }
30265         
30266         this.active = state;
30267         
30268         if (state) {
30269             this.el.addClass('active');
30270             return;
30271         }
30272         
30273         this.el.removeClass('active');
30274         
30275         return;
30276     },
30277     
30278     setDisabled : function(state)
30279     {
30280         if(this.disabled == state){
30281             return;
30282         }
30283         
30284         this.disabled = state;
30285         
30286         if (state) {
30287             this.el.addClass('disabled');
30288             return;
30289         }
30290         
30291         this.el.removeClass('disabled');
30292     },
30293     
30294     tooltipEl : function()
30295     {
30296         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30297     }
30298 });
30299  
30300
30301  /*
30302  * - LGPL
30303  *
30304  * FieldLabel
30305  * 
30306  */
30307
30308 /**
30309  * @class Roo.bootstrap.FieldLabel
30310  * @extends Roo.bootstrap.Component
30311  * Bootstrap FieldLabel class
30312  * @cfg {String} html contents of the element
30313  * @cfg {String} tag tag of the element default label
30314  * @cfg {String} cls class of the element
30315  * @cfg {String} target label target 
30316  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30317  * @cfg {String} invalidClass default "text-warning"
30318  * @cfg {String} validClass default "text-success"
30319  * @cfg {String} iconTooltip default "This field is required"
30320  * @cfg {String} indicatorpos (left|right) default left
30321  * 
30322  * @constructor
30323  * Create a new FieldLabel
30324  * @param {Object} config The config object
30325  */
30326
30327 Roo.bootstrap.FieldLabel = function(config){
30328     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30329     
30330     this.addEvents({
30331             /**
30332              * @event invalid
30333              * Fires after the field has been marked as invalid.
30334              * @param {Roo.form.FieldLabel} this
30335              * @param {String} msg The validation message
30336              */
30337             invalid : true,
30338             /**
30339              * @event valid
30340              * Fires after the field has been validated with no errors.
30341              * @param {Roo.form.FieldLabel} this
30342              */
30343             valid : true
30344         });
30345 };
30346
30347 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30348     
30349     tag: 'label',
30350     cls: '',
30351     html: '',
30352     target: '',
30353     allowBlank : true,
30354     invalidClass : 'has-warning',
30355     validClass : 'has-success',
30356     iconTooltip : 'This field is required',
30357     indicatorpos : 'left',
30358     
30359     getAutoCreate : function(){
30360         
30361         var cls = "";
30362         if (!this.allowBlank) {
30363             cls  = "visible";
30364         }
30365         
30366         var cfg = {
30367             tag : this.tag,
30368             cls : 'roo-bootstrap-field-label ' + this.cls,
30369             for : this.target,
30370             cn : [
30371                 {
30372                     tag : 'i',
30373                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30374                     tooltip : this.iconTooltip
30375                 },
30376                 {
30377                     tag : 'span',
30378                     html : this.html
30379                 }
30380             ] 
30381         };
30382         
30383         if(this.indicatorpos == 'right'){
30384             var cfg = {
30385                 tag : this.tag,
30386                 cls : 'roo-bootstrap-field-label ' + this.cls,
30387                 for : this.target,
30388                 cn : [
30389                     {
30390                         tag : 'span',
30391                         html : this.html
30392                     },
30393                     {
30394                         tag : 'i',
30395                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30396                         tooltip : this.iconTooltip
30397                     }
30398                 ] 
30399             };
30400         }
30401         
30402         return cfg;
30403     },
30404     
30405     initEvents: function() 
30406     {
30407         Roo.bootstrap.Element.superclass.initEvents.call(this);
30408         
30409         this.indicator = this.indicatorEl();
30410         
30411         if(this.indicator){
30412             this.indicator.removeClass('visible');
30413             this.indicator.addClass('invisible');
30414         }
30415         
30416         Roo.bootstrap.FieldLabel.register(this);
30417     },
30418     
30419     indicatorEl : function()
30420     {
30421         var indicator = this.el.select('i.roo-required-indicator',true).first();
30422         
30423         if(!indicator){
30424             return false;
30425         }
30426         
30427         return indicator;
30428         
30429     },
30430     
30431     /**
30432      * Mark this field as valid
30433      */
30434     markValid : function()
30435     {
30436         if(this.indicator){
30437             this.indicator.removeClass('visible');
30438             this.indicator.addClass('invisible');
30439         }
30440         
30441         this.el.removeClass(this.invalidClass);
30442         
30443         this.el.addClass(this.validClass);
30444         
30445         this.fireEvent('valid', this);
30446     },
30447     
30448     /**
30449      * Mark this field as invalid
30450      * @param {String} msg The validation message
30451      */
30452     markInvalid : function(msg)
30453     {
30454         if(this.indicator){
30455             this.indicator.removeClass('invisible');
30456             this.indicator.addClass('visible');
30457         }
30458         
30459         this.el.removeClass(this.validClass);
30460         
30461         this.el.addClass(this.invalidClass);
30462         
30463         this.fireEvent('invalid', this, msg);
30464     }
30465     
30466    
30467 });
30468
30469 Roo.apply(Roo.bootstrap.FieldLabel, {
30470     
30471     groups: {},
30472     
30473      /**
30474     * register a FieldLabel Group
30475     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30476     */
30477     register : function(label)
30478     {
30479         if(this.groups.hasOwnProperty(label.target)){
30480             return;
30481         }
30482      
30483         this.groups[label.target] = label;
30484         
30485     },
30486     /**
30487     * fetch a FieldLabel Group based on the target
30488     * @param {string} target
30489     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30490     */
30491     get: function(target) {
30492         if (typeof(this.groups[target]) == 'undefined') {
30493             return false;
30494         }
30495         
30496         return this.groups[target] ;
30497     }
30498 });
30499
30500  
30501
30502  /*
30503  * - LGPL
30504  *
30505  * page DateSplitField.
30506  * 
30507  */
30508
30509
30510 /**
30511  * @class Roo.bootstrap.DateSplitField
30512  * @extends Roo.bootstrap.Component
30513  * Bootstrap DateSplitField class
30514  * @cfg {string} fieldLabel - the label associated
30515  * @cfg {Number} labelWidth set the width of label (0-12)
30516  * @cfg {String} labelAlign (top|left)
30517  * @cfg {Boolean} dayAllowBlank (true|false) default false
30518  * @cfg {Boolean} monthAllowBlank (true|false) default false
30519  * @cfg {Boolean} yearAllowBlank (true|false) default false
30520  * @cfg {string} dayPlaceholder 
30521  * @cfg {string} monthPlaceholder
30522  * @cfg {string} yearPlaceholder
30523  * @cfg {string} dayFormat default 'd'
30524  * @cfg {string} monthFormat default 'm'
30525  * @cfg {string} yearFormat default 'Y'
30526  * @cfg {Number} labellg set the width of label (1-12)
30527  * @cfg {Number} labelmd set the width of label (1-12)
30528  * @cfg {Number} labelsm set the width of label (1-12)
30529  * @cfg {Number} labelxs set the width of label (1-12)
30530
30531  *     
30532  * @constructor
30533  * Create a new DateSplitField
30534  * @param {Object} config The config object
30535  */
30536
30537 Roo.bootstrap.DateSplitField = function(config){
30538     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30539     
30540     this.addEvents({
30541         // raw events
30542          /**
30543          * @event years
30544          * getting the data of years
30545          * @param {Roo.bootstrap.DateSplitField} this
30546          * @param {Object} years
30547          */
30548         "years" : true,
30549         /**
30550          * @event days
30551          * getting the data of days
30552          * @param {Roo.bootstrap.DateSplitField} this
30553          * @param {Object} days
30554          */
30555         "days" : true,
30556         /**
30557          * @event invalid
30558          * Fires after the field has been marked as invalid.
30559          * @param {Roo.form.Field} this
30560          * @param {String} msg The validation message
30561          */
30562         invalid : true,
30563        /**
30564          * @event valid
30565          * Fires after the field has been validated with no errors.
30566          * @param {Roo.form.Field} this
30567          */
30568         valid : true
30569     });
30570 };
30571
30572 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30573     
30574     fieldLabel : '',
30575     labelAlign : 'top',
30576     labelWidth : 3,
30577     dayAllowBlank : false,
30578     monthAllowBlank : false,
30579     yearAllowBlank : false,
30580     dayPlaceholder : '',
30581     monthPlaceholder : '',
30582     yearPlaceholder : '',
30583     dayFormat : 'd',
30584     monthFormat : 'm',
30585     yearFormat : 'Y',
30586     isFormField : true,
30587     labellg : 0,
30588     labelmd : 0,
30589     labelsm : 0,
30590     labelxs : 0,
30591     
30592     getAutoCreate : function()
30593     {
30594         var cfg = {
30595             tag : 'div',
30596             cls : 'row roo-date-split-field-group',
30597             cn : [
30598                 {
30599                     tag : 'input',
30600                     type : 'hidden',
30601                     cls : 'form-hidden-field roo-date-split-field-group-value',
30602                     name : this.name
30603                 }
30604             ]
30605         };
30606         
30607         var labelCls = 'col-md-12';
30608         var contentCls = 'col-md-4';
30609         
30610         if(this.fieldLabel){
30611             
30612             var label = {
30613                 tag : 'div',
30614                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30615                 cn : [
30616                     {
30617                         tag : 'label',
30618                         html : this.fieldLabel
30619                     }
30620                 ]
30621             };
30622             
30623             if(this.labelAlign == 'left'){
30624             
30625                 if(this.labelWidth > 12){
30626                     label.style = "width: " + this.labelWidth + 'px';
30627                 }
30628
30629                 if(this.labelWidth < 13 && this.labelmd == 0){
30630                     this.labelmd = this.labelWidth;
30631                 }
30632
30633                 if(this.labellg > 0){
30634                     labelCls = ' col-lg-' + this.labellg;
30635                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30636                 }
30637
30638                 if(this.labelmd > 0){
30639                     labelCls = ' col-md-' + this.labelmd;
30640                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30641                 }
30642
30643                 if(this.labelsm > 0){
30644                     labelCls = ' col-sm-' + this.labelsm;
30645                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30646                 }
30647
30648                 if(this.labelxs > 0){
30649                     labelCls = ' col-xs-' + this.labelxs;
30650                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30651                 }
30652             }
30653             
30654             label.cls += ' ' + labelCls;
30655             
30656             cfg.cn.push(label);
30657         }
30658         
30659         Roo.each(['day', 'month', 'year'], function(t){
30660             cfg.cn.push({
30661                 tag : 'div',
30662                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30663             });
30664         }, this);
30665         
30666         return cfg;
30667     },
30668     
30669     inputEl: function ()
30670     {
30671         return this.el.select('.roo-date-split-field-group-value', true).first();
30672     },
30673     
30674     onRender : function(ct, position) 
30675     {
30676         var _this = this;
30677         
30678         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30679         
30680         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30681         
30682         this.dayField = new Roo.bootstrap.ComboBox({
30683             allowBlank : this.dayAllowBlank,
30684             alwaysQuery : true,
30685             displayField : 'value',
30686             editable : false,
30687             fieldLabel : '',
30688             forceSelection : true,
30689             mode : 'local',
30690             placeholder : this.dayPlaceholder,
30691             selectOnFocus : true,
30692             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30693             triggerAction : 'all',
30694             typeAhead : true,
30695             valueField : 'value',
30696             store : new Roo.data.SimpleStore({
30697                 data : (function() {    
30698                     var days = [];
30699                     _this.fireEvent('days', _this, days);
30700                     return days;
30701                 })(),
30702                 fields : [ 'value' ]
30703             }),
30704             listeners : {
30705                 select : function (_self, record, index)
30706                 {
30707                     _this.setValue(_this.getValue());
30708                 }
30709             }
30710         });
30711
30712         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30713         
30714         this.monthField = new Roo.bootstrap.MonthField({
30715             after : '<i class=\"fa fa-calendar\"></i>',
30716             allowBlank : this.monthAllowBlank,
30717             placeholder : this.monthPlaceholder,
30718             readOnly : true,
30719             listeners : {
30720                 render : function (_self)
30721                 {
30722                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30723                         e.preventDefault();
30724                         _self.focus();
30725                     });
30726                 },
30727                 select : function (_self, oldvalue, newvalue)
30728                 {
30729                     _this.setValue(_this.getValue());
30730                 }
30731             }
30732         });
30733         
30734         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30735         
30736         this.yearField = new Roo.bootstrap.ComboBox({
30737             allowBlank : this.yearAllowBlank,
30738             alwaysQuery : true,
30739             displayField : 'value',
30740             editable : false,
30741             fieldLabel : '',
30742             forceSelection : true,
30743             mode : 'local',
30744             placeholder : this.yearPlaceholder,
30745             selectOnFocus : true,
30746             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30747             triggerAction : 'all',
30748             typeAhead : true,
30749             valueField : 'value',
30750             store : new Roo.data.SimpleStore({
30751                 data : (function() {
30752                     var years = [];
30753                     _this.fireEvent('years', _this, years);
30754                     return years;
30755                 })(),
30756                 fields : [ 'value' ]
30757             }),
30758             listeners : {
30759                 select : function (_self, record, index)
30760                 {
30761                     _this.setValue(_this.getValue());
30762                 }
30763             }
30764         });
30765
30766         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30767     },
30768     
30769     setValue : function(v, format)
30770     {
30771         this.inputEl.dom.value = v;
30772         
30773         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30774         
30775         var d = Date.parseDate(v, f);
30776         
30777         if(!d){
30778             this.validate();
30779             return;
30780         }
30781         
30782         this.setDay(d.format(this.dayFormat));
30783         this.setMonth(d.format(this.monthFormat));
30784         this.setYear(d.format(this.yearFormat));
30785         
30786         this.validate();
30787         
30788         return;
30789     },
30790     
30791     setDay : function(v)
30792     {
30793         this.dayField.setValue(v);
30794         this.inputEl.dom.value = this.getValue();
30795         this.validate();
30796         return;
30797     },
30798     
30799     setMonth : function(v)
30800     {
30801         this.monthField.setValue(v, true);
30802         this.inputEl.dom.value = this.getValue();
30803         this.validate();
30804         return;
30805     },
30806     
30807     setYear : function(v)
30808     {
30809         this.yearField.setValue(v);
30810         this.inputEl.dom.value = this.getValue();
30811         this.validate();
30812         return;
30813     },
30814     
30815     getDay : function()
30816     {
30817         return this.dayField.getValue();
30818     },
30819     
30820     getMonth : function()
30821     {
30822         return this.monthField.getValue();
30823     },
30824     
30825     getYear : function()
30826     {
30827         return this.yearField.getValue();
30828     },
30829     
30830     getValue : function()
30831     {
30832         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30833         
30834         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30835         
30836         return date;
30837     },
30838     
30839     reset : function()
30840     {
30841         this.setDay('');
30842         this.setMonth('');
30843         this.setYear('');
30844         this.inputEl.dom.value = '';
30845         this.validate();
30846         return;
30847     },
30848     
30849     validate : function()
30850     {
30851         var d = this.dayField.validate();
30852         var m = this.monthField.validate();
30853         var y = this.yearField.validate();
30854         
30855         var valid = true;
30856         
30857         if(
30858                 (!this.dayAllowBlank && !d) ||
30859                 (!this.monthAllowBlank && !m) ||
30860                 (!this.yearAllowBlank && !y)
30861         ){
30862             valid = false;
30863         }
30864         
30865         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30866             return valid;
30867         }
30868         
30869         if(valid){
30870             this.markValid();
30871             return valid;
30872         }
30873         
30874         this.markInvalid();
30875         
30876         return valid;
30877     },
30878     
30879     markValid : function()
30880     {
30881         
30882         var label = this.el.select('label', true).first();
30883         var icon = this.el.select('i.fa-star', true).first();
30884
30885         if(label && icon){
30886             icon.remove();
30887         }
30888         
30889         this.fireEvent('valid', this);
30890     },
30891     
30892      /**
30893      * Mark this field as invalid
30894      * @param {String} msg The validation message
30895      */
30896     markInvalid : function(msg)
30897     {
30898         
30899         var label = this.el.select('label', true).first();
30900         var icon = this.el.select('i.fa-star', true).first();
30901
30902         if(label && !icon){
30903             this.el.select('.roo-date-split-field-label', true).createChild({
30904                 tag : 'i',
30905                 cls : 'text-danger fa fa-lg fa-star',
30906                 tooltip : 'This field is required',
30907                 style : 'margin-right:5px;'
30908             }, label, true);
30909         }
30910         
30911         this.fireEvent('invalid', this, msg);
30912     },
30913     
30914     clearInvalid : function()
30915     {
30916         var label = this.el.select('label', true).first();
30917         var icon = this.el.select('i.fa-star', true).first();
30918
30919         if(label && icon){
30920             icon.remove();
30921         }
30922         
30923         this.fireEvent('valid', this);
30924     },
30925     
30926     getName: function()
30927     {
30928         return this.name;
30929     }
30930     
30931 });
30932
30933  /**
30934  *
30935  * This is based on 
30936  * http://masonry.desandro.com
30937  *
30938  * The idea is to render all the bricks based on vertical width...
30939  *
30940  * The original code extends 'outlayer' - we might need to use that....
30941  * 
30942  */
30943
30944
30945 /**
30946  * @class Roo.bootstrap.LayoutMasonry
30947  * @extends Roo.bootstrap.Component
30948  * Bootstrap Layout Masonry class
30949  * 
30950  * @constructor
30951  * Create a new Element
30952  * @param {Object} config The config object
30953  */
30954
30955 Roo.bootstrap.LayoutMasonry = function(config){
30956     
30957     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30958     
30959     this.bricks = [];
30960     
30961     Roo.bootstrap.LayoutMasonry.register(this);
30962     
30963     this.addEvents({
30964         // raw events
30965         /**
30966          * @event layout
30967          * Fire after layout the items
30968          * @param {Roo.bootstrap.LayoutMasonry} this
30969          * @param {Roo.EventObject} e
30970          */
30971         "layout" : true
30972     });
30973     
30974 };
30975
30976 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30977     
30978     /**
30979      * @cfg {Boolean} isLayoutInstant = no animation?
30980      */   
30981     isLayoutInstant : false, // needed?
30982    
30983     /**
30984      * @cfg {Number} boxWidth  width of the columns
30985      */   
30986     boxWidth : 450,
30987     
30988       /**
30989      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30990      */   
30991     boxHeight : 0,
30992     
30993     /**
30994      * @cfg {Number} padWidth padding below box..
30995      */   
30996     padWidth : 10, 
30997     
30998     /**
30999      * @cfg {Number} gutter gutter width..
31000      */   
31001     gutter : 10,
31002     
31003      /**
31004      * @cfg {Number} maxCols maximum number of columns
31005      */   
31006     
31007     maxCols: 0,
31008     
31009     /**
31010      * @cfg {Boolean} isAutoInitial defalut true
31011      */   
31012     isAutoInitial : true, 
31013     
31014     containerWidth: 0,
31015     
31016     /**
31017      * @cfg {Boolean} isHorizontal defalut false
31018      */   
31019     isHorizontal : false, 
31020
31021     currentSize : null,
31022     
31023     tag: 'div',
31024     
31025     cls: '',
31026     
31027     bricks: null, //CompositeElement
31028     
31029     cols : 1,
31030     
31031     _isLayoutInited : false,
31032     
31033 //    isAlternative : false, // only use for vertical layout...
31034     
31035     /**
31036      * @cfg {Number} alternativePadWidth padding below box..
31037      */   
31038     alternativePadWidth : 50,
31039     
31040     selectedBrick : [],
31041     
31042     getAutoCreate : function(){
31043         
31044         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31045         
31046         var cfg = {
31047             tag: this.tag,
31048             cls: 'blog-masonary-wrapper ' + this.cls,
31049             cn : {
31050                 cls : 'mas-boxes masonary'
31051             }
31052         };
31053         
31054         return cfg;
31055     },
31056     
31057     getChildContainer: function( )
31058     {
31059         if (this.boxesEl) {
31060             return this.boxesEl;
31061         }
31062         
31063         this.boxesEl = this.el.select('.mas-boxes').first();
31064         
31065         return this.boxesEl;
31066     },
31067     
31068     
31069     initEvents : function()
31070     {
31071         var _this = this;
31072         
31073         if(this.isAutoInitial){
31074             Roo.log('hook children rendered');
31075             this.on('childrenrendered', function() {
31076                 Roo.log('children rendered');
31077                 _this.initial();
31078             } ,this);
31079         }
31080     },
31081     
31082     initial : function()
31083     {
31084         this.selectedBrick = [];
31085         
31086         this.currentSize = this.el.getBox(true);
31087         
31088         Roo.EventManager.onWindowResize(this.resize, this); 
31089
31090         if(!this.isAutoInitial){
31091             this.layout();
31092             return;
31093         }
31094         
31095         this.layout();
31096         
31097         return;
31098         //this.layout.defer(500,this);
31099         
31100     },
31101     
31102     resize : function()
31103     {
31104         var cs = this.el.getBox(true);
31105         
31106         if (
31107                 this.currentSize.width == cs.width && 
31108                 this.currentSize.x == cs.x && 
31109                 this.currentSize.height == cs.height && 
31110                 this.currentSize.y == cs.y 
31111         ) {
31112             Roo.log("no change in with or X or Y");
31113             return;
31114         }
31115         
31116         this.currentSize = cs;
31117         
31118         this.layout();
31119         
31120     },
31121     
31122     layout : function()
31123     {   
31124         this._resetLayout();
31125         
31126         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31127         
31128         this.layoutItems( isInstant );
31129       
31130         this._isLayoutInited = true;
31131         
31132         this.fireEvent('layout', this);
31133         
31134     },
31135     
31136     _resetLayout : function()
31137     {
31138         if(this.isHorizontal){
31139             this.horizontalMeasureColumns();
31140             return;
31141         }
31142         
31143         this.verticalMeasureColumns();
31144         
31145     },
31146     
31147     verticalMeasureColumns : function()
31148     {
31149         this.getContainerWidth();
31150         
31151 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31152 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31153 //            return;
31154 //        }
31155         
31156         var boxWidth = this.boxWidth + this.padWidth;
31157         
31158         if(this.containerWidth < this.boxWidth){
31159             boxWidth = this.containerWidth
31160         }
31161         
31162         var containerWidth = this.containerWidth;
31163         
31164         var cols = Math.floor(containerWidth / boxWidth);
31165         
31166         this.cols = Math.max( cols, 1 );
31167         
31168         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31169         
31170         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31171         
31172         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31173         
31174         this.colWidth = boxWidth + avail - this.padWidth;
31175         
31176         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31177         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31178     },
31179     
31180     horizontalMeasureColumns : function()
31181     {
31182         this.getContainerWidth();
31183         
31184         var boxWidth = this.boxWidth;
31185         
31186         if(this.containerWidth < boxWidth){
31187             boxWidth = this.containerWidth;
31188         }
31189         
31190         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31191         
31192         this.el.setHeight(boxWidth);
31193         
31194     },
31195     
31196     getContainerWidth : function()
31197     {
31198         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31199     },
31200     
31201     layoutItems : function( isInstant )
31202     {
31203         Roo.log(this.bricks);
31204         
31205         var items = Roo.apply([], this.bricks);
31206         
31207         if(this.isHorizontal){
31208             this._horizontalLayoutItems( items , isInstant );
31209             return;
31210         }
31211         
31212 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31213 //            this._verticalAlternativeLayoutItems( items , isInstant );
31214 //            return;
31215 //        }
31216         
31217         this._verticalLayoutItems( items , isInstant );
31218         
31219     },
31220     
31221     _verticalLayoutItems : function ( items , isInstant)
31222     {
31223         if ( !items || !items.length ) {
31224             return;
31225         }
31226         
31227         var standard = [
31228             ['xs', 'xs', 'xs', 'tall'],
31229             ['xs', 'xs', 'tall'],
31230             ['xs', 'xs', 'sm'],
31231             ['xs', 'xs', 'xs'],
31232             ['xs', 'tall'],
31233             ['xs', 'sm'],
31234             ['xs', 'xs'],
31235             ['xs'],
31236             
31237             ['sm', 'xs', 'xs'],
31238             ['sm', 'xs'],
31239             ['sm'],
31240             
31241             ['tall', 'xs', 'xs', 'xs'],
31242             ['tall', 'xs', 'xs'],
31243             ['tall', 'xs'],
31244             ['tall']
31245             
31246         ];
31247         
31248         var queue = [];
31249         
31250         var boxes = [];
31251         
31252         var box = [];
31253         
31254         Roo.each(items, function(item, k){
31255             
31256             switch (item.size) {
31257                 // these layouts take up a full box,
31258                 case 'md' :
31259                 case 'md-left' :
31260                 case 'md-right' :
31261                 case 'wide' :
31262                     
31263                     if(box.length){
31264                         boxes.push(box);
31265                         box = [];
31266                     }
31267                     
31268                     boxes.push([item]);
31269                     
31270                     break;
31271                     
31272                 case 'xs' :
31273                 case 'sm' :
31274                 case 'tall' :
31275                     
31276                     box.push(item);
31277                     
31278                     break;
31279                 default :
31280                     break;
31281                     
31282             }
31283             
31284         }, this);
31285         
31286         if(box.length){
31287             boxes.push(box);
31288             box = [];
31289         }
31290         
31291         var filterPattern = function(box, length)
31292         {
31293             if(!box.length){
31294                 return;
31295             }
31296             
31297             var match = false;
31298             
31299             var pattern = box.slice(0, length);
31300             
31301             var format = [];
31302             
31303             Roo.each(pattern, function(i){
31304                 format.push(i.size);
31305             }, this);
31306             
31307             Roo.each(standard, function(s){
31308                 
31309                 if(String(s) != String(format)){
31310                     return;
31311                 }
31312                 
31313                 match = true;
31314                 return false;
31315                 
31316             }, this);
31317             
31318             if(!match && length == 1){
31319                 return;
31320             }
31321             
31322             if(!match){
31323                 filterPattern(box, length - 1);
31324                 return;
31325             }
31326                 
31327             queue.push(pattern);
31328
31329             box = box.slice(length, box.length);
31330
31331             filterPattern(box, 4);
31332
31333             return;
31334             
31335         }
31336         
31337         Roo.each(boxes, function(box, k){
31338             
31339             if(!box.length){
31340                 return;
31341             }
31342             
31343             if(box.length == 1){
31344                 queue.push(box);
31345                 return;
31346             }
31347             
31348             filterPattern(box, 4);
31349             
31350         }, this);
31351         
31352         this._processVerticalLayoutQueue( queue, isInstant );
31353         
31354     },
31355     
31356 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31357 //    {
31358 //        if ( !items || !items.length ) {
31359 //            return;
31360 //        }
31361 //
31362 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31363 //        
31364 //    },
31365     
31366     _horizontalLayoutItems : function ( items , isInstant)
31367     {
31368         if ( !items || !items.length || items.length < 3) {
31369             return;
31370         }
31371         
31372         items.reverse();
31373         
31374         var eItems = items.slice(0, 3);
31375         
31376         items = items.slice(3, items.length);
31377         
31378         var standard = [
31379             ['xs', 'xs', 'xs', 'wide'],
31380             ['xs', 'xs', 'wide'],
31381             ['xs', 'xs', 'sm'],
31382             ['xs', 'xs', 'xs'],
31383             ['xs', 'wide'],
31384             ['xs', 'sm'],
31385             ['xs', 'xs'],
31386             ['xs'],
31387             
31388             ['sm', 'xs', 'xs'],
31389             ['sm', 'xs'],
31390             ['sm'],
31391             
31392             ['wide', 'xs', 'xs', 'xs'],
31393             ['wide', 'xs', 'xs'],
31394             ['wide', 'xs'],
31395             ['wide'],
31396             
31397             ['wide-thin']
31398         ];
31399         
31400         var queue = [];
31401         
31402         var boxes = [];
31403         
31404         var box = [];
31405         
31406         Roo.each(items, function(item, k){
31407             
31408             switch (item.size) {
31409                 case 'md' :
31410                 case 'md-left' :
31411                 case 'md-right' :
31412                 case 'tall' :
31413                     
31414                     if(box.length){
31415                         boxes.push(box);
31416                         box = [];
31417                     }
31418                     
31419                     boxes.push([item]);
31420                     
31421                     break;
31422                     
31423                 case 'xs' :
31424                 case 'sm' :
31425                 case 'wide' :
31426                 case 'wide-thin' :
31427                     
31428                     box.push(item);
31429                     
31430                     break;
31431                 default :
31432                     break;
31433                     
31434             }
31435             
31436         }, this);
31437         
31438         if(box.length){
31439             boxes.push(box);
31440             box = [];
31441         }
31442         
31443         var filterPattern = function(box, length)
31444         {
31445             if(!box.length){
31446                 return;
31447             }
31448             
31449             var match = false;
31450             
31451             var pattern = box.slice(0, length);
31452             
31453             var format = [];
31454             
31455             Roo.each(pattern, function(i){
31456                 format.push(i.size);
31457             }, this);
31458             
31459             Roo.each(standard, function(s){
31460                 
31461                 if(String(s) != String(format)){
31462                     return;
31463                 }
31464                 
31465                 match = true;
31466                 return false;
31467                 
31468             }, this);
31469             
31470             if(!match && length == 1){
31471                 return;
31472             }
31473             
31474             if(!match){
31475                 filterPattern(box, length - 1);
31476                 return;
31477             }
31478                 
31479             queue.push(pattern);
31480
31481             box = box.slice(length, box.length);
31482
31483             filterPattern(box, 4);
31484
31485             return;
31486             
31487         }
31488         
31489         Roo.each(boxes, function(box, k){
31490             
31491             if(!box.length){
31492                 return;
31493             }
31494             
31495             if(box.length == 1){
31496                 queue.push(box);
31497                 return;
31498             }
31499             
31500             filterPattern(box, 4);
31501             
31502         }, this);
31503         
31504         
31505         var prune = [];
31506         
31507         var pos = this.el.getBox(true);
31508         
31509         var minX = pos.x;
31510         
31511         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31512         
31513         var hit_end = false;
31514         
31515         Roo.each(queue, function(box){
31516             
31517             if(hit_end){
31518                 
31519                 Roo.each(box, function(b){
31520                 
31521                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31522                     b.el.hide();
31523
31524                 }, this);
31525
31526                 return;
31527             }
31528             
31529             var mx = 0;
31530             
31531             Roo.each(box, function(b){
31532                 
31533                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31534                 b.el.show();
31535
31536                 mx = Math.max(mx, b.x);
31537                 
31538             }, this);
31539             
31540             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31541             
31542             if(maxX < minX){
31543                 
31544                 Roo.each(box, function(b){
31545                 
31546                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31547                     b.el.hide();
31548                     
31549                 }, this);
31550                 
31551                 hit_end = true;
31552                 
31553                 return;
31554             }
31555             
31556             prune.push(box);
31557             
31558         }, this);
31559         
31560         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31561     },
31562     
31563     /** Sets position of item in DOM
31564     * @param {Element} item
31565     * @param {Number} x - horizontal position
31566     * @param {Number} y - vertical position
31567     * @param {Boolean} isInstant - disables transitions
31568     */
31569     _processVerticalLayoutQueue : function( queue, isInstant )
31570     {
31571         var pos = this.el.getBox(true);
31572         var x = pos.x;
31573         var y = pos.y;
31574         var maxY = [];
31575         
31576         for (var i = 0; i < this.cols; i++){
31577             maxY[i] = pos.y;
31578         }
31579         
31580         Roo.each(queue, function(box, k){
31581             
31582             var col = k % this.cols;
31583             
31584             Roo.each(box, function(b,kk){
31585                 
31586                 b.el.position('absolute');
31587                 
31588                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31589                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31590                 
31591                 if(b.size == 'md-left' || b.size == 'md-right'){
31592                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31593                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31594                 }
31595                 
31596                 b.el.setWidth(width);
31597                 b.el.setHeight(height);
31598                 // iframe?
31599                 b.el.select('iframe',true).setSize(width,height);
31600                 
31601             }, this);
31602             
31603             for (var i = 0; i < this.cols; i++){
31604                 
31605                 if(maxY[i] < maxY[col]){
31606                     col = i;
31607                     continue;
31608                 }
31609                 
31610                 col = Math.min(col, i);
31611                 
31612             }
31613             
31614             x = pos.x + col * (this.colWidth + this.padWidth);
31615             
31616             y = maxY[col];
31617             
31618             var positions = [];
31619             
31620             switch (box.length){
31621                 case 1 :
31622                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31623                     break;
31624                 case 2 :
31625                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31626                     break;
31627                 case 3 :
31628                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31629                     break;
31630                 case 4 :
31631                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31632                     break;
31633                 default :
31634                     break;
31635             }
31636             
31637             Roo.each(box, function(b,kk){
31638                 
31639                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31640                 
31641                 var sz = b.el.getSize();
31642                 
31643                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31644                 
31645             }, this);
31646             
31647         }, this);
31648         
31649         var mY = 0;
31650         
31651         for (var i = 0; i < this.cols; i++){
31652             mY = Math.max(mY, maxY[i]);
31653         }
31654         
31655         this.el.setHeight(mY - pos.y);
31656         
31657     },
31658     
31659 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31660 //    {
31661 //        var pos = this.el.getBox(true);
31662 //        var x = pos.x;
31663 //        var y = pos.y;
31664 //        var maxX = pos.right;
31665 //        
31666 //        var maxHeight = 0;
31667 //        
31668 //        Roo.each(items, function(item, k){
31669 //            
31670 //            var c = k % 2;
31671 //            
31672 //            item.el.position('absolute');
31673 //                
31674 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31675 //
31676 //            item.el.setWidth(width);
31677 //
31678 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31679 //
31680 //            item.el.setHeight(height);
31681 //            
31682 //            if(c == 0){
31683 //                item.el.setXY([x, y], isInstant ? false : true);
31684 //            } else {
31685 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31686 //            }
31687 //            
31688 //            y = y + height + this.alternativePadWidth;
31689 //            
31690 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31691 //            
31692 //        }, this);
31693 //        
31694 //        this.el.setHeight(maxHeight);
31695 //        
31696 //    },
31697     
31698     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31699     {
31700         var pos = this.el.getBox(true);
31701         
31702         var minX = pos.x;
31703         var minY = pos.y;
31704         
31705         var maxX = pos.right;
31706         
31707         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31708         
31709         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31710         
31711         Roo.each(queue, function(box, k){
31712             
31713             Roo.each(box, function(b, kk){
31714                 
31715                 b.el.position('absolute');
31716                 
31717                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31718                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31719                 
31720                 if(b.size == 'md-left' || b.size == 'md-right'){
31721                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31722                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31723                 }
31724                 
31725                 b.el.setWidth(width);
31726                 b.el.setHeight(height);
31727                 
31728             }, this);
31729             
31730             if(!box.length){
31731                 return;
31732             }
31733             
31734             var positions = [];
31735             
31736             switch (box.length){
31737                 case 1 :
31738                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31739                     break;
31740                 case 2 :
31741                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31742                     break;
31743                 case 3 :
31744                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31745                     break;
31746                 case 4 :
31747                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31748                     break;
31749                 default :
31750                     break;
31751             }
31752             
31753             Roo.each(box, function(b,kk){
31754                 
31755                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31756                 
31757                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31758                 
31759             }, this);
31760             
31761         }, this);
31762         
31763     },
31764     
31765     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31766     {
31767         Roo.each(eItems, function(b,k){
31768             
31769             b.size = (k == 0) ? 'sm' : 'xs';
31770             b.x = (k == 0) ? 2 : 1;
31771             b.y = (k == 0) ? 2 : 1;
31772             
31773             b.el.position('absolute');
31774             
31775             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31776                 
31777             b.el.setWidth(width);
31778             
31779             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31780             
31781             b.el.setHeight(height);
31782             
31783         }, this);
31784
31785         var positions = [];
31786         
31787         positions.push({
31788             x : maxX - this.unitWidth * 2 - this.gutter,
31789             y : minY
31790         });
31791         
31792         positions.push({
31793             x : maxX - this.unitWidth,
31794             y : minY + (this.unitWidth + this.gutter) * 2
31795         });
31796         
31797         positions.push({
31798             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31799             y : minY
31800         });
31801         
31802         Roo.each(eItems, function(b,k){
31803             
31804             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31805
31806         }, this);
31807         
31808     },
31809     
31810     getVerticalOneBoxColPositions : function(x, y, box)
31811     {
31812         var pos = [];
31813         
31814         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31815         
31816         if(box[0].size == 'md-left'){
31817             rand = 0;
31818         }
31819         
31820         if(box[0].size == 'md-right'){
31821             rand = 1;
31822         }
31823         
31824         pos.push({
31825             x : x + (this.unitWidth + this.gutter) * rand,
31826             y : y
31827         });
31828         
31829         return pos;
31830     },
31831     
31832     getVerticalTwoBoxColPositions : function(x, y, box)
31833     {
31834         var pos = [];
31835         
31836         if(box[0].size == 'xs'){
31837             
31838             pos.push({
31839                 x : x,
31840                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31841             });
31842
31843             pos.push({
31844                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31845                 y : y
31846             });
31847             
31848             return pos;
31849             
31850         }
31851         
31852         pos.push({
31853             x : x,
31854             y : y
31855         });
31856
31857         pos.push({
31858             x : x + (this.unitWidth + this.gutter) * 2,
31859             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31860         });
31861         
31862         return pos;
31863         
31864     },
31865     
31866     getVerticalThreeBoxColPositions : function(x, y, box)
31867     {
31868         var pos = [];
31869         
31870         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31871             
31872             pos.push({
31873                 x : x,
31874                 y : y
31875             });
31876
31877             pos.push({
31878                 x : x + (this.unitWidth + this.gutter) * 1,
31879                 y : y
31880             });
31881             
31882             pos.push({
31883                 x : x + (this.unitWidth + this.gutter) * 2,
31884                 y : y
31885             });
31886             
31887             return pos;
31888             
31889         }
31890         
31891         if(box[0].size == 'xs' && box[1].size == 'xs'){
31892             
31893             pos.push({
31894                 x : x,
31895                 y : y
31896             });
31897
31898             pos.push({
31899                 x : x,
31900                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31901             });
31902             
31903             pos.push({
31904                 x : x + (this.unitWidth + this.gutter) * 1,
31905                 y : y
31906             });
31907             
31908             return pos;
31909             
31910         }
31911         
31912         pos.push({
31913             x : x,
31914             y : y
31915         });
31916
31917         pos.push({
31918             x : x + (this.unitWidth + this.gutter) * 2,
31919             y : y
31920         });
31921
31922         pos.push({
31923             x : x + (this.unitWidth + this.gutter) * 2,
31924             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31925         });
31926             
31927         return pos;
31928         
31929     },
31930     
31931     getVerticalFourBoxColPositions : function(x, y, box)
31932     {
31933         var pos = [];
31934         
31935         if(box[0].size == 'xs'){
31936             
31937             pos.push({
31938                 x : x,
31939                 y : y
31940             });
31941
31942             pos.push({
31943                 x : x,
31944                 y : y + (this.unitHeight + this.gutter) * 1
31945             });
31946             
31947             pos.push({
31948                 x : x,
31949                 y : y + (this.unitHeight + this.gutter) * 2
31950             });
31951             
31952             pos.push({
31953                 x : x + (this.unitWidth + this.gutter) * 1,
31954                 y : y
31955             });
31956             
31957             return pos;
31958             
31959         }
31960         
31961         pos.push({
31962             x : x,
31963             y : y
31964         });
31965
31966         pos.push({
31967             x : x + (this.unitWidth + this.gutter) * 2,
31968             y : y
31969         });
31970
31971         pos.push({
31972             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31973             y : y + (this.unitHeight + this.gutter) * 1
31974         });
31975
31976         pos.push({
31977             x : x + (this.unitWidth + this.gutter) * 2,
31978             y : y + (this.unitWidth + this.gutter) * 2
31979         });
31980
31981         return pos;
31982         
31983     },
31984     
31985     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31986     {
31987         var pos = [];
31988         
31989         if(box[0].size == 'md-left'){
31990             pos.push({
31991                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31992                 y : minY
31993             });
31994             
31995             return pos;
31996         }
31997         
31998         if(box[0].size == 'md-right'){
31999             pos.push({
32000                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32001                 y : minY + (this.unitWidth + this.gutter) * 1
32002             });
32003             
32004             return pos;
32005         }
32006         
32007         var rand = Math.floor(Math.random() * (4 - box[0].y));
32008         
32009         pos.push({
32010             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32011             y : minY + (this.unitWidth + this.gutter) * rand
32012         });
32013         
32014         return pos;
32015         
32016     },
32017     
32018     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32019     {
32020         var pos = [];
32021         
32022         if(box[0].size == 'xs'){
32023             
32024             pos.push({
32025                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32026                 y : minY
32027             });
32028
32029             pos.push({
32030                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32031                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32032             });
32033             
32034             return pos;
32035             
32036         }
32037         
32038         pos.push({
32039             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32040             y : minY
32041         });
32042
32043         pos.push({
32044             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32045             y : minY + (this.unitWidth + this.gutter) * 2
32046         });
32047         
32048         return pos;
32049         
32050     },
32051     
32052     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32053     {
32054         var pos = [];
32055         
32056         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32057             
32058             pos.push({
32059                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32060                 y : minY
32061             });
32062
32063             pos.push({
32064                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32065                 y : minY + (this.unitWidth + this.gutter) * 1
32066             });
32067             
32068             pos.push({
32069                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32070                 y : minY + (this.unitWidth + this.gutter) * 2
32071             });
32072             
32073             return pos;
32074             
32075         }
32076         
32077         if(box[0].size == 'xs' && box[1].size == 'xs'){
32078             
32079             pos.push({
32080                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32081                 y : minY
32082             });
32083
32084             pos.push({
32085                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32086                 y : minY
32087             });
32088             
32089             pos.push({
32090                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32091                 y : minY + (this.unitWidth + this.gutter) * 1
32092             });
32093             
32094             return pos;
32095             
32096         }
32097         
32098         pos.push({
32099             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32100             y : minY
32101         });
32102
32103         pos.push({
32104             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32105             y : minY + (this.unitWidth + this.gutter) * 2
32106         });
32107
32108         pos.push({
32109             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32110             y : minY + (this.unitWidth + this.gutter) * 2
32111         });
32112             
32113         return pos;
32114         
32115     },
32116     
32117     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32118     {
32119         var pos = [];
32120         
32121         if(box[0].size == 'xs'){
32122             
32123             pos.push({
32124                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32125                 y : minY
32126             });
32127
32128             pos.push({
32129                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32130                 y : minY
32131             });
32132             
32133             pos.push({
32134                 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),
32135                 y : minY
32136             });
32137             
32138             pos.push({
32139                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32140                 y : minY + (this.unitWidth + this.gutter) * 1
32141             });
32142             
32143             return pos;
32144             
32145         }
32146         
32147         pos.push({
32148             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32149             y : minY
32150         });
32151         
32152         pos.push({
32153             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32154             y : minY + (this.unitWidth + this.gutter) * 2
32155         });
32156         
32157         pos.push({
32158             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32159             y : minY + (this.unitWidth + this.gutter) * 2
32160         });
32161         
32162         pos.push({
32163             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),
32164             y : minY + (this.unitWidth + this.gutter) * 2
32165         });
32166
32167         return pos;
32168         
32169     },
32170     
32171     /**
32172     * remove a Masonry Brick
32173     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32174     */
32175     removeBrick : function(brick_id)
32176     {
32177         if (!brick_id) {
32178             return;
32179         }
32180         
32181         for (var i = 0; i<this.bricks.length; i++) {
32182             if (this.bricks[i].id == brick_id) {
32183                 this.bricks.splice(i,1);
32184                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32185                 this.initial();
32186             }
32187         }
32188     },
32189     
32190     /**
32191     * adds a Masonry Brick
32192     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32193     */
32194     addBrick : function(cfg)
32195     {
32196         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32197         //this.register(cn);
32198         cn.parentId = this.id;
32199         cn.render(this.el);
32200         return cn;
32201     },
32202     
32203     /**
32204     * register a Masonry Brick
32205     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32206     */
32207     
32208     register : function(brick)
32209     {
32210         this.bricks.push(brick);
32211         brick.masonryId = this.id;
32212     },
32213     
32214     /**
32215     * clear all the Masonry Brick
32216     */
32217     clearAll : function()
32218     {
32219         this.bricks = [];
32220         //this.getChildContainer().dom.innerHTML = "";
32221         this.el.dom.innerHTML = '';
32222     },
32223     
32224     getSelected : function()
32225     {
32226         if (!this.selectedBrick) {
32227             return false;
32228         }
32229         
32230         return this.selectedBrick;
32231     }
32232 });
32233
32234 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32235     
32236     groups: {},
32237      /**
32238     * register a Masonry Layout
32239     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32240     */
32241     
32242     register : function(layout)
32243     {
32244         this.groups[layout.id] = layout;
32245     },
32246     /**
32247     * fetch a  Masonry Layout based on the masonry layout ID
32248     * @param {string} the masonry layout to add
32249     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32250     */
32251     
32252     get: function(layout_id) {
32253         if (typeof(this.groups[layout_id]) == 'undefined') {
32254             return false;
32255         }
32256         return this.groups[layout_id] ;
32257     }
32258     
32259     
32260     
32261 });
32262
32263  
32264
32265  /**
32266  *
32267  * This is based on 
32268  * http://masonry.desandro.com
32269  *
32270  * The idea is to render all the bricks based on vertical width...
32271  *
32272  * The original code extends 'outlayer' - we might need to use that....
32273  * 
32274  */
32275
32276
32277 /**
32278  * @class Roo.bootstrap.LayoutMasonryAuto
32279  * @extends Roo.bootstrap.Component
32280  * Bootstrap Layout Masonry class
32281  * 
32282  * @constructor
32283  * Create a new Element
32284  * @param {Object} config The config object
32285  */
32286
32287 Roo.bootstrap.LayoutMasonryAuto = function(config){
32288     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32289 };
32290
32291 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32292     
32293       /**
32294      * @cfg {Boolean} isFitWidth  - resize the width..
32295      */   
32296     isFitWidth : false,  // options..
32297     /**
32298      * @cfg {Boolean} isOriginLeft = left align?
32299      */   
32300     isOriginLeft : true,
32301     /**
32302      * @cfg {Boolean} isOriginTop = top align?
32303      */   
32304     isOriginTop : false,
32305     /**
32306      * @cfg {Boolean} isLayoutInstant = no animation?
32307      */   
32308     isLayoutInstant : false, // needed?
32309     /**
32310      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32311      */   
32312     isResizingContainer : true,
32313     /**
32314      * @cfg {Number} columnWidth  width of the columns 
32315      */   
32316     
32317     columnWidth : 0,
32318     
32319     /**
32320      * @cfg {Number} maxCols maximum number of columns
32321      */   
32322     
32323     maxCols: 0,
32324     /**
32325      * @cfg {Number} padHeight padding below box..
32326      */   
32327     
32328     padHeight : 10, 
32329     
32330     /**
32331      * @cfg {Boolean} isAutoInitial defalut true
32332      */   
32333     
32334     isAutoInitial : true, 
32335     
32336     // private?
32337     gutter : 0,
32338     
32339     containerWidth: 0,
32340     initialColumnWidth : 0,
32341     currentSize : null,
32342     
32343     colYs : null, // array.
32344     maxY : 0,
32345     padWidth: 10,
32346     
32347     
32348     tag: 'div',
32349     cls: '',
32350     bricks: null, //CompositeElement
32351     cols : 0, // array?
32352     // element : null, // wrapped now this.el
32353     _isLayoutInited : null, 
32354     
32355     
32356     getAutoCreate : function(){
32357         
32358         var cfg = {
32359             tag: this.tag,
32360             cls: 'blog-masonary-wrapper ' + this.cls,
32361             cn : {
32362                 cls : 'mas-boxes masonary'
32363             }
32364         };
32365         
32366         return cfg;
32367     },
32368     
32369     getChildContainer: function( )
32370     {
32371         if (this.boxesEl) {
32372             return this.boxesEl;
32373         }
32374         
32375         this.boxesEl = this.el.select('.mas-boxes').first();
32376         
32377         return this.boxesEl;
32378     },
32379     
32380     
32381     initEvents : function()
32382     {
32383         var _this = this;
32384         
32385         if(this.isAutoInitial){
32386             Roo.log('hook children rendered');
32387             this.on('childrenrendered', function() {
32388                 Roo.log('children rendered');
32389                 _this.initial();
32390             } ,this);
32391         }
32392         
32393     },
32394     
32395     initial : function()
32396     {
32397         this.reloadItems();
32398
32399         this.currentSize = this.el.getBox(true);
32400
32401         /// was window resize... - let's see if this works..
32402         Roo.EventManager.onWindowResize(this.resize, this); 
32403
32404         if(!this.isAutoInitial){
32405             this.layout();
32406             return;
32407         }
32408         
32409         this.layout.defer(500,this);
32410     },
32411     
32412     reloadItems: function()
32413     {
32414         this.bricks = this.el.select('.masonry-brick', true);
32415         
32416         this.bricks.each(function(b) {
32417             //Roo.log(b.getSize());
32418             if (!b.attr('originalwidth')) {
32419                 b.attr('originalwidth',  b.getSize().width);
32420             }
32421             
32422         });
32423         
32424         Roo.log(this.bricks.elements.length);
32425     },
32426     
32427     resize : function()
32428     {
32429         Roo.log('resize');
32430         var cs = this.el.getBox(true);
32431         
32432         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32433             Roo.log("no change in with or X");
32434             return;
32435         }
32436         this.currentSize = cs;
32437         this.layout();
32438     },
32439     
32440     layout : function()
32441     {
32442          Roo.log('layout');
32443         this._resetLayout();
32444         //this._manageStamps();
32445       
32446         // don't animate first layout
32447         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32448         this.layoutItems( isInstant );
32449       
32450         // flag for initalized
32451         this._isLayoutInited = true;
32452     },
32453     
32454     layoutItems : function( isInstant )
32455     {
32456         //var items = this._getItemsForLayout( this.items );
32457         // original code supports filtering layout items.. we just ignore it..
32458         
32459         this._layoutItems( this.bricks , isInstant );
32460       
32461         this._postLayout();
32462     },
32463     _layoutItems : function ( items , isInstant)
32464     {
32465        //this.fireEvent( 'layout', this, items );
32466     
32467
32468         if ( !items || !items.elements.length ) {
32469           // no items, emit event with empty array
32470             return;
32471         }
32472
32473         var queue = [];
32474         items.each(function(item) {
32475             Roo.log("layout item");
32476             Roo.log(item);
32477             // get x/y object from method
32478             var position = this._getItemLayoutPosition( item );
32479             // enqueue
32480             position.item = item;
32481             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32482             queue.push( position );
32483         }, this);
32484       
32485         this._processLayoutQueue( queue );
32486     },
32487     /** Sets position of item in DOM
32488     * @param {Element} item
32489     * @param {Number} x - horizontal position
32490     * @param {Number} y - vertical position
32491     * @param {Boolean} isInstant - disables transitions
32492     */
32493     _processLayoutQueue : function( queue )
32494     {
32495         for ( var i=0, len = queue.length; i < len; i++ ) {
32496             var obj = queue[i];
32497             obj.item.position('absolute');
32498             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32499         }
32500     },
32501       
32502     
32503     /**
32504     * Any logic you want to do after each layout,
32505     * i.e. size the container
32506     */
32507     _postLayout : function()
32508     {
32509         this.resizeContainer();
32510     },
32511     
32512     resizeContainer : function()
32513     {
32514         if ( !this.isResizingContainer ) {
32515             return;
32516         }
32517         var size = this._getContainerSize();
32518         if ( size ) {
32519             this.el.setSize(size.width,size.height);
32520             this.boxesEl.setSize(size.width,size.height);
32521         }
32522     },
32523     
32524     
32525     
32526     _resetLayout : function()
32527     {
32528         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32529         this.colWidth = this.el.getWidth();
32530         //this.gutter = this.el.getWidth(); 
32531         
32532         this.measureColumns();
32533
32534         // reset column Y
32535         var i = this.cols;
32536         this.colYs = [];
32537         while (i--) {
32538             this.colYs.push( 0 );
32539         }
32540     
32541         this.maxY = 0;
32542     },
32543
32544     measureColumns : function()
32545     {
32546         this.getContainerWidth();
32547       // if columnWidth is 0, default to outerWidth of first item
32548         if ( !this.columnWidth ) {
32549             var firstItem = this.bricks.first();
32550             Roo.log(firstItem);
32551             this.columnWidth  = this.containerWidth;
32552             if (firstItem && firstItem.attr('originalwidth') ) {
32553                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32554             }
32555             // columnWidth fall back to item of first element
32556             Roo.log("set column width?");
32557                         this.initialColumnWidth = this.columnWidth  ;
32558
32559             // if first elem has no width, default to size of container
32560             
32561         }
32562         
32563         
32564         if (this.initialColumnWidth) {
32565             this.columnWidth = this.initialColumnWidth;
32566         }
32567         
32568         
32569             
32570         // column width is fixed at the top - however if container width get's smaller we should
32571         // reduce it...
32572         
32573         // this bit calcs how man columns..
32574             
32575         var columnWidth = this.columnWidth += this.gutter;
32576       
32577         // calculate columns
32578         var containerWidth = this.containerWidth + this.gutter;
32579         
32580         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32581         // fix rounding errors, typically with gutters
32582         var excess = columnWidth - containerWidth % columnWidth;
32583         
32584         
32585         // if overshoot is less than a pixel, round up, otherwise floor it
32586         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32587         cols = Math[ mathMethod ]( cols );
32588         this.cols = Math.max( cols, 1 );
32589         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32590         
32591          // padding positioning..
32592         var totalColWidth = this.cols * this.columnWidth;
32593         var padavail = this.containerWidth - totalColWidth;
32594         // so for 2 columns - we need 3 'pads'
32595         
32596         var padNeeded = (1+this.cols) * this.padWidth;
32597         
32598         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32599         
32600         this.columnWidth += padExtra
32601         //this.padWidth = Math.floor(padavail /  ( this.cols));
32602         
32603         // adjust colum width so that padding is fixed??
32604         
32605         // we have 3 columns ... total = width * 3
32606         // we have X left over... that should be used by 
32607         
32608         //if (this.expandC) {
32609             
32610         //}
32611         
32612         
32613         
32614     },
32615     
32616     getContainerWidth : function()
32617     {
32618        /* // container is parent if fit width
32619         var container = this.isFitWidth ? this.element.parentNode : this.element;
32620         // check that this.size and size are there
32621         // IE8 triggers resize on body size change, so they might not be
32622         
32623         var size = getSize( container );  //FIXME
32624         this.containerWidth = size && size.innerWidth; //FIXME
32625         */
32626          
32627         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32628         
32629     },
32630     
32631     _getItemLayoutPosition : function( item )  // what is item?
32632     {
32633         // we resize the item to our columnWidth..
32634       
32635         item.setWidth(this.columnWidth);
32636         item.autoBoxAdjust  = false;
32637         
32638         var sz = item.getSize();
32639  
32640         // how many columns does this brick span
32641         var remainder = this.containerWidth % this.columnWidth;
32642         
32643         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32644         // round if off by 1 pixel, otherwise use ceil
32645         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32646         colSpan = Math.min( colSpan, this.cols );
32647         
32648         // normally this should be '1' as we dont' currently allow multi width columns..
32649         
32650         var colGroup = this._getColGroup( colSpan );
32651         // get the minimum Y value from the columns
32652         var minimumY = Math.min.apply( Math, colGroup );
32653         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32654         
32655         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32656          
32657         // position the brick
32658         var position = {
32659             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32660             y: this.currentSize.y + minimumY + this.padHeight
32661         };
32662         
32663         Roo.log(position);
32664         // apply setHeight to necessary columns
32665         var setHeight = minimumY + sz.height + this.padHeight;
32666         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32667         
32668         var setSpan = this.cols + 1 - colGroup.length;
32669         for ( var i = 0; i < setSpan; i++ ) {
32670           this.colYs[ shortColIndex + i ] = setHeight ;
32671         }
32672       
32673         return position;
32674     },
32675     
32676     /**
32677      * @param {Number} colSpan - number of columns the element spans
32678      * @returns {Array} colGroup
32679      */
32680     _getColGroup : function( colSpan )
32681     {
32682         if ( colSpan < 2 ) {
32683           // if brick spans only one column, use all the column Ys
32684           return this.colYs;
32685         }
32686       
32687         var colGroup = [];
32688         // how many different places could this brick fit horizontally
32689         var groupCount = this.cols + 1 - colSpan;
32690         // for each group potential horizontal position
32691         for ( var i = 0; i < groupCount; i++ ) {
32692           // make an array of colY values for that one group
32693           var groupColYs = this.colYs.slice( i, i + colSpan );
32694           // and get the max value of the array
32695           colGroup[i] = Math.max.apply( Math, groupColYs );
32696         }
32697         return colGroup;
32698     },
32699     /*
32700     _manageStamp : function( stamp )
32701     {
32702         var stampSize =  stamp.getSize();
32703         var offset = stamp.getBox();
32704         // get the columns that this stamp affects
32705         var firstX = this.isOriginLeft ? offset.x : offset.right;
32706         var lastX = firstX + stampSize.width;
32707         var firstCol = Math.floor( firstX / this.columnWidth );
32708         firstCol = Math.max( 0, firstCol );
32709         
32710         var lastCol = Math.floor( lastX / this.columnWidth );
32711         // lastCol should not go over if multiple of columnWidth #425
32712         lastCol -= lastX % this.columnWidth ? 0 : 1;
32713         lastCol = Math.min( this.cols - 1, lastCol );
32714         
32715         // set colYs to bottom of the stamp
32716         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32717             stampSize.height;
32718             
32719         for ( var i = firstCol; i <= lastCol; i++ ) {
32720           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32721         }
32722     },
32723     */
32724     
32725     _getContainerSize : function()
32726     {
32727         this.maxY = Math.max.apply( Math, this.colYs );
32728         var size = {
32729             height: this.maxY
32730         };
32731       
32732         if ( this.isFitWidth ) {
32733             size.width = this._getContainerFitWidth();
32734         }
32735       
32736         return size;
32737     },
32738     
32739     _getContainerFitWidth : function()
32740     {
32741         var unusedCols = 0;
32742         // count unused columns
32743         var i = this.cols;
32744         while ( --i ) {
32745           if ( this.colYs[i] !== 0 ) {
32746             break;
32747           }
32748           unusedCols++;
32749         }
32750         // fit container to columns that have been used
32751         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32752     },
32753     
32754     needsResizeLayout : function()
32755     {
32756         var previousWidth = this.containerWidth;
32757         this.getContainerWidth();
32758         return previousWidth !== this.containerWidth;
32759     }
32760  
32761 });
32762
32763  
32764
32765  /*
32766  * - LGPL
32767  *
32768  * element
32769  * 
32770  */
32771
32772 /**
32773  * @class Roo.bootstrap.MasonryBrick
32774  * @extends Roo.bootstrap.Component
32775  * Bootstrap MasonryBrick class
32776  * 
32777  * @constructor
32778  * Create a new MasonryBrick
32779  * @param {Object} config The config object
32780  */
32781
32782 Roo.bootstrap.MasonryBrick = function(config){
32783     
32784     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32785     
32786     Roo.bootstrap.MasonryBrick.register(this);
32787     
32788     this.addEvents({
32789         // raw events
32790         /**
32791          * @event click
32792          * When a MasonryBrick is clcik
32793          * @param {Roo.bootstrap.MasonryBrick} this
32794          * @param {Roo.EventObject} e
32795          */
32796         "click" : true
32797     });
32798 };
32799
32800 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32801     
32802     /**
32803      * @cfg {String} title
32804      */   
32805     title : '',
32806     /**
32807      * @cfg {String} html
32808      */   
32809     html : '',
32810     /**
32811      * @cfg {String} bgimage
32812      */   
32813     bgimage : '',
32814     /**
32815      * @cfg {String} videourl
32816      */   
32817     videourl : '',
32818     /**
32819      * @cfg {String} cls
32820      */   
32821     cls : '',
32822     /**
32823      * @cfg {String} href
32824      */   
32825     href : '',
32826     /**
32827      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32828      */   
32829     size : 'xs',
32830     
32831     /**
32832      * @cfg {String} placetitle (center|bottom)
32833      */   
32834     placetitle : '',
32835     
32836     /**
32837      * @cfg {Boolean} isFitContainer defalut true
32838      */   
32839     isFitContainer : true, 
32840     
32841     /**
32842      * @cfg {Boolean} preventDefault defalut false
32843      */   
32844     preventDefault : false, 
32845     
32846     /**
32847      * @cfg {Boolean} inverse defalut false
32848      */   
32849     maskInverse : false, 
32850     
32851     getAutoCreate : function()
32852     {
32853         if(!this.isFitContainer){
32854             return this.getSplitAutoCreate();
32855         }
32856         
32857         var cls = 'masonry-brick masonry-brick-full';
32858         
32859         if(this.href.length){
32860             cls += ' masonry-brick-link';
32861         }
32862         
32863         if(this.bgimage.length){
32864             cls += ' masonry-brick-image';
32865         }
32866         
32867         if(this.maskInverse){
32868             cls += ' mask-inverse';
32869         }
32870         
32871         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32872             cls += ' enable-mask';
32873         }
32874         
32875         if(this.size){
32876             cls += ' masonry-' + this.size + '-brick';
32877         }
32878         
32879         if(this.placetitle.length){
32880             
32881             switch (this.placetitle) {
32882                 case 'center' :
32883                     cls += ' masonry-center-title';
32884                     break;
32885                 case 'bottom' :
32886                     cls += ' masonry-bottom-title';
32887                     break;
32888                 default:
32889                     break;
32890             }
32891             
32892         } else {
32893             if(!this.html.length && !this.bgimage.length){
32894                 cls += ' masonry-center-title';
32895             }
32896
32897             if(!this.html.length && this.bgimage.length){
32898                 cls += ' masonry-bottom-title';
32899             }
32900         }
32901         
32902         if(this.cls){
32903             cls += ' ' + this.cls;
32904         }
32905         
32906         var cfg = {
32907             tag: (this.href.length) ? 'a' : 'div',
32908             cls: cls,
32909             cn: [
32910                 {
32911                     tag: 'div',
32912                     cls: 'masonry-brick-mask'
32913                 },
32914                 {
32915                     tag: 'div',
32916                     cls: 'masonry-brick-paragraph',
32917                     cn: []
32918                 }
32919             ]
32920         };
32921         
32922         if(this.href.length){
32923             cfg.href = this.href;
32924         }
32925         
32926         var cn = cfg.cn[1].cn;
32927         
32928         if(this.title.length){
32929             cn.push({
32930                 tag: 'h4',
32931                 cls: 'masonry-brick-title',
32932                 html: this.title
32933             });
32934         }
32935         
32936         if(this.html.length){
32937             cn.push({
32938                 tag: 'p',
32939                 cls: 'masonry-brick-text',
32940                 html: this.html
32941             });
32942         }
32943         
32944         if (!this.title.length && !this.html.length) {
32945             cfg.cn[1].cls += ' hide';
32946         }
32947         
32948         if(this.bgimage.length){
32949             cfg.cn.push({
32950                 tag: 'img',
32951                 cls: 'masonry-brick-image-view',
32952                 src: this.bgimage
32953             });
32954         }
32955         
32956         if(this.videourl.length){
32957             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32958             // youtube support only?
32959             cfg.cn.push({
32960                 tag: 'iframe',
32961                 cls: 'masonry-brick-image-view',
32962                 src: vurl,
32963                 frameborder : 0,
32964                 allowfullscreen : true
32965             });
32966         }
32967         
32968         return cfg;
32969         
32970     },
32971     
32972     getSplitAutoCreate : function()
32973     {
32974         var cls = 'masonry-brick masonry-brick-split';
32975         
32976         if(this.href.length){
32977             cls += ' masonry-brick-link';
32978         }
32979         
32980         if(this.bgimage.length){
32981             cls += ' masonry-brick-image';
32982         }
32983         
32984         if(this.size){
32985             cls += ' masonry-' + this.size + '-brick';
32986         }
32987         
32988         switch (this.placetitle) {
32989             case 'center' :
32990                 cls += ' masonry-center-title';
32991                 break;
32992             case 'bottom' :
32993                 cls += ' masonry-bottom-title';
32994                 break;
32995             default:
32996                 if(!this.bgimage.length){
32997                     cls += ' masonry-center-title';
32998                 }
32999
33000                 if(this.bgimage.length){
33001                     cls += ' masonry-bottom-title';
33002                 }
33003                 break;
33004         }
33005         
33006         if(this.cls){
33007             cls += ' ' + this.cls;
33008         }
33009         
33010         var cfg = {
33011             tag: (this.href.length) ? 'a' : 'div',
33012             cls: cls,
33013             cn: [
33014                 {
33015                     tag: 'div',
33016                     cls: 'masonry-brick-split-head',
33017                     cn: [
33018                         {
33019                             tag: 'div',
33020                             cls: 'masonry-brick-paragraph',
33021                             cn: []
33022                         }
33023                     ]
33024                 },
33025                 {
33026                     tag: 'div',
33027                     cls: 'masonry-brick-split-body',
33028                     cn: []
33029                 }
33030             ]
33031         };
33032         
33033         if(this.href.length){
33034             cfg.href = this.href;
33035         }
33036         
33037         if(this.title.length){
33038             cfg.cn[0].cn[0].cn.push({
33039                 tag: 'h4',
33040                 cls: 'masonry-brick-title',
33041                 html: this.title
33042             });
33043         }
33044         
33045         if(this.html.length){
33046             cfg.cn[1].cn.push({
33047                 tag: 'p',
33048                 cls: 'masonry-brick-text',
33049                 html: this.html
33050             });
33051         }
33052
33053         if(this.bgimage.length){
33054             cfg.cn[0].cn.push({
33055                 tag: 'img',
33056                 cls: 'masonry-brick-image-view',
33057                 src: this.bgimage
33058             });
33059         }
33060         
33061         if(this.videourl.length){
33062             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33063             // youtube support only?
33064             cfg.cn[0].cn.cn.push({
33065                 tag: 'iframe',
33066                 cls: 'masonry-brick-image-view',
33067                 src: vurl,
33068                 frameborder : 0,
33069                 allowfullscreen : true
33070             });
33071         }
33072         
33073         return cfg;
33074     },
33075     
33076     initEvents: function() 
33077     {
33078         switch (this.size) {
33079             case 'xs' :
33080                 this.x = 1;
33081                 this.y = 1;
33082                 break;
33083             case 'sm' :
33084                 this.x = 2;
33085                 this.y = 2;
33086                 break;
33087             case 'md' :
33088             case 'md-left' :
33089             case 'md-right' :
33090                 this.x = 3;
33091                 this.y = 3;
33092                 break;
33093             case 'tall' :
33094                 this.x = 2;
33095                 this.y = 3;
33096                 break;
33097             case 'wide' :
33098                 this.x = 3;
33099                 this.y = 2;
33100                 break;
33101             case 'wide-thin' :
33102                 this.x = 3;
33103                 this.y = 1;
33104                 break;
33105                         
33106             default :
33107                 break;
33108         }
33109         
33110         if(Roo.isTouch){
33111             this.el.on('touchstart', this.onTouchStart, this);
33112             this.el.on('touchmove', this.onTouchMove, this);
33113             this.el.on('touchend', this.onTouchEnd, this);
33114             this.el.on('contextmenu', this.onContextMenu, this);
33115         } else {
33116             this.el.on('mouseenter'  ,this.enter, this);
33117             this.el.on('mouseleave', this.leave, this);
33118             this.el.on('click', this.onClick, this);
33119         }
33120         
33121         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33122             this.parent().bricks.push(this);   
33123         }
33124         
33125     },
33126     
33127     onClick: function(e, el)
33128     {
33129         var time = this.endTimer - this.startTimer;
33130         // Roo.log(e.preventDefault());
33131         if(Roo.isTouch){
33132             if(time > 1000){
33133                 e.preventDefault();
33134                 return;
33135             }
33136         }
33137         
33138         if(!this.preventDefault){
33139             return;
33140         }
33141         
33142         e.preventDefault();
33143         
33144         if (this.activeClass != '') {
33145             this.selectBrick();
33146         }
33147         
33148         this.fireEvent('click', this, e);
33149     },
33150     
33151     enter: function(e, el)
33152     {
33153         e.preventDefault();
33154         
33155         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33156             return;
33157         }
33158         
33159         if(this.bgimage.length && this.html.length){
33160             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33161         }
33162     },
33163     
33164     leave: function(e, el)
33165     {
33166         e.preventDefault();
33167         
33168         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33169             return;
33170         }
33171         
33172         if(this.bgimage.length && this.html.length){
33173             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33174         }
33175     },
33176     
33177     onTouchStart: function(e, el)
33178     {
33179 //        e.preventDefault();
33180         
33181         this.touchmoved = false;
33182         
33183         if(!this.isFitContainer){
33184             return;
33185         }
33186         
33187         if(!this.bgimage.length || !this.html.length){
33188             return;
33189         }
33190         
33191         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33192         
33193         this.timer = new Date().getTime();
33194         
33195     },
33196     
33197     onTouchMove: function(e, el)
33198     {
33199         this.touchmoved = true;
33200     },
33201     
33202     onContextMenu : function(e,el)
33203     {
33204         e.preventDefault();
33205         e.stopPropagation();
33206         return false;
33207     },
33208     
33209     onTouchEnd: function(e, el)
33210     {
33211 //        e.preventDefault();
33212         
33213         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33214         
33215             this.leave(e,el);
33216             
33217             return;
33218         }
33219         
33220         if(!this.bgimage.length || !this.html.length){
33221             
33222             if(this.href.length){
33223                 window.location.href = this.href;
33224             }
33225             
33226             return;
33227         }
33228         
33229         if(!this.isFitContainer){
33230             return;
33231         }
33232         
33233         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33234         
33235         window.location.href = this.href;
33236     },
33237     
33238     //selection on single brick only
33239     selectBrick : function() {
33240         
33241         if (!this.parentId) {
33242             return;
33243         }
33244         
33245         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33246         var index = m.selectedBrick.indexOf(this.id);
33247         
33248         if ( index > -1) {
33249             m.selectedBrick.splice(index,1);
33250             this.el.removeClass(this.activeClass);
33251             return;
33252         }
33253         
33254         for(var i = 0; i < m.selectedBrick.length; i++) {
33255             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33256             b.el.removeClass(b.activeClass);
33257         }
33258         
33259         m.selectedBrick = [];
33260         
33261         m.selectedBrick.push(this.id);
33262         this.el.addClass(this.activeClass);
33263         return;
33264     },
33265     
33266     isSelected : function(){
33267         return this.el.hasClass(this.activeClass);
33268         
33269     }
33270 });
33271
33272 Roo.apply(Roo.bootstrap.MasonryBrick, {
33273     
33274     //groups: {},
33275     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33276      /**
33277     * register a Masonry Brick
33278     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33279     */
33280     
33281     register : function(brick)
33282     {
33283         //this.groups[brick.id] = brick;
33284         this.groups.add(brick.id, brick);
33285     },
33286     /**
33287     * fetch a  masonry brick based on the masonry brick ID
33288     * @param {string} the masonry brick to add
33289     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33290     */
33291     
33292     get: function(brick_id) 
33293     {
33294         // if (typeof(this.groups[brick_id]) == 'undefined') {
33295         //     return false;
33296         // }
33297         // return this.groups[brick_id] ;
33298         
33299         if(this.groups.key(brick_id)) {
33300             return this.groups.key(brick_id);
33301         }
33302         
33303         return false;
33304     }
33305     
33306     
33307     
33308 });
33309
33310  /*
33311  * - LGPL
33312  *
33313  * element
33314  * 
33315  */
33316
33317 /**
33318  * @class Roo.bootstrap.Brick
33319  * @extends Roo.bootstrap.Component
33320  * Bootstrap Brick class
33321  * 
33322  * @constructor
33323  * Create a new Brick
33324  * @param {Object} config The config object
33325  */
33326
33327 Roo.bootstrap.Brick = function(config){
33328     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33329     
33330     this.addEvents({
33331         // raw events
33332         /**
33333          * @event click
33334          * When a Brick is click
33335          * @param {Roo.bootstrap.Brick} this
33336          * @param {Roo.EventObject} e
33337          */
33338         "click" : true
33339     });
33340 };
33341
33342 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33343     
33344     /**
33345      * @cfg {String} title
33346      */   
33347     title : '',
33348     /**
33349      * @cfg {String} html
33350      */   
33351     html : '',
33352     /**
33353      * @cfg {String} bgimage
33354      */   
33355     bgimage : '',
33356     /**
33357      * @cfg {String} cls
33358      */   
33359     cls : '',
33360     /**
33361      * @cfg {String} href
33362      */   
33363     href : '',
33364     /**
33365      * @cfg {String} video
33366      */   
33367     video : '',
33368     /**
33369      * @cfg {Boolean} square
33370      */   
33371     square : true,
33372     
33373     getAutoCreate : function()
33374     {
33375         var cls = 'roo-brick';
33376         
33377         if(this.href.length){
33378             cls += ' roo-brick-link';
33379         }
33380         
33381         if(this.bgimage.length){
33382             cls += ' roo-brick-image';
33383         }
33384         
33385         if(!this.html.length && !this.bgimage.length){
33386             cls += ' roo-brick-center-title';
33387         }
33388         
33389         if(!this.html.length && this.bgimage.length){
33390             cls += ' roo-brick-bottom-title';
33391         }
33392         
33393         if(this.cls){
33394             cls += ' ' + this.cls;
33395         }
33396         
33397         var cfg = {
33398             tag: (this.href.length) ? 'a' : 'div',
33399             cls: cls,
33400             cn: [
33401                 {
33402                     tag: 'div',
33403                     cls: 'roo-brick-paragraph',
33404                     cn: []
33405                 }
33406             ]
33407         };
33408         
33409         if(this.href.length){
33410             cfg.href = this.href;
33411         }
33412         
33413         var cn = cfg.cn[0].cn;
33414         
33415         if(this.title.length){
33416             cn.push({
33417                 tag: 'h4',
33418                 cls: 'roo-brick-title',
33419                 html: this.title
33420             });
33421         }
33422         
33423         if(this.html.length){
33424             cn.push({
33425                 tag: 'p',
33426                 cls: 'roo-brick-text',
33427                 html: this.html
33428             });
33429         } else {
33430             cn.cls += ' hide';
33431         }
33432         
33433         if(this.bgimage.length){
33434             cfg.cn.push({
33435                 tag: 'img',
33436                 cls: 'roo-brick-image-view',
33437                 src: this.bgimage
33438             });
33439         }
33440         
33441         return cfg;
33442     },
33443     
33444     initEvents: function() 
33445     {
33446         if(this.title.length || this.html.length){
33447             this.el.on('mouseenter'  ,this.enter, this);
33448             this.el.on('mouseleave', this.leave, this);
33449         }
33450         
33451         Roo.EventManager.onWindowResize(this.resize, this); 
33452         
33453         if(this.bgimage.length){
33454             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33455             this.imageEl.on('load', this.onImageLoad, this);
33456             return;
33457         }
33458         
33459         this.resize();
33460     },
33461     
33462     onImageLoad : function()
33463     {
33464         this.resize();
33465     },
33466     
33467     resize : function()
33468     {
33469         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33470         
33471         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33472         
33473         if(this.bgimage.length){
33474             var image = this.el.select('.roo-brick-image-view', true).first();
33475             
33476             image.setWidth(paragraph.getWidth());
33477             
33478             if(this.square){
33479                 image.setHeight(paragraph.getWidth());
33480             }
33481             
33482             this.el.setHeight(image.getHeight());
33483             paragraph.setHeight(image.getHeight());
33484             
33485         }
33486         
33487     },
33488     
33489     enter: function(e, el)
33490     {
33491         e.preventDefault();
33492         
33493         if(this.bgimage.length){
33494             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33495             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33496         }
33497     },
33498     
33499     leave: function(e, el)
33500     {
33501         e.preventDefault();
33502         
33503         if(this.bgimage.length){
33504             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33505             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33506         }
33507     }
33508     
33509 });
33510
33511  
33512
33513  /*
33514  * - LGPL
33515  *
33516  * Number field 
33517  */
33518
33519 /**
33520  * @class Roo.bootstrap.NumberField
33521  * @extends Roo.bootstrap.Input
33522  * Bootstrap NumberField class
33523  * 
33524  * 
33525  * 
33526  * 
33527  * @constructor
33528  * Create a new NumberField
33529  * @param {Object} config The config object
33530  */
33531
33532 Roo.bootstrap.NumberField = function(config){
33533     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33534 };
33535
33536 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33537     
33538     /**
33539      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33540      */
33541     allowDecimals : true,
33542     /**
33543      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33544      */
33545     decimalSeparator : ".",
33546     /**
33547      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33548      */
33549     decimalPrecision : 2,
33550     /**
33551      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33552      */
33553     allowNegative : true,
33554     
33555     /**
33556      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33557      */
33558     allowZero: true,
33559     /**
33560      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33561      */
33562     minValue : Number.NEGATIVE_INFINITY,
33563     /**
33564      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33565      */
33566     maxValue : Number.MAX_VALUE,
33567     /**
33568      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33569      */
33570     minText : "The minimum value for this field is {0}",
33571     /**
33572      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33573      */
33574     maxText : "The maximum value for this field is {0}",
33575     /**
33576      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33577      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33578      */
33579     nanText : "{0} is not a valid number",
33580     /**
33581      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33582      */
33583     thousandsDelimiter : false,
33584     /**
33585      * @cfg {String} valueAlign alignment of value
33586      */
33587     valueAlign : "left",
33588
33589     getAutoCreate : function()
33590     {
33591         var hiddenInput = {
33592             tag: 'input',
33593             type: 'hidden',
33594             id: Roo.id(),
33595             cls: 'hidden-number-input'
33596         };
33597         
33598         if (this.name) {
33599             hiddenInput.name = this.name;
33600         }
33601         
33602         this.name = '';
33603         
33604         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33605         
33606         this.name = hiddenInput.name;
33607         
33608         if(cfg.cn.length > 0) {
33609             cfg.cn.push(hiddenInput);
33610         }
33611         
33612         return cfg;
33613     },
33614
33615     // private
33616     initEvents : function()
33617     {   
33618         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33619         
33620         var allowed = "0123456789";
33621         
33622         if(this.allowDecimals){
33623             allowed += this.decimalSeparator;
33624         }
33625         
33626         if(this.allowNegative){
33627             allowed += "-";
33628         }
33629         
33630         if(this.thousandsDelimiter) {
33631             allowed += ",";
33632         }
33633         
33634         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33635         
33636         var keyPress = function(e){
33637             
33638             var k = e.getKey();
33639             
33640             var c = e.getCharCode();
33641             
33642             if(
33643                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33644                     allowed.indexOf(String.fromCharCode(c)) === -1
33645             ){
33646                 e.stopEvent();
33647                 return;
33648             }
33649             
33650             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33651                 return;
33652             }
33653             
33654             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33655                 e.stopEvent();
33656             }
33657         };
33658         
33659         this.el.on("keypress", keyPress, this);
33660     },
33661     
33662     validateValue : function(value)
33663     {
33664         
33665         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33666             return false;
33667         }
33668         
33669         var num = this.parseValue(value);
33670         
33671         if(isNaN(num)){
33672             this.markInvalid(String.format(this.nanText, value));
33673             return false;
33674         }
33675         
33676         if(num < this.minValue){
33677             this.markInvalid(String.format(this.minText, this.minValue));
33678             return false;
33679         }
33680         
33681         if(num > this.maxValue){
33682             this.markInvalid(String.format(this.maxText, this.maxValue));
33683             return false;
33684         }
33685         
33686         return true;
33687     },
33688
33689     getValue : function()
33690     {
33691         var v = this.hiddenEl().getValue();
33692         
33693         return this.fixPrecision(this.parseValue(v));
33694     },
33695
33696     parseValue : function(value)
33697     {
33698         if(this.thousandsDelimiter) {
33699             value += "";
33700             r = new RegExp(",", "g");
33701             value = value.replace(r, "");
33702         }
33703         
33704         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33705         return isNaN(value) ? '' : value;
33706     },
33707
33708     fixPrecision : function(value)
33709     {
33710         if(this.thousandsDelimiter) {
33711             value += "";
33712             r = new RegExp(",", "g");
33713             value = value.replace(r, "");
33714         }
33715         
33716         var nan = isNaN(value);
33717         
33718         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33719             return nan ? '' : value;
33720         }
33721         return parseFloat(value).toFixed(this.decimalPrecision);
33722     },
33723
33724     setValue : function(v)
33725     {
33726         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33727         
33728         this.value = v;
33729         
33730         if(this.rendered){
33731             
33732             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33733             
33734             this.inputEl().dom.value = (v == '') ? '' :
33735                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33736             
33737             if(!this.allowZero && v === '0') {
33738                 this.hiddenEl().dom.value = '';
33739                 this.inputEl().dom.value = '';
33740             }
33741             
33742             this.validate();
33743         }
33744     },
33745
33746     decimalPrecisionFcn : function(v)
33747     {
33748         return Math.floor(v);
33749     },
33750
33751     beforeBlur : function()
33752     {
33753         var v = this.parseValue(this.getRawValue());
33754         
33755         if(v || v === 0 || v === ''){
33756             this.setValue(v);
33757         }
33758     },
33759     
33760     hiddenEl : function()
33761     {
33762         return this.el.select('input.hidden-number-input',true).first();
33763     }
33764     
33765 });
33766
33767  
33768
33769 /*
33770 * Licence: LGPL
33771 */
33772
33773 /**
33774  * @class Roo.bootstrap.DocumentSlider
33775  * @extends Roo.bootstrap.Component
33776  * Bootstrap DocumentSlider class
33777  * 
33778  * @constructor
33779  * Create a new DocumentViewer
33780  * @param {Object} config The config object
33781  */
33782
33783 Roo.bootstrap.DocumentSlider = function(config){
33784     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33785     
33786     this.files = [];
33787     
33788     this.addEvents({
33789         /**
33790          * @event initial
33791          * Fire after initEvent
33792          * @param {Roo.bootstrap.DocumentSlider} this
33793          */
33794         "initial" : true,
33795         /**
33796          * @event update
33797          * Fire after update
33798          * @param {Roo.bootstrap.DocumentSlider} this
33799          */
33800         "update" : true,
33801         /**
33802          * @event click
33803          * Fire after click
33804          * @param {Roo.bootstrap.DocumentSlider} this
33805          */
33806         "click" : true
33807     });
33808 };
33809
33810 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33811     
33812     files : false,
33813     
33814     indicator : 0,
33815     
33816     getAutoCreate : function()
33817     {
33818         var cfg = {
33819             tag : 'div',
33820             cls : 'roo-document-slider',
33821             cn : [
33822                 {
33823                     tag : 'div',
33824                     cls : 'roo-document-slider-header',
33825                     cn : [
33826                         {
33827                             tag : 'div',
33828                             cls : 'roo-document-slider-header-title'
33829                         }
33830                     ]
33831                 },
33832                 {
33833                     tag : 'div',
33834                     cls : 'roo-document-slider-body',
33835                     cn : [
33836                         {
33837                             tag : 'div',
33838                             cls : 'roo-document-slider-prev',
33839                             cn : [
33840                                 {
33841                                     tag : 'i',
33842                                     cls : 'fa fa-chevron-left'
33843                                 }
33844                             ]
33845                         },
33846                         {
33847                             tag : 'div',
33848                             cls : 'roo-document-slider-thumb',
33849                             cn : [
33850                                 {
33851                                     tag : 'img',
33852                                     cls : 'roo-document-slider-image'
33853                                 }
33854                             ]
33855                         },
33856                         {
33857                             tag : 'div',
33858                             cls : 'roo-document-slider-next',
33859                             cn : [
33860                                 {
33861                                     tag : 'i',
33862                                     cls : 'fa fa-chevron-right'
33863                                 }
33864                             ]
33865                         }
33866                     ]
33867                 }
33868             ]
33869         };
33870         
33871         return cfg;
33872     },
33873     
33874     initEvents : function()
33875     {
33876         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33877         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33878         
33879         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33880         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33881         
33882         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33883         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33884         
33885         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33886         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33887         
33888         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33889         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33890         
33891         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33892         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33893         
33894         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33895         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33896         
33897         this.thumbEl.on('click', this.onClick, this);
33898         
33899         this.prevIndicator.on('click', this.prev, this);
33900         
33901         this.nextIndicator.on('click', this.next, this);
33902         
33903     },
33904     
33905     initial : function()
33906     {
33907         if(this.files.length){
33908             this.indicator = 1;
33909             this.update()
33910         }
33911         
33912         this.fireEvent('initial', this);
33913     },
33914     
33915     update : function()
33916     {
33917         this.imageEl.attr('src', this.files[this.indicator - 1]);
33918         
33919         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33920         
33921         this.prevIndicator.show();
33922         
33923         if(this.indicator == 1){
33924             this.prevIndicator.hide();
33925         }
33926         
33927         this.nextIndicator.show();
33928         
33929         if(this.indicator == this.files.length){
33930             this.nextIndicator.hide();
33931         }
33932         
33933         this.thumbEl.scrollTo('top');
33934         
33935         this.fireEvent('update', this);
33936     },
33937     
33938     onClick : function(e)
33939     {
33940         e.preventDefault();
33941         
33942         this.fireEvent('click', this);
33943     },
33944     
33945     prev : function(e)
33946     {
33947         e.preventDefault();
33948         
33949         this.indicator = Math.max(1, this.indicator - 1);
33950         
33951         this.update();
33952     },
33953     
33954     next : function(e)
33955     {
33956         e.preventDefault();
33957         
33958         this.indicator = Math.min(this.files.length, this.indicator + 1);
33959         
33960         this.update();
33961     }
33962 });
33963 /*
33964  * - LGPL
33965  *
33966  * RadioSet
33967  *
33968  *
33969  */
33970
33971 /**
33972  * @class Roo.bootstrap.RadioSet
33973  * @extends Roo.bootstrap.Input
33974  * Bootstrap RadioSet class
33975  * @cfg {String} indicatorpos (left|right) default left
33976  * @cfg {Boolean} inline (true|false) inline the element (default true)
33977  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33978  * @constructor
33979  * Create a new RadioSet
33980  * @param {Object} config The config object
33981  */
33982
33983 Roo.bootstrap.RadioSet = function(config){
33984     
33985     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33986     
33987     this.radioes = [];
33988     
33989     Roo.bootstrap.RadioSet.register(this);
33990     
33991     this.addEvents({
33992         /**
33993         * @event check
33994         * Fires when the element is checked or unchecked.
33995         * @param {Roo.bootstrap.RadioSet} this This radio
33996         * @param {Roo.bootstrap.Radio} item The checked item
33997         */
33998        check : true,
33999        /**
34000         * @event click
34001         * Fires when the element is click.
34002         * @param {Roo.bootstrap.RadioSet} this This radio set
34003         * @param {Roo.bootstrap.Radio} item The checked item
34004         * @param {Roo.EventObject} e The event object
34005         */
34006        click : true
34007     });
34008     
34009 };
34010
34011 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34012
34013     radioes : false,
34014     
34015     inline : true,
34016     
34017     weight : '',
34018     
34019     indicatorpos : 'left',
34020     
34021     getAutoCreate : function()
34022     {
34023         var label = {
34024             tag : 'label',
34025             cls : 'roo-radio-set-label',
34026             cn : [
34027                 {
34028                     tag : 'span',
34029                     html : this.fieldLabel
34030                 }
34031             ]
34032         };
34033         
34034         if(this.indicatorpos == 'left'){
34035             label.cn.unshift({
34036                 tag : 'i',
34037                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34038                 tooltip : 'This field is required'
34039             });
34040         } else {
34041             label.cn.push({
34042                 tag : 'i',
34043                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34044                 tooltip : 'This field is required'
34045             });
34046         }
34047         
34048         var items = {
34049             tag : 'div',
34050             cls : 'roo-radio-set-items'
34051         };
34052         
34053         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34054         
34055         if (align === 'left' && this.fieldLabel.length) {
34056             
34057             items = {
34058                 cls : "roo-radio-set-right", 
34059                 cn: [
34060                     items
34061                 ]
34062             };
34063             
34064             if(this.labelWidth > 12){
34065                 label.style = "width: " + this.labelWidth + 'px';
34066             }
34067             
34068             if(this.labelWidth < 13 && this.labelmd == 0){
34069                 this.labelmd = this.labelWidth;
34070             }
34071             
34072             if(this.labellg > 0){
34073                 label.cls += ' col-lg-' + this.labellg;
34074                 items.cls += ' col-lg-' + (12 - this.labellg);
34075             }
34076             
34077             if(this.labelmd > 0){
34078                 label.cls += ' col-md-' + this.labelmd;
34079                 items.cls += ' col-md-' + (12 - this.labelmd);
34080             }
34081             
34082             if(this.labelsm > 0){
34083                 label.cls += ' col-sm-' + this.labelsm;
34084                 items.cls += ' col-sm-' + (12 - this.labelsm);
34085             }
34086             
34087             if(this.labelxs > 0){
34088                 label.cls += ' col-xs-' + this.labelxs;
34089                 items.cls += ' col-xs-' + (12 - this.labelxs);
34090             }
34091         }
34092         
34093         var cfg = {
34094             tag : 'div',
34095             cls : 'roo-radio-set',
34096             cn : [
34097                 {
34098                     tag : 'input',
34099                     cls : 'roo-radio-set-input',
34100                     type : 'hidden',
34101                     name : this.name,
34102                     value : this.value ? this.value :  ''
34103                 },
34104                 label,
34105                 items
34106             ]
34107         };
34108         
34109         if(this.weight.length){
34110             cfg.cls += ' roo-radio-' + this.weight;
34111         }
34112         
34113         if(this.inline) {
34114             cfg.cls += ' roo-radio-set-inline';
34115         }
34116         
34117         var settings=this;
34118         ['xs','sm','md','lg'].map(function(size){
34119             if (settings[size]) {
34120                 cfg.cls += ' col-' + size + '-' + settings[size];
34121             }
34122         });
34123         
34124         return cfg;
34125         
34126     },
34127
34128     initEvents : function()
34129     {
34130         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34131         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34132         
34133         if(!this.fieldLabel.length){
34134             this.labelEl.hide();
34135         }
34136         
34137         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34138         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34139         
34140         this.indicator = this.indicatorEl();
34141         
34142         if(this.indicator){
34143             this.indicator.addClass('invisible');
34144         }
34145         
34146         this.originalValue = this.getValue();
34147         
34148     },
34149     
34150     inputEl: function ()
34151     {
34152         return this.el.select('.roo-radio-set-input', true).first();
34153     },
34154     
34155     getChildContainer : function()
34156     {
34157         return this.itemsEl;
34158     },
34159     
34160     register : function(item)
34161     {
34162         this.radioes.push(item);
34163         
34164     },
34165     
34166     validate : function()
34167     {   
34168         if(this.getVisibilityEl().hasClass('hidden')){
34169             return true;
34170         }
34171         
34172         var valid = false;
34173         
34174         Roo.each(this.radioes, function(i){
34175             if(!i.checked){
34176                 return;
34177             }
34178             
34179             valid = true;
34180             return false;
34181         });
34182         
34183         if(this.allowBlank) {
34184             return true;
34185         }
34186         
34187         if(this.disabled || valid){
34188             this.markValid();
34189             return true;
34190         }
34191         
34192         this.markInvalid();
34193         return false;
34194         
34195     },
34196     
34197     markValid : function()
34198     {
34199         if(this.labelEl.isVisible(true)){
34200             this.indicatorEl().removeClass('visible');
34201             this.indicatorEl().addClass('invisible');
34202         }
34203         
34204         this.el.removeClass([this.invalidClass, this.validClass]);
34205         this.el.addClass(this.validClass);
34206         
34207         this.fireEvent('valid', this);
34208     },
34209     
34210     markInvalid : function(msg)
34211     {
34212         if(this.allowBlank || this.disabled){
34213             return;
34214         }
34215         
34216         if(this.labelEl.isVisible(true)){
34217             this.indicatorEl().removeClass('invisible');
34218             this.indicatorEl().addClass('visible');
34219         }
34220         
34221         this.el.removeClass([this.invalidClass, this.validClass]);
34222         this.el.addClass(this.invalidClass);
34223         
34224         this.fireEvent('invalid', this, msg);
34225         
34226     },
34227     
34228     setValue : function(v, suppressEvent)
34229     {   
34230         if(this.value === v){
34231             return;
34232         }
34233         
34234         this.value = v;
34235         
34236         if(this.rendered){
34237             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34238         }
34239         
34240         Roo.each(this.radioes, function(i){
34241             i.checked = false;
34242             i.el.removeClass('checked');
34243         });
34244         
34245         Roo.each(this.radioes, function(i){
34246             
34247             if(i.value === v || i.value.toString() === v.toString()){
34248                 i.checked = true;
34249                 i.el.addClass('checked');
34250                 
34251                 if(suppressEvent !== true){
34252                     this.fireEvent('check', this, i);
34253                 }
34254                 
34255                 return false;
34256             }
34257             
34258         }, this);
34259         
34260         this.validate();
34261     },
34262     
34263     clearInvalid : function(){
34264         
34265         if(!this.el || this.preventMark){
34266             return;
34267         }
34268         
34269         this.el.removeClass([this.invalidClass]);
34270         
34271         this.fireEvent('valid', this);
34272     }
34273     
34274 });
34275
34276 Roo.apply(Roo.bootstrap.RadioSet, {
34277     
34278     groups: {},
34279     
34280     register : function(set)
34281     {
34282         this.groups[set.name] = set;
34283     },
34284     
34285     get: function(name) 
34286     {
34287         if (typeof(this.groups[name]) == 'undefined') {
34288             return false;
34289         }
34290         
34291         return this.groups[name] ;
34292     }
34293     
34294 });
34295 /*
34296  * Based on:
34297  * Ext JS Library 1.1.1
34298  * Copyright(c) 2006-2007, Ext JS, LLC.
34299  *
34300  * Originally Released Under LGPL - original licence link has changed is not relivant.
34301  *
34302  * Fork - LGPL
34303  * <script type="text/javascript">
34304  */
34305
34306
34307 /**
34308  * @class Roo.bootstrap.SplitBar
34309  * @extends Roo.util.Observable
34310  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34311  * <br><br>
34312  * Usage:
34313  * <pre><code>
34314 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34315                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34316 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34317 split.minSize = 100;
34318 split.maxSize = 600;
34319 split.animate = true;
34320 split.on('moved', splitterMoved);
34321 </code></pre>
34322  * @constructor
34323  * Create a new SplitBar
34324  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34325  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34326  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34327  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34328                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34329                         position of the SplitBar).
34330  */
34331 Roo.bootstrap.SplitBar = function(cfg){
34332     
34333     /** @private */
34334     
34335     //{
34336     //  dragElement : elm
34337     //  resizingElement: el,
34338         // optional..
34339     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34340     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34341         // existingProxy ???
34342     //}
34343     
34344     this.el = Roo.get(cfg.dragElement, true);
34345     this.el.dom.unselectable = "on";
34346     /** @private */
34347     this.resizingEl = Roo.get(cfg.resizingElement, true);
34348
34349     /**
34350      * @private
34351      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34352      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34353      * @type Number
34354      */
34355     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34356     
34357     /**
34358      * The minimum size of the resizing element. (Defaults to 0)
34359      * @type Number
34360      */
34361     this.minSize = 0;
34362     
34363     /**
34364      * The maximum size of the resizing element. (Defaults to 2000)
34365      * @type Number
34366      */
34367     this.maxSize = 2000;
34368     
34369     /**
34370      * Whether to animate the transition to the new size
34371      * @type Boolean
34372      */
34373     this.animate = false;
34374     
34375     /**
34376      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34377      * @type Boolean
34378      */
34379     this.useShim = false;
34380     
34381     /** @private */
34382     this.shim = null;
34383     
34384     if(!cfg.existingProxy){
34385         /** @private */
34386         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34387     }else{
34388         this.proxy = Roo.get(cfg.existingProxy).dom;
34389     }
34390     /** @private */
34391     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34392     
34393     /** @private */
34394     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34395     
34396     /** @private */
34397     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34398     
34399     /** @private */
34400     this.dragSpecs = {};
34401     
34402     /**
34403      * @private The adapter to use to positon and resize elements
34404      */
34405     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34406     this.adapter.init(this);
34407     
34408     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34409         /** @private */
34410         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34411         this.el.addClass("roo-splitbar-h");
34412     }else{
34413         /** @private */
34414         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34415         this.el.addClass("roo-splitbar-v");
34416     }
34417     
34418     this.addEvents({
34419         /**
34420          * @event resize
34421          * Fires when the splitter is moved (alias for {@link #event-moved})
34422          * @param {Roo.bootstrap.SplitBar} this
34423          * @param {Number} newSize the new width or height
34424          */
34425         "resize" : true,
34426         /**
34427          * @event moved
34428          * Fires when the splitter is moved
34429          * @param {Roo.bootstrap.SplitBar} this
34430          * @param {Number} newSize the new width or height
34431          */
34432         "moved" : true,
34433         /**
34434          * @event beforeresize
34435          * Fires before the splitter is dragged
34436          * @param {Roo.bootstrap.SplitBar} this
34437          */
34438         "beforeresize" : true,
34439
34440         "beforeapply" : true
34441     });
34442
34443     Roo.util.Observable.call(this);
34444 };
34445
34446 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34447     onStartProxyDrag : function(x, y){
34448         this.fireEvent("beforeresize", this);
34449         if(!this.overlay){
34450             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34451             o.unselectable();
34452             o.enableDisplayMode("block");
34453             // all splitbars share the same overlay
34454             Roo.bootstrap.SplitBar.prototype.overlay = o;
34455         }
34456         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34457         this.overlay.show();
34458         Roo.get(this.proxy).setDisplayed("block");
34459         var size = this.adapter.getElementSize(this);
34460         this.activeMinSize = this.getMinimumSize();;
34461         this.activeMaxSize = this.getMaximumSize();;
34462         var c1 = size - this.activeMinSize;
34463         var c2 = Math.max(this.activeMaxSize - size, 0);
34464         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34465             this.dd.resetConstraints();
34466             this.dd.setXConstraint(
34467                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34468                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34469             );
34470             this.dd.setYConstraint(0, 0);
34471         }else{
34472             this.dd.resetConstraints();
34473             this.dd.setXConstraint(0, 0);
34474             this.dd.setYConstraint(
34475                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34476                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34477             );
34478          }
34479         this.dragSpecs.startSize = size;
34480         this.dragSpecs.startPoint = [x, y];
34481         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34482     },
34483     
34484     /** 
34485      * @private Called after the drag operation by the DDProxy
34486      */
34487     onEndProxyDrag : function(e){
34488         Roo.get(this.proxy).setDisplayed(false);
34489         var endPoint = Roo.lib.Event.getXY(e);
34490         if(this.overlay){
34491             this.overlay.hide();
34492         }
34493         var newSize;
34494         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34495             newSize = this.dragSpecs.startSize + 
34496                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34497                     endPoint[0] - this.dragSpecs.startPoint[0] :
34498                     this.dragSpecs.startPoint[0] - endPoint[0]
34499                 );
34500         }else{
34501             newSize = this.dragSpecs.startSize + 
34502                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34503                     endPoint[1] - this.dragSpecs.startPoint[1] :
34504                     this.dragSpecs.startPoint[1] - endPoint[1]
34505                 );
34506         }
34507         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34508         if(newSize != this.dragSpecs.startSize){
34509             if(this.fireEvent('beforeapply', this, newSize) !== false){
34510                 this.adapter.setElementSize(this, newSize);
34511                 this.fireEvent("moved", this, newSize);
34512                 this.fireEvent("resize", this, newSize);
34513             }
34514         }
34515     },
34516     
34517     /**
34518      * Get the adapter this SplitBar uses
34519      * @return The adapter object
34520      */
34521     getAdapter : function(){
34522         return this.adapter;
34523     },
34524     
34525     /**
34526      * Set the adapter this SplitBar uses
34527      * @param {Object} adapter A SplitBar adapter object
34528      */
34529     setAdapter : function(adapter){
34530         this.adapter = adapter;
34531         this.adapter.init(this);
34532     },
34533     
34534     /**
34535      * Gets the minimum size for the resizing element
34536      * @return {Number} The minimum size
34537      */
34538     getMinimumSize : function(){
34539         return this.minSize;
34540     },
34541     
34542     /**
34543      * Sets the minimum size for the resizing element
34544      * @param {Number} minSize The minimum size
34545      */
34546     setMinimumSize : function(minSize){
34547         this.minSize = minSize;
34548     },
34549     
34550     /**
34551      * Gets the maximum size for the resizing element
34552      * @return {Number} The maximum size
34553      */
34554     getMaximumSize : function(){
34555         return this.maxSize;
34556     },
34557     
34558     /**
34559      * Sets the maximum size for the resizing element
34560      * @param {Number} maxSize The maximum size
34561      */
34562     setMaximumSize : function(maxSize){
34563         this.maxSize = maxSize;
34564     },
34565     
34566     /**
34567      * Sets the initialize size for the resizing element
34568      * @param {Number} size The initial size
34569      */
34570     setCurrentSize : function(size){
34571         var oldAnimate = this.animate;
34572         this.animate = false;
34573         this.adapter.setElementSize(this, size);
34574         this.animate = oldAnimate;
34575     },
34576     
34577     /**
34578      * Destroy this splitbar. 
34579      * @param {Boolean} removeEl True to remove the element
34580      */
34581     destroy : function(removeEl){
34582         if(this.shim){
34583             this.shim.remove();
34584         }
34585         this.dd.unreg();
34586         this.proxy.parentNode.removeChild(this.proxy);
34587         if(removeEl){
34588             this.el.remove();
34589         }
34590     }
34591 });
34592
34593 /**
34594  * @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.
34595  */
34596 Roo.bootstrap.SplitBar.createProxy = function(dir){
34597     var proxy = new Roo.Element(document.createElement("div"));
34598     proxy.unselectable();
34599     var cls = 'roo-splitbar-proxy';
34600     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34601     document.body.appendChild(proxy.dom);
34602     return proxy.dom;
34603 };
34604
34605 /** 
34606  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34607  * Default Adapter. It assumes the splitter and resizing element are not positioned
34608  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34609  */
34610 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34611 };
34612
34613 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34614     // do nothing for now
34615     init : function(s){
34616     
34617     },
34618     /**
34619      * Called before drag operations to get the current size of the resizing element. 
34620      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34621      */
34622      getElementSize : function(s){
34623         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34624             return s.resizingEl.getWidth();
34625         }else{
34626             return s.resizingEl.getHeight();
34627         }
34628     },
34629     
34630     /**
34631      * Called after drag operations to set the size of the resizing element.
34632      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34633      * @param {Number} newSize The new size to set
34634      * @param {Function} onComplete A function to be invoked when resizing is complete
34635      */
34636     setElementSize : function(s, newSize, onComplete){
34637         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34638             if(!s.animate){
34639                 s.resizingEl.setWidth(newSize);
34640                 if(onComplete){
34641                     onComplete(s, newSize);
34642                 }
34643             }else{
34644                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34645             }
34646         }else{
34647             
34648             if(!s.animate){
34649                 s.resizingEl.setHeight(newSize);
34650                 if(onComplete){
34651                     onComplete(s, newSize);
34652                 }
34653             }else{
34654                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34655             }
34656         }
34657     }
34658 };
34659
34660 /** 
34661  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34662  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34663  * Adapter that  moves the splitter element to align with the resized sizing element. 
34664  * Used with an absolute positioned SplitBar.
34665  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34666  * document.body, make sure you assign an id to the body element.
34667  */
34668 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34669     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34670     this.container = Roo.get(container);
34671 };
34672
34673 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34674     init : function(s){
34675         this.basic.init(s);
34676     },
34677     
34678     getElementSize : function(s){
34679         return this.basic.getElementSize(s);
34680     },
34681     
34682     setElementSize : function(s, newSize, onComplete){
34683         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34684     },
34685     
34686     moveSplitter : function(s){
34687         var yes = Roo.bootstrap.SplitBar;
34688         switch(s.placement){
34689             case yes.LEFT:
34690                 s.el.setX(s.resizingEl.getRight());
34691                 break;
34692             case yes.RIGHT:
34693                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34694                 break;
34695             case yes.TOP:
34696                 s.el.setY(s.resizingEl.getBottom());
34697                 break;
34698             case yes.BOTTOM:
34699                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34700                 break;
34701         }
34702     }
34703 };
34704
34705 /**
34706  * Orientation constant - Create a vertical SplitBar
34707  * @static
34708  * @type Number
34709  */
34710 Roo.bootstrap.SplitBar.VERTICAL = 1;
34711
34712 /**
34713  * Orientation constant - Create a horizontal SplitBar
34714  * @static
34715  * @type Number
34716  */
34717 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34718
34719 /**
34720  * Placement constant - The resizing element is to the left of the splitter element
34721  * @static
34722  * @type Number
34723  */
34724 Roo.bootstrap.SplitBar.LEFT = 1;
34725
34726 /**
34727  * Placement constant - The resizing element is to the right of the splitter element
34728  * @static
34729  * @type Number
34730  */
34731 Roo.bootstrap.SplitBar.RIGHT = 2;
34732
34733 /**
34734  * Placement constant - The resizing element is positioned above the splitter element
34735  * @static
34736  * @type Number
34737  */
34738 Roo.bootstrap.SplitBar.TOP = 3;
34739
34740 /**
34741  * Placement constant - The resizing element is positioned under splitter element
34742  * @static
34743  * @type Number
34744  */
34745 Roo.bootstrap.SplitBar.BOTTOM = 4;
34746 Roo.namespace("Roo.bootstrap.layout");/*
34747  * Based on:
34748  * Ext JS Library 1.1.1
34749  * Copyright(c) 2006-2007, Ext JS, LLC.
34750  *
34751  * Originally Released Under LGPL - original licence link has changed is not relivant.
34752  *
34753  * Fork - LGPL
34754  * <script type="text/javascript">
34755  */
34756
34757 /**
34758  * @class Roo.bootstrap.layout.Manager
34759  * @extends Roo.bootstrap.Component
34760  * Base class for layout managers.
34761  */
34762 Roo.bootstrap.layout.Manager = function(config)
34763 {
34764     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34765
34766
34767
34768
34769
34770     /** false to disable window resize monitoring @type Boolean */
34771     this.monitorWindowResize = true;
34772     this.regions = {};
34773     this.addEvents({
34774         /**
34775          * @event layout
34776          * Fires when a layout is performed.
34777          * @param {Roo.LayoutManager} this
34778          */
34779         "layout" : true,
34780         /**
34781          * @event regionresized
34782          * Fires when the user resizes a region.
34783          * @param {Roo.LayoutRegion} region The resized region
34784          * @param {Number} newSize The new size (width for east/west, height for north/south)
34785          */
34786         "regionresized" : true,
34787         /**
34788          * @event regioncollapsed
34789          * Fires when a region is collapsed.
34790          * @param {Roo.LayoutRegion} region The collapsed region
34791          */
34792         "regioncollapsed" : true,
34793         /**
34794          * @event regionexpanded
34795          * Fires when a region is expanded.
34796          * @param {Roo.LayoutRegion} region The expanded region
34797          */
34798         "regionexpanded" : true
34799     });
34800     this.updating = false;
34801
34802     if (config.el) {
34803         this.el = Roo.get(config.el);
34804         this.initEvents();
34805     }
34806
34807 };
34808
34809 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34810
34811
34812     regions : null,
34813
34814     monitorWindowResize : true,
34815
34816
34817     updating : false,
34818
34819
34820     onRender : function(ct, position)
34821     {
34822         if(!this.el){
34823             this.el = Roo.get(ct);
34824             this.initEvents();
34825         }
34826         //this.fireEvent('render',this);
34827     },
34828
34829
34830     initEvents: function()
34831     {
34832
34833
34834         // ie scrollbar fix
34835         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34836             document.body.scroll = "no";
34837         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34838             this.el.position('relative');
34839         }
34840         this.id = this.el.id;
34841         this.el.addClass("roo-layout-container");
34842         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34843         if(this.el.dom != document.body ) {
34844             this.el.on('resize', this.layout,this);
34845             this.el.on('show', this.layout,this);
34846         }
34847
34848     },
34849
34850     /**
34851      * Returns true if this layout is currently being updated
34852      * @return {Boolean}
34853      */
34854     isUpdating : function(){
34855         return this.updating;
34856     },
34857
34858     /**
34859      * Suspend the LayoutManager from doing auto-layouts while
34860      * making multiple add or remove calls
34861      */
34862     beginUpdate : function(){
34863         this.updating = true;
34864     },
34865
34866     /**
34867      * Restore auto-layouts and optionally disable the manager from performing a layout
34868      * @param {Boolean} noLayout true to disable a layout update
34869      */
34870     endUpdate : function(noLayout){
34871         this.updating = false;
34872         if(!noLayout){
34873             this.layout();
34874         }
34875     },
34876
34877     layout: function(){
34878         // abstract...
34879     },
34880
34881     onRegionResized : function(region, newSize){
34882         this.fireEvent("regionresized", region, newSize);
34883         this.layout();
34884     },
34885
34886     onRegionCollapsed : function(region){
34887         this.fireEvent("regioncollapsed", region);
34888     },
34889
34890     onRegionExpanded : function(region){
34891         this.fireEvent("regionexpanded", region);
34892     },
34893
34894     /**
34895      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34896      * performs box-model adjustments.
34897      * @return {Object} The size as an object {width: (the width), height: (the height)}
34898      */
34899     getViewSize : function()
34900     {
34901         var size;
34902         if(this.el.dom != document.body){
34903             size = this.el.getSize();
34904         }else{
34905             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34906         }
34907         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34908         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34909         return size;
34910     },
34911
34912     /**
34913      * Returns the Element this layout is bound to.
34914      * @return {Roo.Element}
34915      */
34916     getEl : function(){
34917         return this.el;
34918     },
34919
34920     /**
34921      * Returns the specified region.
34922      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34923      * @return {Roo.LayoutRegion}
34924      */
34925     getRegion : function(target){
34926         return this.regions[target.toLowerCase()];
34927     },
34928
34929     onWindowResize : function(){
34930         if(this.monitorWindowResize){
34931             this.layout();
34932         }
34933     }
34934 });
34935 /*
34936  * Based on:
34937  * Ext JS Library 1.1.1
34938  * Copyright(c) 2006-2007, Ext JS, LLC.
34939  *
34940  * Originally Released Under LGPL - original licence link has changed is not relivant.
34941  *
34942  * Fork - LGPL
34943  * <script type="text/javascript">
34944  */
34945 /**
34946  * @class Roo.bootstrap.layout.Border
34947  * @extends Roo.bootstrap.layout.Manager
34948  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34949  * please see: examples/bootstrap/nested.html<br><br>
34950  
34951 <b>The container the layout is rendered into can be either the body element or any other element.
34952 If it is not the body element, the container needs to either be an absolute positioned element,
34953 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34954 the container size if it is not the body element.</b>
34955
34956 * @constructor
34957 * Create a new Border
34958 * @param {Object} config Configuration options
34959  */
34960 Roo.bootstrap.layout.Border = function(config){
34961     config = config || {};
34962     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34963     
34964     
34965     
34966     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34967         if(config[region]){
34968             config[region].region = region;
34969             this.addRegion(config[region]);
34970         }
34971     },this);
34972     
34973 };
34974
34975 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34976
34977 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34978     /**
34979      * Creates and adds a new region if it doesn't already exist.
34980      * @param {String} target The target region key (north, south, east, west or center).
34981      * @param {Object} config The regions config object
34982      * @return {BorderLayoutRegion} The new region
34983      */
34984     addRegion : function(config)
34985     {
34986         if(!this.regions[config.region]){
34987             var r = this.factory(config);
34988             this.bindRegion(r);
34989         }
34990         return this.regions[config.region];
34991     },
34992
34993     // private (kinda)
34994     bindRegion : function(r){
34995         this.regions[r.config.region] = r;
34996         
34997         r.on("visibilitychange",    this.layout, this);
34998         r.on("paneladded",          this.layout, this);
34999         r.on("panelremoved",        this.layout, this);
35000         r.on("invalidated",         this.layout, this);
35001         r.on("resized",             this.onRegionResized, this);
35002         r.on("collapsed",           this.onRegionCollapsed, this);
35003         r.on("expanded",            this.onRegionExpanded, this);
35004     },
35005
35006     /**
35007      * Performs a layout update.
35008      */
35009     layout : function()
35010     {
35011         if(this.updating) {
35012             return;
35013         }
35014         
35015         // render all the rebions if they have not been done alreayd?
35016         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35017             if(this.regions[region] && !this.regions[region].bodyEl){
35018                 this.regions[region].onRender(this.el)
35019             }
35020         },this);
35021         
35022         var size = this.getViewSize();
35023         var w = size.width;
35024         var h = size.height;
35025         var centerW = w;
35026         var centerH = h;
35027         var centerY = 0;
35028         var centerX = 0;
35029         //var x = 0, y = 0;
35030
35031         var rs = this.regions;
35032         var north = rs["north"];
35033         var south = rs["south"]; 
35034         var west = rs["west"];
35035         var east = rs["east"];
35036         var center = rs["center"];
35037         //if(this.hideOnLayout){ // not supported anymore
35038             //c.el.setStyle("display", "none");
35039         //}
35040         if(north && north.isVisible()){
35041             var b = north.getBox();
35042             var m = north.getMargins();
35043             b.width = w - (m.left+m.right);
35044             b.x = m.left;
35045             b.y = m.top;
35046             centerY = b.height + b.y + m.bottom;
35047             centerH -= centerY;
35048             north.updateBox(this.safeBox(b));
35049         }
35050         if(south && south.isVisible()){
35051             var b = south.getBox();
35052             var m = south.getMargins();
35053             b.width = w - (m.left+m.right);
35054             b.x = m.left;
35055             var totalHeight = (b.height + m.top + m.bottom);
35056             b.y = h - totalHeight + m.top;
35057             centerH -= totalHeight;
35058             south.updateBox(this.safeBox(b));
35059         }
35060         if(west && west.isVisible()){
35061             var b = west.getBox();
35062             var m = west.getMargins();
35063             b.height = centerH - (m.top+m.bottom);
35064             b.x = m.left;
35065             b.y = centerY + m.top;
35066             var totalWidth = (b.width + m.left + m.right);
35067             centerX += totalWidth;
35068             centerW -= totalWidth;
35069             west.updateBox(this.safeBox(b));
35070         }
35071         if(east && east.isVisible()){
35072             var b = east.getBox();
35073             var m = east.getMargins();
35074             b.height = centerH - (m.top+m.bottom);
35075             var totalWidth = (b.width + m.left + m.right);
35076             b.x = w - totalWidth + m.left;
35077             b.y = centerY + m.top;
35078             centerW -= totalWidth;
35079             east.updateBox(this.safeBox(b));
35080         }
35081         if(center){
35082             var m = center.getMargins();
35083             var centerBox = {
35084                 x: centerX + m.left,
35085                 y: centerY + m.top,
35086                 width: centerW - (m.left+m.right),
35087                 height: centerH - (m.top+m.bottom)
35088             };
35089             //if(this.hideOnLayout){
35090                 //center.el.setStyle("display", "block");
35091             //}
35092             center.updateBox(this.safeBox(centerBox));
35093         }
35094         this.el.repaint();
35095         this.fireEvent("layout", this);
35096     },
35097
35098     // private
35099     safeBox : function(box){
35100         box.width = Math.max(0, box.width);
35101         box.height = Math.max(0, box.height);
35102         return box;
35103     },
35104
35105     /**
35106      * Adds a ContentPanel (or subclass) to this layout.
35107      * @param {String} target The target region key (north, south, east, west or center).
35108      * @param {Roo.ContentPanel} panel The panel to add
35109      * @return {Roo.ContentPanel} The added panel
35110      */
35111     add : function(target, panel){
35112          
35113         target = target.toLowerCase();
35114         return this.regions[target].add(panel);
35115     },
35116
35117     /**
35118      * Remove a ContentPanel (or subclass) to this layout.
35119      * @param {String} target The target region key (north, south, east, west or center).
35120      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35121      * @return {Roo.ContentPanel} The removed panel
35122      */
35123     remove : function(target, panel){
35124         target = target.toLowerCase();
35125         return this.regions[target].remove(panel);
35126     },
35127
35128     /**
35129      * Searches all regions for a panel with the specified id
35130      * @param {String} panelId
35131      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35132      */
35133     findPanel : function(panelId){
35134         var rs = this.regions;
35135         for(var target in rs){
35136             if(typeof rs[target] != "function"){
35137                 var p = rs[target].getPanel(panelId);
35138                 if(p){
35139                     return p;
35140                 }
35141             }
35142         }
35143         return null;
35144     },
35145
35146     /**
35147      * Searches all regions for a panel with the specified id and activates (shows) it.
35148      * @param {String/ContentPanel} panelId The panels id or the panel itself
35149      * @return {Roo.ContentPanel} The shown panel or null
35150      */
35151     showPanel : function(panelId) {
35152       var rs = this.regions;
35153       for(var target in rs){
35154          var r = rs[target];
35155          if(typeof r != "function"){
35156             if(r.hasPanel(panelId)){
35157                return r.showPanel(panelId);
35158             }
35159          }
35160       }
35161       return null;
35162    },
35163
35164    /**
35165      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35166      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35167      */
35168    /*
35169     restoreState : function(provider){
35170         if(!provider){
35171             provider = Roo.state.Manager;
35172         }
35173         var sm = new Roo.LayoutStateManager();
35174         sm.init(this, provider);
35175     },
35176 */
35177  
35178  
35179     /**
35180      * Adds a xtype elements to the layout.
35181      * <pre><code>
35182
35183 layout.addxtype({
35184        xtype : 'ContentPanel',
35185        region: 'west',
35186        items: [ .... ]
35187    }
35188 );
35189
35190 layout.addxtype({
35191         xtype : 'NestedLayoutPanel',
35192         region: 'west',
35193         layout: {
35194            center: { },
35195            west: { }   
35196         },
35197         items : [ ... list of content panels or nested layout panels.. ]
35198    }
35199 );
35200 </code></pre>
35201      * @param {Object} cfg Xtype definition of item to add.
35202      */
35203     addxtype : function(cfg)
35204     {
35205         // basically accepts a pannel...
35206         // can accept a layout region..!?!?
35207         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35208         
35209         
35210         // theory?  children can only be panels??
35211         
35212         //if (!cfg.xtype.match(/Panel$/)) {
35213         //    return false;
35214         //}
35215         var ret = false;
35216         
35217         if (typeof(cfg.region) == 'undefined') {
35218             Roo.log("Failed to add Panel, region was not set");
35219             Roo.log(cfg);
35220             return false;
35221         }
35222         var region = cfg.region;
35223         delete cfg.region;
35224         
35225           
35226         var xitems = [];
35227         if (cfg.items) {
35228             xitems = cfg.items;
35229             delete cfg.items;
35230         }
35231         var nb = false;
35232         
35233         switch(cfg.xtype) 
35234         {
35235             case 'Content':  // ContentPanel (el, cfg)
35236             case 'Scroll':  // ContentPanel (el, cfg)
35237             case 'View': 
35238                 cfg.autoCreate = true;
35239                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35240                 //} else {
35241                 //    var el = this.el.createChild();
35242                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35243                 //}
35244                 
35245                 this.add(region, ret);
35246                 break;
35247             
35248             /*
35249             case 'TreePanel': // our new panel!
35250                 cfg.el = this.el.createChild();
35251                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35252                 this.add(region, ret);
35253                 break;
35254             */
35255             
35256             case 'Nest': 
35257                 // create a new Layout (which is  a Border Layout...
35258                 
35259                 var clayout = cfg.layout;
35260                 clayout.el  = this.el.createChild();
35261                 clayout.items   = clayout.items  || [];
35262                 
35263                 delete cfg.layout;
35264                 
35265                 // replace this exitems with the clayout ones..
35266                 xitems = clayout.items;
35267                  
35268                 // force background off if it's in center...
35269                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35270                     cfg.background = false;
35271                 }
35272                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35273                 
35274                 
35275                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35276                 //console.log('adding nested layout panel '  + cfg.toSource());
35277                 this.add(region, ret);
35278                 nb = {}; /// find first...
35279                 break;
35280             
35281             case 'Grid':
35282                 
35283                 // needs grid and region
35284                 
35285                 //var el = this.getRegion(region).el.createChild();
35286                 /*
35287                  *var el = this.el.createChild();
35288                 // create the grid first...
35289                 cfg.grid.container = el;
35290                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35291                 */
35292                 
35293                 if (region == 'center' && this.active ) {
35294                     cfg.background = false;
35295                 }
35296                 
35297                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35298                 
35299                 this.add(region, ret);
35300                 /*
35301                 if (cfg.background) {
35302                     // render grid on panel activation (if panel background)
35303                     ret.on('activate', function(gp) {
35304                         if (!gp.grid.rendered) {
35305                     //        gp.grid.render(el);
35306                         }
35307                     });
35308                 } else {
35309                   //  cfg.grid.render(el);
35310                 }
35311                 */
35312                 break;
35313            
35314            
35315             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35316                 // it was the old xcomponent building that caused this before.
35317                 // espeically if border is the top element in the tree.
35318                 ret = this;
35319                 break; 
35320                 
35321                     
35322                 
35323                 
35324                 
35325             default:
35326                 /*
35327                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35328                     
35329                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35330                     this.add(region, ret);
35331                 } else {
35332                 */
35333                     Roo.log(cfg);
35334                     throw "Can not add '" + cfg.xtype + "' to Border";
35335                     return null;
35336              
35337                                 
35338              
35339         }
35340         this.beginUpdate();
35341         // add children..
35342         var region = '';
35343         var abn = {};
35344         Roo.each(xitems, function(i)  {
35345             region = nb && i.region ? i.region : false;
35346             
35347             var add = ret.addxtype(i);
35348            
35349             if (region) {
35350                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35351                 if (!i.background) {
35352                     abn[region] = nb[region] ;
35353                 }
35354             }
35355             
35356         });
35357         this.endUpdate();
35358
35359         // make the last non-background panel active..
35360         //if (nb) { Roo.log(abn); }
35361         if (nb) {
35362             
35363             for(var r in abn) {
35364                 region = this.getRegion(r);
35365                 if (region) {
35366                     // tried using nb[r], but it does not work..
35367                      
35368                     region.showPanel(abn[r]);
35369                    
35370                 }
35371             }
35372         }
35373         return ret;
35374         
35375     },
35376     
35377     
35378 // private
35379     factory : function(cfg)
35380     {
35381         
35382         var validRegions = Roo.bootstrap.layout.Border.regions;
35383
35384         var target = cfg.region;
35385         cfg.mgr = this;
35386         
35387         var r = Roo.bootstrap.layout;
35388         Roo.log(target);
35389         switch(target){
35390             case "north":
35391                 return new r.North(cfg);
35392             case "south":
35393                 return new r.South(cfg);
35394             case "east":
35395                 return new r.East(cfg);
35396             case "west":
35397                 return new r.West(cfg);
35398             case "center":
35399                 return new r.Center(cfg);
35400         }
35401         throw 'Layout region "'+target+'" not supported.';
35402     }
35403     
35404     
35405 });
35406  /*
35407  * Based on:
35408  * Ext JS Library 1.1.1
35409  * Copyright(c) 2006-2007, Ext JS, LLC.
35410  *
35411  * Originally Released Under LGPL - original licence link has changed is not relivant.
35412  *
35413  * Fork - LGPL
35414  * <script type="text/javascript">
35415  */
35416  
35417 /**
35418  * @class Roo.bootstrap.layout.Basic
35419  * @extends Roo.util.Observable
35420  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35421  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35422  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35423  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35424  * @cfg {string}   region  the region that it inhabits..
35425  * @cfg {bool}   skipConfig skip config?
35426  * 
35427
35428  */
35429 Roo.bootstrap.layout.Basic = function(config){
35430     
35431     this.mgr = config.mgr;
35432     
35433     this.position = config.region;
35434     
35435     var skipConfig = config.skipConfig;
35436     
35437     this.events = {
35438         /**
35439          * @scope Roo.BasicLayoutRegion
35440          */
35441         
35442         /**
35443          * @event beforeremove
35444          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35445          * @param {Roo.LayoutRegion} this
35446          * @param {Roo.ContentPanel} panel The panel
35447          * @param {Object} e The cancel event object
35448          */
35449         "beforeremove" : true,
35450         /**
35451          * @event invalidated
35452          * Fires when the layout for this region is changed.
35453          * @param {Roo.LayoutRegion} this
35454          */
35455         "invalidated" : true,
35456         /**
35457          * @event visibilitychange
35458          * Fires when this region is shown or hidden 
35459          * @param {Roo.LayoutRegion} this
35460          * @param {Boolean} visibility true or false
35461          */
35462         "visibilitychange" : true,
35463         /**
35464          * @event paneladded
35465          * Fires when a panel is added. 
35466          * @param {Roo.LayoutRegion} this
35467          * @param {Roo.ContentPanel} panel The panel
35468          */
35469         "paneladded" : true,
35470         /**
35471          * @event panelremoved
35472          * Fires when a panel is removed. 
35473          * @param {Roo.LayoutRegion} this
35474          * @param {Roo.ContentPanel} panel The panel
35475          */
35476         "panelremoved" : true,
35477         /**
35478          * @event beforecollapse
35479          * Fires when this region before collapse.
35480          * @param {Roo.LayoutRegion} this
35481          */
35482         "beforecollapse" : true,
35483         /**
35484          * @event collapsed
35485          * Fires when this region is collapsed.
35486          * @param {Roo.LayoutRegion} this
35487          */
35488         "collapsed" : true,
35489         /**
35490          * @event expanded
35491          * Fires when this region is expanded.
35492          * @param {Roo.LayoutRegion} this
35493          */
35494         "expanded" : true,
35495         /**
35496          * @event slideshow
35497          * Fires when this region is slid into view.
35498          * @param {Roo.LayoutRegion} this
35499          */
35500         "slideshow" : true,
35501         /**
35502          * @event slidehide
35503          * Fires when this region slides out of view. 
35504          * @param {Roo.LayoutRegion} this
35505          */
35506         "slidehide" : true,
35507         /**
35508          * @event panelactivated
35509          * Fires when a panel is activated. 
35510          * @param {Roo.LayoutRegion} this
35511          * @param {Roo.ContentPanel} panel The activated panel
35512          */
35513         "panelactivated" : true,
35514         /**
35515          * @event resized
35516          * Fires when the user resizes this region. 
35517          * @param {Roo.LayoutRegion} this
35518          * @param {Number} newSize The new size (width for east/west, height for north/south)
35519          */
35520         "resized" : true
35521     };
35522     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35523     this.panels = new Roo.util.MixedCollection();
35524     this.panels.getKey = this.getPanelId.createDelegate(this);
35525     this.box = null;
35526     this.activePanel = null;
35527     // ensure listeners are added...
35528     
35529     if (config.listeners || config.events) {
35530         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35531             listeners : config.listeners || {},
35532             events : config.events || {}
35533         });
35534     }
35535     
35536     if(skipConfig !== true){
35537         this.applyConfig(config);
35538     }
35539 };
35540
35541 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35542 {
35543     getPanelId : function(p){
35544         return p.getId();
35545     },
35546     
35547     applyConfig : function(config){
35548         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35549         this.config = config;
35550         
35551     },
35552     
35553     /**
35554      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35555      * the width, for horizontal (north, south) the height.
35556      * @param {Number} newSize The new width or height
35557      */
35558     resizeTo : function(newSize){
35559         var el = this.el ? this.el :
35560                  (this.activePanel ? this.activePanel.getEl() : null);
35561         if(el){
35562             switch(this.position){
35563                 case "east":
35564                 case "west":
35565                     el.setWidth(newSize);
35566                     this.fireEvent("resized", this, newSize);
35567                 break;
35568                 case "north":
35569                 case "south":
35570                     el.setHeight(newSize);
35571                     this.fireEvent("resized", this, newSize);
35572                 break;                
35573             }
35574         }
35575     },
35576     
35577     getBox : function(){
35578         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35579     },
35580     
35581     getMargins : function(){
35582         return this.margins;
35583     },
35584     
35585     updateBox : function(box){
35586         this.box = box;
35587         var el = this.activePanel.getEl();
35588         el.dom.style.left = box.x + "px";
35589         el.dom.style.top = box.y + "px";
35590         this.activePanel.setSize(box.width, box.height);
35591     },
35592     
35593     /**
35594      * Returns the container element for this region.
35595      * @return {Roo.Element}
35596      */
35597     getEl : function(){
35598         return this.activePanel;
35599     },
35600     
35601     /**
35602      * Returns true if this region is currently visible.
35603      * @return {Boolean}
35604      */
35605     isVisible : function(){
35606         return this.activePanel ? true : false;
35607     },
35608     
35609     setActivePanel : function(panel){
35610         panel = this.getPanel(panel);
35611         if(this.activePanel && this.activePanel != panel){
35612             this.activePanel.setActiveState(false);
35613             this.activePanel.getEl().setLeftTop(-10000,-10000);
35614         }
35615         this.activePanel = panel;
35616         panel.setActiveState(true);
35617         if(this.box){
35618             panel.setSize(this.box.width, this.box.height);
35619         }
35620         this.fireEvent("panelactivated", this, panel);
35621         this.fireEvent("invalidated");
35622     },
35623     
35624     /**
35625      * Show the specified panel.
35626      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35627      * @return {Roo.ContentPanel} The shown panel or null
35628      */
35629     showPanel : function(panel){
35630         panel = this.getPanel(panel);
35631         if(panel){
35632             this.setActivePanel(panel);
35633         }
35634         return panel;
35635     },
35636     
35637     /**
35638      * Get the active panel for this region.
35639      * @return {Roo.ContentPanel} The active panel or null
35640      */
35641     getActivePanel : function(){
35642         return this.activePanel;
35643     },
35644     
35645     /**
35646      * Add the passed ContentPanel(s)
35647      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35648      * @return {Roo.ContentPanel} The panel added (if only one was added)
35649      */
35650     add : function(panel){
35651         if(arguments.length > 1){
35652             for(var i = 0, len = arguments.length; i < len; i++) {
35653                 this.add(arguments[i]);
35654             }
35655             return null;
35656         }
35657         if(this.hasPanel(panel)){
35658             this.showPanel(panel);
35659             return panel;
35660         }
35661         var el = panel.getEl();
35662         if(el.dom.parentNode != this.mgr.el.dom){
35663             this.mgr.el.dom.appendChild(el.dom);
35664         }
35665         if(panel.setRegion){
35666             panel.setRegion(this);
35667         }
35668         this.panels.add(panel);
35669         el.setStyle("position", "absolute");
35670         if(!panel.background){
35671             this.setActivePanel(panel);
35672             if(this.config.initialSize && this.panels.getCount()==1){
35673                 this.resizeTo(this.config.initialSize);
35674             }
35675         }
35676         this.fireEvent("paneladded", this, panel);
35677         return panel;
35678     },
35679     
35680     /**
35681      * Returns true if the panel is in this region.
35682      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35683      * @return {Boolean}
35684      */
35685     hasPanel : function(panel){
35686         if(typeof panel == "object"){ // must be panel obj
35687             panel = panel.getId();
35688         }
35689         return this.getPanel(panel) ? true : false;
35690     },
35691     
35692     /**
35693      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35694      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35695      * @param {Boolean} preservePanel Overrides the config preservePanel option
35696      * @return {Roo.ContentPanel} The panel that was removed
35697      */
35698     remove : function(panel, preservePanel){
35699         panel = this.getPanel(panel);
35700         if(!panel){
35701             return null;
35702         }
35703         var e = {};
35704         this.fireEvent("beforeremove", this, panel, e);
35705         if(e.cancel === true){
35706             return null;
35707         }
35708         var panelId = panel.getId();
35709         this.panels.removeKey(panelId);
35710         return panel;
35711     },
35712     
35713     /**
35714      * Returns the panel specified or null if it's not in this region.
35715      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35716      * @return {Roo.ContentPanel}
35717      */
35718     getPanel : function(id){
35719         if(typeof id == "object"){ // must be panel obj
35720             return id;
35721         }
35722         return this.panels.get(id);
35723     },
35724     
35725     /**
35726      * Returns this regions position (north/south/east/west/center).
35727      * @return {String} 
35728      */
35729     getPosition: function(){
35730         return this.position;    
35731     }
35732 });/*
35733  * Based on:
35734  * Ext JS Library 1.1.1
35735  * Copyright(c) 2006-2007, Ext JS, LLC.
35736  *
35737  * Originally Released Under LGPL - original licence link has changed is not relivant.
35738  *
35739  * Fork - LGPL
35740  * <script type="text/javascript">
35741  */
35742  
35743 /**
35744  * @class Roo.bootstrap.layout.Region
35745  * @extends Roo.bootstrap.layout.Basic
35746  * This class represents a region in a layout manager.
35747  
35748  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35749  * @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})
35750  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35751  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35752  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35753  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35754  * @cfg {String}    title           The title for the region (overrides panel titles)
35755  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35756  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35757  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35758  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35759  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35760  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35761  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35762  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35763  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35764  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35765
35766  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35767  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35768  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35769  * @cfg {Number}    width           For East/West panels
35770  * @cfg {Number}    height          For North/South panels
35771  * @cfg {Boolean}   split           To show the splitter
35772  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35773  * 
35774  * @cfg {string}   cls             Extra CSS classes to add to region
35775  * 
35776  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35777  * @cfg {string}   region  the region that it inhabits..
35778  *
35779
35780  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35781  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35782
35783  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35784  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35785  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35786  */
35787 Roo.bootstrap.layout.Region = function(config)
35788 {
35789     this.applyConfig(config);
35790
35791     var mgr = config.mgr;
35792     var pos = config.region;
35793     config.skipConfig = true;
35794     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35795     
35796     if (mgr.el) {
35797         this.onRender(mgr.el);   
35798     }
35799      
35800     this.visible = true;
35801     this.collapsed = false;
35802     this.unrendered_panels = [];
35803 };
35804
35805 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35806
35807     position: '', // set by wrapper (eg. north/south etc..)
35808     unrendered_panels : null,  // unrendered panels.
35809     createBody : function(){
35810         /** This region's body element 
35811         * @type Roo.Element */
35812         this.bodyEl = this.el.createChild({
35813                 tag: "div",
35814                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35815         });
35816     },
35817
35818     onRender: function(ctr, pos)
35819     {
35820         var dh = Roo.DomHelper;
35821         /** This region's container element 
35822         * @type Roo.Element */
35823         this.el = dh.append(ctr.dom, {
35824                 tag: "div",
35825                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35826             }, true);
35827         /** This region's title element 
35828         * @type Roo.Element */
35829     
35830         this.titleEl = dh.append(this.el.dom,
35831             {
35832                     tag: "div",
35833                     unselectable: "on",
35834                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35835                     children:[
35836                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35837                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35838                     ]}, true);
35839         
35840         this.titleEl.enableDisplayMode();
35841         /** This region's title text element 
35842         * @type HTMLElement */
35843         this.titleTextEl = this.titleEl.dom.firstChild;
35844         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35845         /*
35846         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35847         this.closeBtn.enableDisplayMode();
35848         this.closeBtn.on("click", this.closeClicked, this);
35849         this.closeBtn.hide();
35850     */
35851         this.createBody(this.config);
35852         if(this.config.hideWhenEmpty){
35853             this.hide();
35854             this.on("paneladded", this.validateVisibility, this);
35855             this.on("panelremoved", this.validateVisibility, this);
35856         }
35857         if(this.autoScroll){
35858             this.bodyEl.setStyle("overflow", "auto");
35859         }else{
35860             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35861         }
35862         //if(c.titlebar !== false){
35863             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35864                 this.titleEl.hide();
35865             }else{
35866                 this.titleEl.show();
35867                 if(this.config.title){
35868                     this.titleTextEl.innerHTML = this.config.title;
35869                 }
35870             }
35871         //}
35872         if(this.config.collapsed){
35873             this.collapse(true);
35874         }
35875         if(this.config.hidden){
35876             this.hide();
35877         }
35878         
35879         if (this.unrendered_panels && this.unrendered_panels.length) {
35880             for (var i =0;i< this.unrendered_panels.length; i++) {
35881                 this.add(this.unrendered_panels[i]);
35882             }
35883             this.unrendered_panels = null;
35884             
35885         }
35886         
35887     },
35888     
35889     applyConfig : function(c)
35890     {
35891         /*
35892          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35893             var dh = Roo.DomHelper;
35894             if(c.titlebar !== false){
35895                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35896                 this.collapseBtn.on("click", this.collapse, this);
35897                 this.collapseBtn.enableDisplayMode();
35898                 /*
35899                 if(c.showPin === true || this.showPin){
35900                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35901                     this.stickBtn.enableDisplayMode();
35902                     this.stickBtn.on("click", this.expand, this);
35903                     this.stickBtn.hide();
35904                 }
35905                 
35906             }
35907             */
35908             /** This region's collapsed element
35909             * @type Roo.Element */
35910             /*
35911              *
35912             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35913                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35914             ]}, true);
35915             
35916             if(c.floatable !== false){
35917                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35918                this.collapsedEl.on("click", this.collapseClick, this);
35919             }
35920
35921             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35922                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35923                    id: "message", unselectable: "on", style:{"float":"left"}});
35924                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35925              }
35926             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35927             this.expandBtn.on("click", this.expand, this);
35928             
35929         }
35930         
35931         if(this.collapseBtn){
35932             this.collapseBtn.setVisible(c.collapsible == true);
35933         }
35934         
35935         this.cmargins = c.cmargins || this.cmargins ||
35936                          (this.position == "west" || this.position == "east" ?
35937                              {top: 0, left: 2, right:2, bottom: 0} :
35938                              {top: 2, left: 0, right:0, bottom: 2});
35939         */
35940         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35941         
35942         
35943         this.bottomTabs = c.tabPosition != "top";
35944         
35945         this.autoScroll = c.autoScroll || false;
35946         
35947         
35948        
35949         
35950         this.duration = c.duration || .30;
35951         this.slideDuration = c.slideDuration || .45;
35952         this.config = c;
35953        
35954     },
35955     /**
35956      * Returns true if this region is currently visible.
35957      * @return {Boolean}
35958      */
35959     isVisible : function(){
35960         return this.visible;
35961     },
35962
35963     /**
35964      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35965      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35966      */
35967     //setCollapsedTitle : function(title){
35968     //    title = title || "&#160;";
35969      //   if(this.collapsedTitleTextEl){
35970       //      this.collapsedTitleTextEl.innerHTML = title;
35971        // }
35972     //},
35973
35974     getBox : function(){
35975         var b;
35976       //  if(!this.collapsed){
35977             b = this.el.getBox(false, true);
35978        // }else{
35979           //  b = this.collapsedEl.getBox(false, true);
35980         //}
35981         return b;
35982     },
35983
35984     getMargins : function(){
35985         return this.margins;
35986         //return this.collapsed ? this.cmargins : this.margins;
35987     },
35988 /*
35989     highlight : function(){
35990         this.el.addClass("x-layout-panel-dragover");
35991     },
35992
35993     unhighlight : function(){
35994         this.el.removeClass("x-layout-panel-dragover");
35995     },
35996 */
35997     updateBox : function(box)
35998     {
35999         if (!this.bodyEl) {
36000             return; // not rendered yet..
36001         }
36002         
36003         this.box = box;
36004         if(!this.collapsed){
36005             this.el.dom.style.left = box.x + "px";
36006             this.el.dom.style.top = box.y + "px";
36007             this.updateBody(box.width, box.height);
36008         }else{
36009             this.collapsedEl.dom.style.left = box.x + "px";
36010             this.collapsedEl.dom.style.top = box.y + "px";
36011             this.collapsedEl.setSize(box.width, box.height);
36012         }
36013         if(this.tabs){
36014             this.tabs.autoSizeTabs();
36015         }
36016     },
36017
36018     updateBody : function(w, h)
36019     {
36020         if(w !== null){
36021             this.el.setWidth(w);
36022             w -= this.el.getBorderWidth("rl");
36023             if(this.config.adjustments){
36024                 w += this.config.adjustments[0];
36025             }
36026         }
36027         if(h !== null && h > 0){
36028             this.el.setHeight(h);
36029             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36030             h -= this.el.getBorderWidth("tb");
36031             if(this.config.adjustments){
36032                 h += this.config.adjustments[1];
36033             }
36034             this.bodyEl.setHeight(h);
36035             if(this.tabs){
36036                 h = this.tabs.syncHeight(h);
36037             }
36038         }
36039         if(this.panelSize){
36040             w = w !== null ? w : this.panelSize.width;
36041             h = h !== null ? h : this.panelSize.height;
36042         }
36043         if(this.activePanel){
36044             var el = this.activePanel.getEl();
36045             w = w !== null ? w : el.getWidth();
36046             h = h !== null ? h : el.getHeight();
36047             this.panelSize = {width: w, height: h};
36048             this.activePanel.setSize(w, h);
36049         }
36050         if(Roo.isIE && this.tabs){
36051             this.tabs.el.repaint();
36052         }
36053     },
36054
36055     /**
36056      * Returns the container element for this region.
36057      * @return {Roo.Element}
36058      */
36059     getEl : function(){
36060         return this.el;
36061     },
36062
36063     /**
36064      * Hides this region.
36065      */
36066     hide : function(){
36067         //if(!this.collapsed){
36068             this.el.dom.style.left = "-2000px";
36069             this.el.hide();
36070         //}else{
36071          //   this.collapsedEl.dom.style.left = "-2000px";
36072          //   this.collapsedEl.hide();
36073        // }
36074         this.visible = false;
36075         this.fireEvent("visibilitychange", this, false);
36076     },
36077
36078     /**
36079      * Shows this region if it was previously hidden.
36080      */
36081     show : function(){
36082         //if(!this.collapsed){
36083             this.el.show();
36084         //}else{
36085         //    this.collapsedEl.show();
36086        // }
36087         this.visible = true;
36088         this.fireEvent("visibilitychange", this, true);
36089     },
36090 /*
36091     closeClicked : function(){
36092         if(this.activePanel){
36093             this.remove(this.activePanel);
36094         }
36095     },
36096
36097     collapseClick : function(e){
36098         if(this.isSlid){
36099            e.stopPropagation();
36100            this.slideIn();
36101         }else{
36102            e.stopPropagation();
36103            this.slideOut();
36104         }
36105     },
36106 */
36107     /**
36108      * Collapses this region.
36109      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36110      */
36111     /*
36112     collapse : function(skipAnim, skipCheck = false){
36113         if(this.collapsed) {
36114             return;
36115         }
36116         
36117         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36118             
36119             this.collapsed = true;
36120             if(this.split){
36121                 this.split.el.hide();
36122             }
36123             if(this.config.animate && skipAnim !== true){
36124                 this.fireEvent("invalidated", this);
36125                 this.animateCollapse();
36126             }else{
36127                 this.el.setLocation(-20000,-20000);
36128                 this.el.hide();
36129                 this.collapsedEl.show();
36130                 this.fireEvent("collapsed", this);
36131                 this.fireEvent("invalidated", this);
36132             }
36133         }
36134         
36135     },
36136 */
36137     animateCollapse : function(){
36138         // overridden
36139     },
36140
36141     /**
36142      * Expands this region if it was previously collapsed.
36143      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36144      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36145      */
36146     /*
36147     expand : function(e, skipAnim){
36148         if(e) {
36149             e.stopPropagation();
36150         }
36151         if(!this.collapsed || this.el.hasActiveFx()) {
36152             return;
36153         }
36154         if(this.isSlid){
36155             this.afterSlideIn();
36156             skipAnim = true;
36157         }
36158         this.collapsed = false;
36159         if(this.config.animate && skipAnim !== true){
36160             this.animateExpand();
36161         }else{
36162             this.el.show();
36163             if(this.split){
36164                 this.split.el.show();
36165             }
36166             this.collapsedEl.setLocation(-2000,-2000);
36167             this.collapsedEl.hide();
36168             this.fireEvent("invalidated", this);
36169             this.fireEvent("expanded", this);
36170         }
36171     },
36172 */
36173     animateExpand : function(){
36174         // overridden
36175     },
36176
36177     initTabs : function()
36178     {
36179         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36180         
36181         var ts = new Roo.bootstrap.panel.Tabs({
36182                 el: this.bodyEl.dom,
36183                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36184                 disableTooltips: this.config.disableTabTips,
36185                 toolbar : this.config.toolbar
36186             });
36187         
36188         if(this.config.hideTabs){
36189             ts.stripWrap.setDisplayed(false);
36190         }
36191         this.tabs = ts;
36192         ts.resizeTabs = this.config.resizeTabs === true;
36193         ts.minTabWidth = this.config.minTabWidth || 40;
36194         ts.maxTabWidth = this.config.maxTabWidth || 250;
36195         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36196         ts.monitorResize = false;
36197         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36198         ts.bodyEl.addClass('roo-layout-tabs-body');
36199         this.panels.each(this.initPanelAsTab, this);
36200     },
36201
36202     initPanelAsTab : function(panel){
36203         var ti = this.tabs.addTab(
36204             panel.getEl().id,
36205             panel.getTitle(),
36206             null,
36207             this.config.closeOnTab && panel.isClosable(),
36208             panel.tpl
36209         );
36210         if(panel.tabTip !== undefined){
36211             ti.setTooltip(panel.tabTip);
36212         }
36213         ti.on("activate", function(){
36214               this.setActivePanel(panel);
36215         }, this);
36216         
36217         if(this.config.closeOnTab){
36218             ti.on("beforeclose", function(t, e){
36219                 e.cancel = true;
36220                 this.remove(panel);
36221             }, this);
36222         }
36223         
36224         panel.tabItem = ti;
36225         
36226         return ti;
36227     },
36228
36229     updatePanelTitle : function(panel, title)
36230     {
36231         if(this.activePanel == panel){
36232             this.updateTitle(title);
36233         }
36234         if(this.tabs){
36235             var ti = this.tabs.getTab(panel.getEl().id);
36236             ti.setText(title);
36237             if(panel.tabTip !== undefined){
36238                 ti.setTooltip(panel.tabTip);
36239             }
36240         }
36241     },
36242
36243     updateTitle : function(title){
36244         if(this.titleTextEl && !this.config.title){
36245             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36246         }
36247     },
36248
36249     setActivePanel : function(panel)
36250     {
36251         panel = this.getPanel(panel);
36252         if(this.activePanel && this.activePanel != panel){
36253             if(this.activePanel.setActiveState(false) === false){
36254                 return;
36255             }
36256         }
36257         this.activePanel = panel;
36258         panel.setActiveState(true);
36259         if(this.panelSize){
36260             panel.setSize(this.panelSize.width, this.panelSize.height);
36261         }
36262         if(this.closeBtn){
36263             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36264         }
36265         this.updateTitle(panel.getTitle());
36266         if(this.tabs){
36267             this.fireEvent("invalidated", this);
36268         }
36269         this.fireEvent("panelactivated", this, panel);
36270     },
36271
36272     /**
36273      * Shows the specified panel.
36274      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36275      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36276      */
36277     showPanel : function(panel)
36278     {
36279         panel = this.getPanel(panel);
36280         if(panel){
36281             if(this.tabs){
36282                 var tab = this.tabs.getTab(panel.getEl().id);
36283                 if(tab.isHidden()){
36284                     this.tabs.unhideTab(tab.id);
36285                 }
36286                 tab.activate();
36287             }else{
36288                 this.setActivePanel(panel);
36289             }
36290         }
36291         return panel;
36292     },
36293
36294     /**
36295      * Get the active panel for this region.
36296      * @return {Roo.ContentPanel} The active panel or null
36297      */
36298     getActivePanel : function(){
36299         return this.activePanel;
36300     },
36301
36302     validateVisibility : function(){
36303         if(this.panels.getCount() < 1){
36304             this.updateTitle("&#160;");
36305             this.closeBtn.hide();
36306             this.hide();
36307         }else{
36308             if(!this.isVisible()){
36309                 this.show();
36310             }
36311         }
36312     },
36313
36314     /**
36315      * Adds the passed ContentPanel(s) to this region.
36316      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36317      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36318      */
36319     add : function(panel)
36320     {
36321         if(arguments.length > 1){
36322             for(var i = 0, len = arguments.length; i < len; i++) {
36323                 this.add(arguments[i]);
36324             }
36325             return null;
36326         }
36327         
36328         // if we have not been rendered yet, then we can not really do much of this..
36329         if (!this.bodyEl) {
36330             this.unrendered_panels.push(panel);
36331             return panel;
36332         }
36333         
36334         
36335         
36336         
36337         if(this.hasPanel(panel)){
36338             this.showPanel(panel);
36339             return panel;
36340         }
36341         panel.setRegion(this);
36342         this.panels.add(panel);
36343        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36344             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36345             // and hide them... ???
36346             this.bodyEl.dom.appendChild(panel.getEl().dom);
36347             if(panel.background !== true){
36348                 this.setActivePanel(panel);
36349             }
36350             this.fireEvent("paneladded", this, panel);
36351             return panel;
36352         }
36353         */
36354         if(!this.tabs){
36355             this.initTabs();
36356         }else{
36357             this.initPanelAsTab(panel);
36358         }
36359         
36360         
36361         if(panel.background !== true){
36362             this.tabs.activate(panel.getEl().id);
36363         }
36364         this.fireEvent("paneladded", this, panel);
36365         return panel;
36366     },
36367
36368     /**
36369      * Hides the tab for the specified panel.
36370      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36371      */
36372     hidePanel : function(panel){
36373         if(this.tabs && (panel = this.getPanel(panel))){
36374             this.tabs.hideTab(panel.getEl().id);
36375         }
36376     },
36377
36378     /**
36379      * Unhides the tab for a previously hidden panel.
36380      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36381      */
36382     unhidePanel : function(panel){
36383         if(this.tabs && (panel = this.getPanel(panel))){
36384             this.tabs.unhideTab(panel.getEl().id);
36385         }
36386     },
36387
36388     clearPanels : function(){
36389         while(this.panels.getCount() > 0){
36390              this.remove(this.panels.first());
36391         }
36392     },
36393
36394     /**
36395      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36396      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36397      * @param {Boolean} preservePanel Overrides the config preservePanel option
36398      * @return {Roo.ContentPanel} The panel that was removed
36399      */
36400     remove : function(panel, preservePanel)
36401     {
36402         panel = this.getPanel(panel);
36403         if(!panel){
36404             return null;
36405         }
36406         var e = {};
36407         this.fireEvent("beforeremove", this, panel, e);
36408         if(e.cancel === true){
36409             return null;
36410         }
36411         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36412         var panelId = panel.getId();
36413         this.panels.removeKey(panelId);
36414         if(preservePanel){
36415             document.body.appendChild(panel.getEl().dom);
36416         }
36417         if(this.tabs){
36418             this.tabs.removeTab(panel.getEl().id);
36419         }else if (!preservePanel){
36420             this.bodyEl.dom.removeChild(panel.getEl().dom);
36421         }
36422         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36423             var p = this.panels.first();
36424             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36425             tempEl.appendChild(p.getEl().dom);
36426             this.bodyEl.update("");
36427             this.bodyEl.dom.appendChild(p.getEl().dom);
36428             tempEl = null;
36429             this.updateTitle(p.getTitle());
36430             this.tabs = null;
36431             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36432             this.setActivePanel(p);
36433         }
36434         panel.setRegion(null);
36435         if(this.activePanel == panel){
36436             this.activePanel = null;
36437         }
36438         if(this.config.autoDestroy !== false && preservePanel !== true){
36439             try{panel.destroy();}catch(e){}
36440         }
36441         this.fireEvent("panelremoved", this, panel);
36442         return panel;
36443     },
36444
36445     /**
36446      * Returns the TabPanel component used by this region
36447      * @return {Roo.TabPanel}
36448      */
36449     getTabs : function(){
36450         return this.tabs;
36451     },
36452
36453     createTool : function(parentEl, className){
36454         var btn = Roo.DomHelper.append(parentEl, {
36455             tag: "div",
36456             cls: "x-layout-tools-button",
36457             children: [ {
36458                 tag: "div",
36459                 cls: "roo-layout-tools-button-inner " + className,
36460                 html: "&#160;"
36461             }]
36462         }, true);
36463         btn.addClassOnOver("roo-layout-tools-button-over");
36464         return btn;
36465     }
36466 });/*
36467  * Based on:
36468  * Ext JS Library 1.1.1
36469  * Copyright(c) 2006-2007, Ext JS, LLC.
36470  *
36471  * Originally Released Under LGPL - original licence link has changed is not relivant.
36472  *
36473  * Fork - LGPL
36474  * <script type="text/javascript">
36475  */
36476  
36477
36478
36479 /**
36480  * @class Roo.SplitLayoutRegion
36481  * @extends Roo.LayoutRegion
36482  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36483  */
36484 Roo.bootstrap.layout.Split = function(config){
36485     this.cursor = config.cursor;
36486     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36487 };
36488
36489 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36490 {
36491     splitTip : "Drag to resize.",
36492     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36493     useSplitTips : false,
36494
36495     applyConfig : function(config){
36496         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36497     },
36498     
36499     onRender : function(ctr,pos) {
36500         
36501         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36502         if(!this.config.split){
36503             return;
36504         }
36505         if(!this.split){
36506             
36507             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36508                             tag: "div",
36509                             id: this.el.id + "-split",
36510                             cls: "roo-layout-split roo-layout-split-"+this.position,
36511                             html: "&#160;"
36512             });
36513             /** The SplitBar for this region 
36514             * @type Roo.SplitBar */
36515             // does not exist yet...
36516             Roo.log([this.position, this.orientation]);
36517             
36518             this.split = new Roo.bootstrap.SplitBar({
36519                 dragElement : splitEl,
36520                 resizingElement: this.el,
36521                 orientation : this.orientation
36522             });
36523             
36524             this.split.on("moved", this.onSplitMove, this);
36525             this.split.useShim = this.config.useShim === true;
36526             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36527             if(this.useSplitTips){
36528                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36529             }
36530             //if(config.collapsible){
36531             //    this.split.el.on("dblclick", this.collapse,  this);
36532             //}
36533         }
36534         if(typeof this.config.minSize != "undefined"){
36535             this.split.minSize = this.config.minSize;
36536         }
36537         if(typeof this.config.maxSize != "undefined"){
36538             this.split.maxSize = this.config.maxSize;
36539         }
36540         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36541             this.hideSplitter();
36542         }
36543         
36544     },
36545
36546     getHMaxSize : function(){
36547          var cmax = this.config.maxSize || 10000;
36548          var center = this.mgr.getRegion("center");
36549          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36550     },
36551
36552     getVMaxSize : function(){
36553          var cmax = this.config.maxSize || 10000;
36554          var center = this.mgr.getRegion("center");
36555          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36556     },
36557
36558     onSplitMove : function(split, newSize){
36559         this.fireEvent("resized", this, newSize);
36560     },
36561     
36562     /** 
36563      * Returns the {@link Roo.SplitBar} for this region.
36564      * @return {Roo.SplitBar}
36565      */
36566     getSplitBar : function(){
36567         return this.split;
36568     },
36569     
36570     hide : function(){
36571         this.hideSplitter();
36572         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36573     },
36574
36575     hideSplitter : function(){
36576         if(this.split){
36577             this.split.el.setLocation(-2000,-2000);
36578             this.split.el.hide();
36579         }
36580     },
36581
36582     show : function(){
36583         if(this.split){
36584             this.split.el.show();
36585         }
36586         Roo.bootstrap.layout.Split.superclass.show.call(this);
36587     },
36588     
36589     beforeSlide: function(){
36590         if(Roo.isGecko){// firefox overflow auto bug workaround
36591             this.bodyEl.clip();
36592             if(this.tabs) {
36593                 this.tabs.bodyEl.clip();
36594             }
36595             if(this.activePanel){
36596                 this.activePanel.getEl().clip();
36597                 
36598                 if(this.activePanel.beforeSlide){
36599                     this.activePanel.beforeSlide();
36600                 }
36601             }
36602         }
36603     },
36604     
36605     afterSlide : function(){
36606         if(Roo.isGecko){// firefox overflow auto bug workaround
36607             this.bodyEl.unclip();
36608             if(this.tabs) {
36609                 this.tabs.bodyEl.unclip();
36610             }
36611             if(this.activePanel){
36612                 this.activePanel.getEl().unclip();
36613                 if(this.activePanel.afterSlide){
36614                     this.activePanel.afterSlide();
36615                 }
36616             }
36617         }
36618     },
36619
36620     initAutoHide : function(){
36621         if(this.autoHide !== false){
36622             if(!this.autoHideHd){
36623                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36624                 this.autoHideHd = {
36625                     "mouseout": function(e){
36626                         if(!e.within(this.el, true)){
36627                             st.delay(500);
36628                         }
36629                     },
36630                     "mouseover" : function(e){
36631                         st.cancel();
36632                     },
36633                     scope : this
36634                 };
36635             }
36636             this.el.on(this.autoHideHd);
36637         }
36638     },
36639
36640     clearAutoHide : function(){
36641         if(this.autoHide !== false){
36642             this.el.un("mouseout", this.autoHideHd.mouseout);
36643             this.el.un("mouseover", this.autoHideHd.mouseover);
36644         }
36645     },
36646
36647     clearMonitor : function(){
36648         Roo.get(document).un("click", this.slideInIf, this);
36649     },
36650
36651     // these names are backwards but not changed for compat
36652     slideOut : function(){
36653         if(this.isSlid || this.el.hasActiveFx()){
36654             return;
36655         }
36656         this.isSlid = true;
36657         if(this.collapseBtn){
36658             this.collapseBtn.hide();
36659         }
36660         this.closeBtnState = this.closeBtn.getStyle('display');
36661         this.closeBtn.hide();
36662         if(this.stickBtn){
36663             this.stickBtn.show();
36664         }
36665         this.el.show();
36666         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36667         this.beforeSlide();
36668         this.el.setStyle("z-index", 10001);
36669         this.el.slideIn(this.getSlideAnchor(), {
36670             callback: function(){
36671                 this.afterSlide();
36672                 this.initAutoHide();
36673                 Roo.get(document).on("click", this.slideInIf, this);
36674                 this.fireEvent("slideshow", this);
36675             },
36676             scope: this,
36677             block: true
36678         });
36679     },
36680
36681     afterSlideIn : function(){
36682         this.clearAutoHide();
36683         this.isSlid = false;
36684         this.clearMonitor();
36685         this.el.setStyle("z-index", "");
36686         if(this.collapseBtn){
36687             this.collapseBtn.show();
36688         }
36689         this.closeBtn.setStyle('display', this.closeBtnState);
36690         if(this.stickBtn){
36691             this.stickBtn.hide();
36692         }
36693         this.fireEvent("slidehide", this);
36694     },
36695
36696     slideIn : function(cb){
36697         if(!this.isSlid || this.el.hasActiveFx()){
36698             Roo.callback(cb);
36699             return;
36700         }
36701         this.isSlid = false;
36702         this.beforeSlide();
36703         this.el.slideOut(this.getSlideAnchor(), {
36704             callback: function(){
36705                 this.el.setLeftTop(-10000, -10000);
36706                 this.afterSlide();
36707                 this.afterSlideIn();
36708                 Roo.callback(cb);
36709             },
36710             scope: this,
36711             block: true
36712         });
36713     },
36714     
36715     slideInIf : function(e){
36716         if(!e.within(this.el)){
36717             this.slideIn();
36718         }
36719     },
36720
36721     animateCollapse : function(){
36722         this.beforeSlide();
36723         this.el.setStyle("z-index", 20000);
36724         var anchor = this.getSlideAnchor();
36725         this.el.slideOut(anchor, {
36726             callback : function(){
36727                 this.el.setStyle("z-index", "");
36728                 this.collapsedEl.slideIn(anchor, {duration:.3});
36729                 this.afterSlide();
36730                 this.el.setLocation(-10000,-10000);
36731                 this.el.hide();
36732                 this.fireEvent("collapsed", this);
36733             },
36734             scope: this,
36735             block: true
36736         });
36737     },
36738
36739     animateExpand : function(){
36740         this.beforeSlide();
36741         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36742         this.el.setStyle("z-index", 20000);
36743         this.collapsedEl.hide({
36744             duration:.1
36745         });
36746         this.el.slideIn(this.getSlideAnchor(), {
36747             callback : function(){
36748                 this.el.setStyle("z-index", "");
36749                 this.afterSlide();
36750                 if(this.split){
36751                     this.split.el.show();
36752                 }
36753                 this.fireEvent("invalidated", this);
36754                 this.fireEvent("expanded", this);
36755             },
36756             scope: this,
36757             block: true
36758         });
36759     },
36760
36761     anchors : {
36762         "west" : "left",
36763         "east" : "right",
36764         "north" : "top",
36765         "south" : "bottom"
36766     },
36767
36768     sanchors : {
36769         "west" : "l",
36770         "east" : "r",
36771         "north" : "t",
36772         "south" : "b"
36773     },
36774
36775     canchors : {
36776         "west" : "tl-tr",
36777         "east" : "tr-tl",
36778         "north" : "tl-bl",
36779         "south" : "bl-tl"
36780     },
36781
36782     getAnchor : function(){
36783         return this.anchors[this.position];
36784     },
36785
36786     getCollapseAnchor : function(){
36787         return this.canchors[this.position];
36788     },
36789
36790     getSlideAnchor : function(){
36791         return this.sanchors[this.position];
36792     },
36793
36794     getAlignAdj : function(){
36795         var cm = this.cmargins;
36796         switch(this.position){
36797             case "west":
36798                 return [0, 0];
36799             break;
36800             case "east":
36801                 return [0, 0];
36802             break;
36803             case "north":
36804                 return [0, 0];
36805             break;
36806             case "south":
36807                 return [0, 0];
36808             break;
36809         }
36810     },
36811
36812     getExpandAdj : function(){
36813         var c = this.collapsedEl, cm = this.cmargins;
36814         switch(this.position){
36815             case "west":
36816                 return [-(cm.right+c.getWidth()+cm.left), 0];
36817             break;
36818             case "east":
36819                 return [cm.right+c.getWidth()+cm.left, 0];
36820             break;
36821             case "north":
36822                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36823             break;
36824             case "south":
36825                 return [0, cm.top+cm.bottom+c.getHeight()];
36826             break;
36827         }
36828     }
36829 });/*
36830  * Based on:
36831  * Ext JS Library 1.1.1
36832  * Copyright(c) 2006-2007, Ext JS, LLC.
36833  *
36834  * Originally Released Under LGPL - original licence link has changed is not relivant.
36835  *
36836  * Fork - LGPL
36837  * <script type="text/javascript">
36838  */
36839 /*
36840  * These classes are private internal classes
36841  */
36842 Roo.bootstrap.layout.Center = function(config){
36843     config.region = "center";
36844     Roo.bootstrap.layout.Region.call(this, config);
36845     this.visible = true;
36846     this.minWidth = config.minWidth || 20;
36847     this.minHeight = config.minHeight || 20;
36848 };
36849
36850 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36851     hide : function(){
36852         // center panel can't be hidden
36853     },
36854     
36855     show : function(){
36856         // center panel can't be hidden
36857     },
36858     
36859     getMinWidth: function(){
36860         return this.minWidth;
36861     },
36862     
36863     getMinHeight: function(){
36864         return this.minHeight;
36865     }
36866 });
36867
36868
36869
36870
36871  
36872
36873
36874
36875
36876
36877 Roo.bootstrap.layout.North = function(config)
36878 {
36879     config.region = 'north';
36880     config.cursor = 'n-resize';
36881     
36882     Roo.bootstrap.layout.Split.call(this, config);
36883     
36884     
36885     if(this.split){
36886         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36887         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36888         this.split.el.addClass("roo-layout-split-v");
36889     }
36890     var size = config.initialSize || config.height;
36891     if(typeof size != "undefined"){
36892         this.el.setHeight(size);
36893     }
36894 };
36895 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36896 {
36897     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36898     
36899     
36900     
36901     getBox : function(){
36902         if(this.collapsed){
36903             return this.collapsedEl.getBox();
36904         }
36905         var box = this.el.getBox();
36906         if(this.split){
36907             box.height += this.split.el.getHeight();
36908         }
36909         return box;
36910     },
36911     
36912     updateBox : function(box){
36913         if(this.split && !this.collapsed){
36914             box.height -= this.split.el.getHeight();
36915             this.split.el.setLeft(box.x);
36916             this.split.el.setTop(box.y+box.height);
36917             this.split.el.setWidth(box.width);
36918         }
36919         if(this.collapsed){
36920             this.updateBody(box.width, null);
36921         }
36922         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36923     }
36924 });
36925
36926
36927
36928
36929
36930 Roo.bootstrap.layout.South = function(config){
36931     config.region = 'south';
36932     config.cursor = 's-resize';
36933     Roo.bootstrap.layout.Split.call(this, config);
36934     if(this.split){
36935         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36936         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36937         this.split.el.addClass("roo-layout-split-v");
36938     }
36939     var size = config.initialSize || config.height;
36940     if(typeof size != "undefined"){
36941         this.el.setHeight(size);
36942     }
36943 };
36944
36945 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36946     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36947     getBox : function(){
36948         if(this.collapsed){
36949             return this.collapsedEl.getBox();
36950         }
36951         var box = this.el.getBox();
36952         if(this.split){
36953             var sh = this.split.el.getHeight();
36954             box.height += sh;
36955             box.y -= sh;
36956         }
36957         return box;
36958     },
36959     
36960     updateBox : function(box){
36961         if(this.split && !this.collapsed){
36962             var sh = this.split.el.getHeight();
36963             box.height -= sh;
36964             box.y += sh;
36965             this.split.el.setLeft(box.x);
36966             this.split.el.setTop(box.y-sh);
36967             this.split.el.setWidth(box.width);
36968         }
36969         if(this.collapsed){
36970             this.updateBody(box.width, null);
36971         }
36972         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36973     }
36974 });
36975
36976 Roo.bootstrap.layout.East = function(config){
36977     config.region = "east";
36978     config.cursor = "e-resize";
36979     Roo.bootstrap.layout.Split.call(this, config);
36980     if(this.split){
36981         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36982         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36983         this.split.el.addClass("roo-layout-split-h");
36984     }
36985     var size = config.initialSize || config.width;
36986     if(typeof size != "undefined"){
36987         this.el.setWidth(size);
36988     }
36989 };
36990 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36991     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36992     getBox : function(){
36993         if(this.collapsed){
36994             return this.collapsedEl.getBox();
36995         }
36996         var box = this.el.getBox();
36997         if(this.split){
36998             var sw = this.split.el.getWidth();
36999             box.width += sw;
37000             box.x -= sw;
37001         }
37002         return box;
37003     },
37004
37005     updateBox : function(box){
37006         if(this.split && !this.collapsed){
37007             var sw = this.split.el.getWidth();
37008             box.width -= sw;
37009             this.split.el.setLeft(box.x);
37010             this.split.el.setTop(box.y);
37011             this.split.el.setHeight(box.height);
37012             box.x += sw;
37013         }
37014         if(this.collapsed){
37015             this.updateBody(null, box.height);
37016         }
37017         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37018     }
37019 });
37020
37021 Roo.bootstrap.layout.West = function(config){
37022     config.region = "west";
37023     config.cursor = "w-resize";
37024     
37025     Roo.bootstrap.layout.Split.call(this, config);
37026     if(this.split){
37027         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37028         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37029         this.split.el.addClass("roo-layout-split-h");
37030     }
37031     
37032 };
37033 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37034     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37035     
37036     onRender: function(ctr, pos)
37037     {
37038         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37039         var size = this.config.initialSize || this.config.width;
37040         if(typeof size != "undefined"){
37041             this.el.setWidth(size);
37042         }
37043     },
37044     
37045     getBox : function(){
37046         if(this.collapsed){
37047             return this.collapsedEl.getBox();
37048         }
37049         var box = this.el.getBox();
37050         if(this.split){
37051             box.width += this.split.el.getWidth();
37052         }
37053         return box;
37054     },
37055     
37056     updateBox : function(box){
37057         if(this.split && !this.collapsed){
37058             var sw = this.split.el.getWidth();
37059             box.width -= sw;
37060             this.split.el.setLeft(box.x+box.width);
37061             this.split.el.setTop(box.y);
37062             this.split.el.setHeight(box.height);
37063         }
37064         if(this.collapsed){
37065             this.updateBody(null, box.height);
37066         }
37067         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37068     }
37069 });
37070 Roo.namespace("Roo.bootstrap.panel");/*
37071  * Based on:
37072  * Ext JS Library 1.1.1
37073  * Copyright(c) 2006-2007, Ext JS, LLC.
37074  *
37075  * Originally Released Under LGPL - original licence link has changed is not relivant.
37076  *
37077  * Fork - LGPL
37078  * <script type="text/javascript">
37079  */
37080 /**
37081  * @class Roo.ContentPanel
37082  * @extends Roo.util.Observable
37083  * A basic ContentPanel element.
37084  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37085  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37086  * @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
37087  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37088  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37089  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37090  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37091  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37092  * @cfg {String} title          The title for this panel
37093  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37094  * @cfg {String} url            Calls {@link #setUrl} with this value
37095  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37096  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37097  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37098  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37099  * @cfg {Boolean} badges render the badges
37100
37101  * @constructor
37102  * Create a new ContentPanel.
37103  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37104  * @param {String/Object} config A string to set only the title or a config object
37105  * @param {String} content (optional) Set the HTML content for this panel
37106  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37107  */
37108 Roo.bootstrap.panel.Content = function( config){
37109     
37110     this.tpl = config.tpl || false;
37111     
37112     var el = config.el;
37113     var content = config.content;
37114
37115     if(config.autoCreate){ // xtype is available if this is called from factory
37116         el = Roo.id();
37117     }
37118     this.el = Roo.get(el);
37119     if(!this.el && config && config.autoCreate){
37120         if(typeof config.autoCreate == "object"){
37121             if(!config.autoCreate.id){
37122                 config.autoCreate.id = config.id||el;
37123             }
37124             this.el = Roo.DomHelper.append(document.body,
37125                         config.autoCreate, true);
37126         }else{
37127             var elcfg =  {   tag: "div",
37128                             cls: "roo-layout-inactive-content",
37129                             id: config.id||el
37130                             };
37131             if (config.html) {
37132                 elcfg.html = config.html;
37133                 
37134             }
37135                         
37136             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37137         }
37138     } 
37139     this.closable = false;
37140     this.loaded = false;
37141     this.active = false;
37142    
37143       
37144     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37145         
37146         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37147         
37148         this.wrapEl = this.el; //this.el.wrap();
37149         var ti = [];
37150         if (config.toolbar.items) {
37151             ti = config.toolbar.items ;
37152             delete config.toolbar.items ;
37153         }
37154         
37155         var nitems = [];
37156         this.toolbar.render(this.wrapEl, 'before');
37157         for(var i =0;i < ti.length;i++) {
37158           //  Roo.log(['add child', items[i]]);
37159             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37160         }
37161         this.toolbar.items = nitems;
37162         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37163         delete config.toolbar;
37164         
37165     }
37166     /*
37167     // xtype created footer. - not sure if will work as we normally have to render first..
37168     if (this.footer && !this.footer.el && this.footer.xtype) {
37169         if (!this.wrapEl) {
37170             this.wrapEl = this.el.wrap();
37171         }
37172     
37173         this.footer.container = this.wrapEl.createChild();
37174          
37175         this.footer = Roo.factory(this.footer, Roo);
37176         
37177     }
37178     */
37179     
37180      if(typeof config == "string"){
37181         this.title = config;
37182     }else{
37183         Roo.apply(this, config);
37184     }
37185     
37186     if(this.resizeEl){
37187         this.resizeEl = Roo.get(this.resizeEl, true);
37188     }else{
37189         this.resizeEl = this.el;
37190     }
37191     // handle view.xtype
37192     
37193  
37194     
37195     
37196     this.addEvents({
37197         /**
37198          * @event activate
37199          * Fires when this panel is activated. 
37200          * @param {Roo.ContentPanel} this
37201          */
37202         "activate" : true,
37203         /**
37204          * @event deactivate
37205          * Fires when this panel is activated. 
37206          * @param {Roo.ContentPanel} this
37207          */
37208         "deactivate" : true,
37209
37210         /**
37211          * @event resize
37212          * Fires when this panel is resized if fitToFrame is true.
37213          * @param {Roo.ContentPanel} this
37214          * @param {Number} width The width after any component adjustments
37215          * @param {Number} height The height after any component adjustments
37216          */
37217         "resize" : true,
37218         
37219          /**
37220          * @event render
37221          * Fires when this tab is created
37222          * @param {Roo.ContentPanel} this
37223          */
37224         "render" : true
37225         
37226         
37227         
37228     });
37229     
37230
37231     
37232     
37233     if(this.autoScroll){
37234         this.resizeEl.setStyle("overflow", "auto");
37235     } else {
37236         // fix randome scrolling
37237         //this.el.on('scroll', function() {
37238         //    Roo.log('fix random scolling');
37239         //    this.scrollTo('top',0); 
37240         //});
37241     }
37242     content = content || this.content;
37243     if(content){
37244         this.setContent(content);
37245     }
37246     if(config && config.url){
37247         this.setUrl(this.url, this.params, this.loadOnce);
37248     }
37249     
37250     
37251     
37252     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37253     
37254     if (this.view && typeof(this.view.xtype) != 'undefined') {
37255         this.view.el = this.el.appendChild(document.createElement("div"));
37256         this.view = Roo.factory(this.view); 
37257         this.view.render  &&  this.view.render(false, '');  
37258     }
37259     
37260     
37261     this.fireEvent('render', this);
37262 };
37263
37264 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37265     
37266     tabTip : '',
37267     
37268     setRegion : function(region){
37269         this.region = region;
37270         this.setActiveClass(region && !this.background);
37271     },
37272     
37273     
37274     setActiveClass: function(state)
37275     {
37276         if(state){
37277            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37278            this.el.setStyle('position','relative');
37279         }else{
37280            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37281            this.el.setStyle('position', 'absolute');
37282         } 
37283     },
37284     
37285     /**
37286      * Returns the toolbar for this Panel if one was configured. 
37287      * @return {Roo.Toolbar} 
37288      */
37289     getToolbar : function(){
37290         return this.toolbar;
37291     },
37292     
37293     setActiveState : function(active)
37294     {
37295         this.active = active;
37296         this.setActiveClass(active);
37297         if(!active){
37298             if(this.fireEvent("deactivate", this) === false){
37299                 return false;
37300             }
37301             return true;
37302         }
37303         this.fireEvent("activate", this);
37304         return true;
37305     },
37306     /**
37307      * Updates this panel's element
37308      * @param {String} content The new content
37309      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37310     */
37311     setContent : function(content, loadScripts){
37312         this.el.update(content, loadScripts);
37313     },
37314
37315     ignoreResize : function(w, h){
37316         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37317             return true;
37318         }else{
37319             this.lastSize = {width: w, height: h};
37320             return false;
37321         }
37322     },
37323     /**
37324      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37325      * @return {Roo.UpdateManager} The UpdateManager
37326      */
37327     getUpdateManager : function(){
37328         return this.el.getUpdateManager();
37329     },
37330      /**
37331      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37332      * @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:
37333 <pre><code>
37334 panel.load({
37335     url: "your-url.php",
37336     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37337     callback: yourFunction,
37338     scope: yourObject, //(optional scope)
37339     discardUrl: false,
37340     nocache: false,
37341     text: "Loading...",
37342     timeout: 30,
37343     scripts: false
37344 });
37345 </code></pre>
37346      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37347      * 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.
37348      * @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}
37349      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37350      * @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.
37351      * @return {Roo.ContentPanel} this
37352      */
37353     load : function(){
37354         var um = this.el.getUpdateManager();
37355         um.update.apply(um, arguments);
37356         return this;
37357     },
37358
37359
37360     /**
37361      * 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.
37362      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37363      * @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)
37364      * @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)
37365      * @return {Roo.UpdateManager} The UpdateManager
37366      */
37367     setUrl : function(url, params, loadOnce){
37368         if(this.refreshDelegate){
37369             this.removeListener("activate", this.refreshDelegate);
37370         }
37371         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37372         this.on("activate", this.refreshDelegate);
37373         return this.el.getUpdateManager();
37374     },
37375     
37376     _handleRefresh : function(url, params, loadOnce){
37377         if(!loadOnce || !this.loaded){
37378             var updater = this.el.getUpdateManager();
37379             updater.update(url, params, this._setLoaded.createDelegate(this));
37380         }
37381     },
37382     
37383     _setLoaded : function(){
37384         this.loaded = true;
37385     }, 
37386     
37387     /**
37388      * Returns this panel's id
37389      * @return {String} 
37390      */
37391     getId : function(){
37392         return this.el.id;
37393     },
37394     
37395     /** 
37396      * Returns this panel's element - used by regiosn to add.
37397      * @return {Roo.Element} 
37398      */
37399     getEl : function(){
37400         return this.wrapEl || this.el;
37401     },
37402     
37403    
37404     
37405     adjustForComponents : function(width, height)
37406     {
37407         //Roo.log('adjustForComponents ');
37408         if(this.resizeEl != this.el){
37409             width -= this.el.getFrameWidth('lr');
37410             height -= this.el.getFrameWidth('tb');
37411         }
37412         if(this.toolbar){
37413             var te = this.toolbar.getEl();
37414             te.setWidth(width);
37415             height -= te.getHeight();
37416         }
37417         if(this.footer){
37418             var te = this.footer.getEl();
37419             te.setWidth(width);
37420             height -= te.getHeight();
37421         }
37422         
37423         
37424         if(this.adjustments){
37425             width += this.adjustments[0];
37426             height += this.adjustments[1];
37427         }
37428         return {"width": width, "height": height};
37429     },
37430     
37431     setSize : function(width, height){
37432         if(this.fitToFrame && !this.ignoreResize(width, height)){
37433             if(this.fitContainer && this.resizeEl != this.el){
37434                 this.el.setSize(width, height);
37435             }
37436             var size = this.adjustForComponents(width, height);
37437             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37438             this.fireEvent('resize', this, size.width, size.height);
37439         }
37440     },
37441     
37442     /**
37443      * Returns this panel's title
37444      * @return {String} 
37445      */
37446     getTitle : function(){
37447         
37448         if (typeof(this.title) != 'object') {
37449             return this.title;
37450         }
37451         
37452         var t = '';
37453         for (var k in this.title) {
37454             if (!this.title.hasOwnProperty(k)) {
37455                 continue;
37456             }
37457             
37458             if (k.indexOf('-') >= 0) {
37459                 var s = k.split('-');
37460                 for (var i = 0; i<s.length; i++) {
37461                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37462                 }
37463             } else {
37464                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37465             }
37466         }
37467         return t;
37468     },
37469     
37470     /**
37471      * Set this panel's title
37472      * @param {String} title
37473      */
37474     setTitle : function(title){
37475         this.title = title;
37476         if(this.region){
37477             this.region.updatePanelTitle(this, title);
37478         }
37479     },
37480     
37481     /**
37482      * Returns true is this panel was configured to be closable
37483      * @return {Boolean} 
37484      */
37485     isClosable : function(){
37486         return this.closable;
37487     },
37488     
37489     beforeSlide : function(){
37490         this.el.clip();
37491         this.resizeEl.clip();
37492     },
37493     
37494     afterSlide : function(){
37495         this.el.unclip();
37496         this.resizeEl.unclip();
37497     },
37498     
37499     /**
37500      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37501      *   Will fail silently if the {@link #setUrl} method has not been called.
37502      *   This does not activate the panel, just updates its content.
37503      */
37504     refresh : function(){
37505         if(this.refreshDelegate){
37506            this.loaded = false;
37507            this.refreshDelegate();
37508         }
37509     },
37510     
37511     /**
37512      * Destroys this panel
37513      */
37514     destroy : function(){
37515         this.el.removeAllListeners();
37516         var tempEl = document.createElement("span");
37517         tempEl.appendChild(this.el.dom);
37518         tempEl.innerHTML = "";
37519         this.el.remove();
37520         this.el = null;
37521     },
37522     
37523     /**
37524      * form - if the content panel contains a form - this is a reference to it.
37525      * @type {Roo.form.Form}
37526      */
37527     form : false,
37528     /**
37529      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37530      *    This contains a reference to it.
37531      * @type {Roo.View}
37532      */
37533     view : false,
37534     
37535       /**
37536      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37537      * <pre><code>
37538
37539 layout.addxtype({
37540        xtype : 'Form',
37541        items: [ .... ]
37542    }
37543 );
37544
37545 </code></pre>
37546      * @param {Object} cfg Xtype definition of item to add.
37547      */
37548     
37549     
37550     getChildContainer: function () {
37551         return this.getEl();
37552     }
37553     
37554     
37555     /*
37556         var  ret = new Roo.factory(cfg);
37557         return ret;
37558         
37559         
37560         // add form..
37561         if (cfg.xtype.match(/^Form$/)) {
37562             
37563             var el;
37564             //if (this.footer) {
37565             //    el = this.footer.container.insertSibling(false, 'before');
37566             //} else {
37567                 el = this.el.createChild();
37568             //}
37569
37570             this.form = new  Roo.form.Form(cfg);
37571             
37572             
37573             if ( this.form.allItems.length) {
37574                 this.form.render(el.dom);
37575             }
37576             return this.form;
37577         }
37578         // should only have one of theses..
37579         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37580             // views.. should not be just added - used named prop 'view''
37581             
37582             cfg.el = this.el.appendChild(document.createElement("div"));
37583             // factory?
37584             
37585             var ret = new Roo.factory(cfg);
37586              
37587              ret.render && ret.render(false, ''); // render blank..
37588             this.view = ret;
37589             return ret;
37590         }
37591         return false;
37592     }
37593     \*/
37594 });
37595  
37596 /**
37597  * @class Roo.bootstrap.panel.Grid
37598  * @extends Roo.bootstrap.panel.Content
37599  * @constructor
37600  * Create a new GridPanel.
37601  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37602  * @param {Object} config A the config object
37603   
37604  */
37605
37606
37607
37608 Roo.bootstrap.panel.Grid = function(config)
37609 {
37610     
37611       
37612     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37613         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37614
37615     config.el = this.wrapper;
37616     //this.el = this.wrapper;
37617     
37618       if (config.container) {
37619         // ctor'ed from a Border/panel.grid
37620         
37621         
37622         this.wrapper.setStyle("overflow", "hidden");
37623         this.wrapper.addClass('roo-grid-container');
37624
37625     }
37626     
37627     
37628     if(config.toolbar){
37629         var tool_el = this.wrapper.createChild();    
37630         this.toolbar = Roo.factory(config.toolbar);
37631         var ti = [];
37632         if (config.toolbar.items) {
37633             ti = config.toolbar.items ;
37634             delete config.toolbar.items ;
37635         }
37636         
37637         var nitems = [];
37638         this.toolbar.render(tool_el);
37639         for(var i =0;i < ti.length;i++) {
37640           //  Roo.log(['add child', items[i]]);
37641             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37642         }
37643         this.toolbar.items = nitems;
37644         
37645         delete config.toolbar;
37646     }
37647     
37648     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37649     config.grid.scrollBody = true;;
37650     config.grid.monitorWindowResize = false; // turn off autosizing
37651     config.grid.autoHeight = false;
37652     config.grid.autoWidth = false;
37653     
37654     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37655     
37656     if (config.background) {
37657         // render grid on panel activation (if panel background)
37658         this.on('activate', function(gp) {
37659             if (!gp.grid.rendered) {
37660                 gp.grid.render(this.wrapper);
37661                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37662             }
37663         });
37664             
37665     } else {
37666         this.grid.render(this.wrapper);
37667         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37668
37669     }
37670     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37671     // ??? needed ??? config.el = this.wrapper;
37672     
37673     
37674     
37675   
37676     // xtype created footer. - not sure if will work as we normally have to render first..
37677     if (this.footer && !this.footer.el && this.footer.xtype) {
37678         
37679         var ctr = this.grid.getView().getFooterPanel(true);
37680         this.footer.dataSource = this.grid.dataSource;
37681         this.footer = Roo.factory(this.footer, Roo);
37682         this.footer.render(ctr);
37683         
37684     }
37685     
37686     
37687     
37688     
37689      
37690 };
37691
37692 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37693     getId : function(){
37694         return this.grid.id;
37695     },
37696     
37697     /**
37698      * Returns the grid for this panel
37699      * @return {Roo.bootstrap.Table} 
37700      */
37701     getGrid : function(){
37702         return this.grid;    
37703     },
37704     
37705     setSize : function(width, height){
37706         if(!this.ignoreResize(width, height)){
37707             var grid = this.grid;
37708             var size = this.adjustForComponents(width, height);
37709             var gridel = grid.getGridEl();
37710             gridel.setSize(size.width, size.height);
37711             /*
37712             var thd = grid.getGridEl().select('thead',true).first();
37713             var tbd = grid.getGridEl().select('tbody', true).first();
37714             if (tbd) {
37715                 tbd.setSize(width, height - thd.getHeight());
37716             }
37717             */
37718             grid.autoSize();
37719         }
37720     },
37721      
37722     
37723     
37724     beforeSlide : function(){
37725         this.grid.getView().scroller.clip();
37726     },
37727     
37728     afterSlide : function(){
37729         this.grid.getView().scroller.unclip();
37730     },
37731     
37732     destroy : function(){
37733         this.grid.destroy();
37734         delete this.grid;
37735         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37736     }
37737 });
37738
37739 /**
37740  * @class Roo.bootstrap.panel.Nest
37741  * @extends Roo.bootstrap.panel.Content
37742  * @constructor
37743  * Create a new Panel, that can contain a layout.Border.
37744  * 
37745  * 
37746  * @param {Roo.BorderLayout} layout The layout for this panel
37747  * @param {String/Object} config A string to set only the title or a config object
37748  */
37749 Roo.bootstrap.panel.Nest = function(config)
37750 {
37751     // construct with only one argument..
37752     /* FIXME - implement nicer consturctors
37753     if (layout.layout) {
37754         config = layout;
37755         layout = config.layout;
37756         delete config.layout;
37757     }
37758     if (layout.xtype && !layout.getEl) {
37759         // then layout needs constructing..
37760         layout = Roo.factory(layout, Roo);
37761     }
37762     */
37763     
37764     config.el =  config.layout.getEl();
37765     
37766     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37767     
37768     config.layout.monitorWindowResize = false; // turn off autosizing
37769     this.layout = config.layout;
37770     this.layout.getEl().addClass("roo-layout-nested-layout");
37771     
37772     
37773     
37774     
37775 };
37776
37777 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37778
37779     setSize : function(width, height){
37780         if(!this.ignoreResize(width, height)){
37781             var size = this.adjustForComponents(width, height);
37782             var el = this.layout.getEl();
37783             if (size.height < 1) {
37784                 el.setWidth(size.width);   
37785             } else {
37786                 el.setSize(size.width, size.height);
37787             }
37788             var touch = el.dom.offsetWidth;
37789             this.layout.layout();
37790             // ie requires a double layout on the first pass
37791             if(Roo.isIE && !this.initialized){
37792                 this.initialized = true;
37793                 this.layout.layout();
37794             }
37795         }
37796     },
37797     
37798     // activate all subpanels if not currently active..
37799     
37800     setActiveState : function(active){
37801         this.active = active;
37802         this.setActiveClass(active);
37803         
37804         if(!active){
37805             this.fireEvent("deactivate", this);
37806             return;
37807         }
37808         
37809         this.fireEvent("activate", this);
37810         // not sure if this should happen before or after..
37811         if (!this.layout) {
37812             return; // should not happen..
37813         }
37814         var reg = false;
37815         for (var r in this.layout.regions) {
37816             reg = this.layout.getRegion(r);
37817             if (reg.getActivePanel()) {
37818                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37819                 reg.setActivePanel(reg.getActivePanel());
37820                 continue;
37821             }
37822             if (!reg.panels.length) {
37823                 continue;
37824             }
37825             reg.showPanel(reg.getPanel(0));
37826         }
37827         
37828         
37829         
37830         
37831     },
37832     
37833     /**
37834      * Returns the nested BorderLayout for this panel
37835      * @return {Roo.BorderLayout} 
37836      */
37837     getLayout : function(){
37838         return this.layout;
37839     },
37840     
37841      /**
37842      * Adds a xtype elements to the layout of the nested panel
37843      * <pre><code>
37844
37845 panel.addxtype({
37846        xtype : 'ContentPanel',
37847        region: 'west',
37848        items: [ .... ]
37849    }
37850 );
37851
37852 panel.addxtype({
37853         xtype : 'NestedLayoutPanel',
37854         region: 'west',
37855         layout: {
37856            center: { },
37857            west: { }   
37858         },
37859         items : [ ... list of content panels or nested layout panels.. ]
37860    }
37861 );
37862 </code></pre>
37863      * @param {Object} cfg Xtype definition of item to add.
37864      */
37865     addxtype : function(cfg) {
37866         return this.layout.addxtype(cfg);
37867     
37868     }
37869 });        /*
37870  * Based on:
37871  * Ext JS Library 1.1.1
37872  * Copyright(c) 2006-2007, Ext JS, LLC.
37873  *
37874  * Originally Released Under LGPL - original licence link has changed is not relivant.
37875  *
37876  * Fork - LGPL
37877  * <script type="text/javascript">
37878  */
37879 /**
37880  * @class Roo.TabPanel
37881  * @extends Roo.util.Observable
37882  * A lightweight tab container.
37883  * <br><br>
37884  * Usage:
37885  * <pre><code>
37886 // basic tabs 1, built from existing content
37887 var tabs = new Roo.TabPanel("tabs1");
37888 tabs.addTab("script", "View Script");
37889 tabs.addTab("markup", "View Markup");
37890 tabs.activate("script");
37891
37892 // more advanced tabs, built from javascript
37893 var jtabs = new Roo.TabPanel("jtabs");
37894 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37895
37896 // set up the UpdateManager
37897 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37898 var updater = tab2.getUpdateManager();
37899 updater.setDefaultUrl("ajax1.htm");
37900 tab2.on('activate', updater.refresh, updater, true);
37901
37902 // Use setUrl for Ajax loading
37903 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37904 tab3.setUrl("ajax2.htm", null, true);
37905
37906 // Disabled tab
37907 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37908 tab4.disable();
37909
37910 jtabs.activate("jtabs-1");
37911  * </code></pre>
37912  * @constructor
37913  * Create a new TabPanel.
37914  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37915  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37916  */
37917 Roo.bootstrap.panel.Tabs = function(config){
37918     /**
37919     * The container element for this TabPanel.
37920     * @type Roo.Element
37921     */
37922     this.el = Roo.get(config.el);
37923     delete config.el;
37924     if(config){
37925         if(typeof config == "boolean"){
37926             this.tabPosition = config ? "bottom" : "top";
37927         }else{
37928             Roo.apply(this, config);
37929         }
37930     }
37931     
37932     if(this.tabPosition == "bottom"){
37933         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37934         this.el.addClass("roo-tabs-bottom");
37935     }
37936     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37937     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37938     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37939     if(Roo.isIE){
37940         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37941     }
37942     if(this.tabPosition != "bottom"){
37943         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37944          * @type Roo.Element
37945          */
37946         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37947         this.el.addClass("roo-tabs-top");
37948     }
37949     this.items = [];
37950
37951     this.bodyEl.setStyle("position", "relative");
37952
37953     this.active = null;
37954     this.activateDelegate = this.activate.createDelegate(this);
37955
37956     this.addEvents({
37957         /**
37958          * @event tabchange
37959          * Fires when the active tab changes
37960          * @param {Roo.TabPanel} this
37961          * @param {Roo.TabPanelItem} activePanel The new active tab
37962          */
37963         "tabchange": true,
37964         /**
37965          * @event beforetabchange
37966          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37967          * @param {Roo.TabPanel} this
37968          * @param {Object} e Set cancel to true on this object to cancel the tab change
37969          * @param {Roo.TabPanelItem} tab The tab being changed to
37970          */
37971         "beforetabchange" : true
37972     });
37973
37974     Roo.EventManager.onWindowResize(this.onResize, this);
37975     this.cpad = this.el.getPadding("lr");
37976     this.hiddenCount = 0;
37977
37978
37979     // toolbar on the tabbar support...
37980     if (this.toolbar) {
37981         alert("no toolbar support yet");
37982         this.toolbar  = false;
37983         /*
37984         var tcfg = this.toolbar;
37985         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37986         this.toolbar = new Roo.Toolbar(tcfg);
37987         if (Roo.isSafari) {
37988             var tbl = tcfg.container.child('table', true);
37989             tbl.setAttribute('width', '100%');
37990         }
37991         */
37992         
37993     }
37994    
37995
37996
37997     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37998 };
37999
38000 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38001     /*
38002      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38003      */
38004     tabPosition : "top",
38005     /*
38006      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38007      */
38008     currentTabWidth : 0,
38009     /*
38010      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38011      */
38012     minTabWidth : 40,
38013     /*
38014      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38015      */
38016     maxTabWidth : 250,
38017     /*
38018      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38019      */
38020     preferredTabWidth : 175,
38021     /*
38022      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38023      */
38024     resizeTabs : false,
38025     /*
38026      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38027      */
38028     monitorResize : true,
38029     /*
38030      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38031      */
38032     toolbar : false,
38033
38034     /**
38035      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38036      * @param {String} id The id of the div to use <b>or create</b>
38037      * @param {String} text The text for the tab
38038      * @param {String} content (optional) Content to put in the TabPanelItem body
38039      * @param {Boolean} closable (optional) True to create a close icon on the tab
38040      * @return {Roo.TabPanelItem} The created TabPanelItem
38041      */
38042     addTab : function(id, text, content, closable, tpl)
38043     {
38044         var item = new Roo.bootstrap.panel.TabItem({
38045             panel: this,
38046             id : id,
38047             text : text,
38048             closable : closable,
38049             tpl : tpl
38050         });
38051         this.addTabItem(item);
38052         if(content){
38053             item.setContent(content);
38054         }
38055         return item;
38056     },
38057
38058     /**
38059      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38060      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38061      * @return {Roo.TabPanelItem}
38062      */
38063     getTab : function(id){
38064         return this.items[id];
38065     },
38066
38067     /**
38068      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38069      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38070      */
38071     hideTab : function(id){
38072         var t = this.items[id];
38073         if(!t.isHidden()){
38074            t.setHidden(true);
38075            this.hiddenCount++;
38076            this.autoSizeTabs();
38077         }
38078     },
38079
38080     /**
38081      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38082      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38083      */
38084     unhideTab : function(id){
38085         var t = this.items[id];
38086         if(t.isHidden()){
38087            t.setHidden(false);
38088            this.hiddenCount--;
38089            this.autoSizeTabs();
38090         }
38091     },
38092
38093     /**
38094      * Adds an existing {@link Roo.TabPanelItem}.
38095      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38096      */
38097     addTabItem : function(item){
38098         this.items[item.id] = item;
38099         this.items.push(item);
38100       //  if(this.resizeTabs){
38101     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38102   //         this.autoSizeTabs();
38103 //        }else{
38104 //            item.autoSize();
38105        // }
38106     },
38107
38108     /**
38109      * Removes a {@link Roo.TabPanelItem}.
38110      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38111      */
38112     removeTab : function(id){
38113         var items = this.items;
38114         var tab = items[id];
38115         if(!tab) { return; }
38116         var index = items.indexOf(tab);
38117         if(this.active == tab && items.length > 1){
38118             var newTab = this.getNextAvailable(index);
38119             if(newTab) {
38120                 newTab.activate();
38121             }
38122         }
38123         this.stripEl.dom.removeChild(tab.pnode.dom);
38124         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38125             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38126         }
38127         items.splice(index, 1);
38128         delete this.items[tab.id];
38129         tab.fireEvent("close", tab);
38130         tab.purgeListeners();
38131         this.autoSizeTabs();
38132     },
38133
38134     getNextAvailable : function(start){
38135         var items = this.items;
38136         var index = start;
38137         // look for a next tab that will slide over to
38138         // replace the one being removed
38139         while(index < items.length){
38140             var item = items[++index];
38141             if(item && !item.isHidden()){
38142                 return item;
38143             }
38144         }
38145         // if one isn't found select the previous tab (on the left)
38146         index = start;
38147         while(index >= 0){
38148             var item = items[--index];
38149             if(item && !item.isHidden()){
38150                 return item;
38151             }
38152         }
38153         return null;
38154     },
38155
38156     /**
38157      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38158      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38159      */
38160     disableTab : function(id){
38161         var tab = this.items[id];
38162         if(tab && this.active != tab){
38163             tab.disable();
38164         }
38165     },
38166
38167     /**
38168      * Enables a {@link Roo.TabPanelItem} that is disabled.
38169      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38170      */
38171     enableTab : function(id){
38172         var tab = this.items[id];
38173         tab.enable();
38174     },
38175
38176     /**
38177      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38178      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38179      * @return {Roo.TabPanelItem} The TabPanelItem.
38180      */
38181     activate : function(id){
38182         var tab = this.items[id];
38183         if(!tab){
38184             return null;
38185         }
38186         if(tab == this.active || tab.disabled){
38187             return tab;
38188         }
38189         var e = {};
38190         this.fireEvent("beforetabchange", this, e, tab);
38191         if(e.cancel !== true && !tab.disabled){
38192             if(this.active){
38193                 this.active.hide();
38194             }
38195             this.active = this.items[id];
38196             this.active.show();
38197             this.fireEvent("tabchange", this, this.active);
38198         }
38199         return tab;
38200     },
38201
38202     /**
38203      * Gets the active {@link Roo.TabPanelItem}.
38204      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38205      */
38206     getActiveTab : function(){
38207         return this.active;
38208     },
38209
38210     /**
38211      * Updates the tab body element to fit the height of the container element
38212      * for overflow scrolling
38213      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38214      */
38215     syncHeight : function(targetHeight){
38216         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38217         var bm = this.bodyEl.getMargins();
38218         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38219         this.bodyEl.setHeight(newHeight);
38220         return newHeight;
38221     },
38222
38223     onResize : function(){
38224         if(this.monitorResize){
38225             this.autoSizeTabs();
38226         }
38227     },
38228
38229     /**
38230      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38231      */
38232     beginUpdate : function(){
38233         this.updating = true;
38234     },
38235
38236     /**
38237      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38238      */
38239     endUpdate : function(){
38240         this.updating = false;
38241         this.autoSizeTabs();
38242     },
38243
38244     /**
38245      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38246      */
38247     autoSizeTabs : function(){
38248         var count = this.items.length;
38249         var vcount = count - this.hiddenCount;
38250         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38251             return;
38252         }
38253         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38254         var availWidth = Math.floor(w / vcount);
38255         var b = this.stripBody;
38256         if(b.getWidth() > w){
38257             var tabs = this.items;
38258             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38259             if(availWidth < this.minTabWidth){
38260                 /*if(!this.sleft){    // incomplete scrolling code
38261                     this.createScrollButtons();
38262                 }
38263                 this.showScroll();
38264                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38265             }
38266         }else{
38267             if(this.currentTabWidth < this.preferredTabWidth){
38268                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38269             }
38270         }
38271     },
38272
38273     /**
38274      * Returns the number of tabs in this TabPanel.
38275      * @return {Number}
38276      */
38277      getCount : function(){
38278          return this.items.length;
38279      },
38280
38281     /**
38282      * Resizes all the tabs to the passed width
38283      * @param {Number} The new width
38284      */
38285     setTabWidth : function(width){
38286         this.currentTabWidth = width;
38287         for(var i = 0, len = this.items.length; i < len; i++) {
38288                 if(!this.items[i].isHidden()) {
38289                 this.items[i].setWidth(width);
38290             }
38291         }
38292     },
38293
38294     /**
38295      * Destroys this TabPanel
38296      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38297      */
38298     destroy : function(removeEl){
38299         Roo.EventManager.removeResizeListener(this.onResize, this);
38300         for(var i = 0, len = this.items.length; i < len; i++){
38301             this.items[i].purgeListeners();
38302         }
38303         if(removeEl === true){
38304             this.el.update("");
38305             this.el.remove();
38306         }
38307     },
38308     
38309     createStrip : function(container)
38310     {
38311         var strip = document.createElement("nav");
38312         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38313         container.appendChild(strip);
38314         return strip;
38315     },
38316     
38317     createStripList : function(strip)
38318     {
38319         // div wrapper for retard IE
38320         // returns the "tr" element.
38321         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38322         //'<div class="x-tabs-strip-wrap">'+
38323           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38324           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38325         return strip.firstChild; //.firstChild.firstChild.firstChild;
38326     },
38327     createBody : function(container)
38328     {
38329         var body = document.createElement("div");
38330         Roo.id(body, "tab-body");
38331         //Roo.fly(body).addClass("x-tabs-body");
38332         Roo.fly(body).addClass("tab-content");
38333         container.appendChild(body);
38334         return body;
38335     },
38336     createItemBody :function(bodyEl, id){
38337         var body = Roo.getDom(id);
38338         if(!body){
38339             body = document.createElement("div");
38340             body.id = id;
38341         }
38342         //Roo.fly(body).addClass("x-tabs-item-body");
38343         Roo.fly(body).addClass("tab-pane");
38344          bodyEl.insertBefore(body, bodyEl.firstChild);
38345         return body;
38346     },
38347     /** @private */
38348     createStripElements :  function(stripEl, text, closable, tpl)
38349     {
38350         var td = document.createElement("li"); // was td..
38351         
38352         
38353         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38354         
38355         
38356         stripEl.appendChild(td);
38357         /*if(closable){
38358             td.className = "x-tabs-closable";
38359             if(!this.closeTpl){
38360                 this.closeTpl = new Roo.Template(
38361                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38362                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38363                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38364                 );
38365             }
38366             var el = this.closeTpl.overwrite(td, {"text": text});
38367             var close = el.getElementsByTagName("div")[0];
38368             var inner = el.getElementsByTagName("em")[0];
38369             return {"el": el, "close": close, "inner": inner};
38370         } else {
38371         */
38372         // not sure what this is..
38373 //            if(!this.tabTpl){
38374                 //this.tabTpl = new Roo.Template(
38375                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38376                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38377                 //);
38378 //                this.tabTpl = new Roo.Template(
38379 //                   '<a href="#">' +
38380 //                   '<span unselectable="on"' +
38381 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38382 //                            ' >{text}</span></a>'
38383 //                );
38384 //                
38385 //            }
38386
38387
38388             var template = tpl || this.tabTpl || false;
38389             
38390             if(!template){
38391                 
38392                 template = new Roo.Template(
38393                    '<a href="#">' +
38394                    '<span unselectable="on"' +
38395                             (this.disableTooltips ? '' : ' title="{text}"') +
38396                             ' >{text}</span></a>'
38397                 );
38398             }
38399             
38400             switch (typeof(template)) {
38401                 case 'object' :
38402                     break;
38403                 case 'string' :
38404                     template = new Roo.Template(template);
38405                     break;
38406                 default :
38407                     break;
38408             }
38409             
38410             var el = template.overwrite(td, {"text": text});
38411             
38412             var inner = el.getElementsByTagName("span")[0];
38413             
38414             return {"el": el, "inner": inner};
38415             
38416     }
38417         
38418     
38419 });
38420
38421 /**
38422  * @class Roo.TabPanelItem
38423  * @extends Roo.util.Observable
38424  * Represents an individual item (tab plus body) in a TabPanel.
38425  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38426  * @param {String} id The id of this TabPanelItem
38427  * @param {String} text The text for the tab of this TabPanelItem
38428  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38429  */
38430 Roo.bootstrap.panel.TabItem = function(config){
38431     /**
38432      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38433      * @type Roo.TabPanel
38434      */
38435     this.tabPanel = config.panel;
38436     /**
38437      * The id for this TabPanelItem
38438      * @type String
38439      */
38440     this.id = config.id;
38441     /** @private */
38442     this.disabled = false;
38443     /** @private */
38444     this.text = config.text;
38445     /** @private */
38446     this.loaded = false;
38447     this.closable = config.closable;
38448
38449     /**
38450      * The body element for this TabPanelItem.
38451      * @type Roo.Element
38452      */
38453     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38454     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38455     this.bodyEl.setStyle("display", "block");
38456     this.bodyEl.setStyle("zoom", "1");
38457     //this.hideAction();
38458
38459     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38460     /** @private */
38461     this.el = Roo.get(els.el);
38462     this.inner = Roo.get(els.inner, true);
38463     this.textEl = Roo.get(this.el.dom.firstChild, true);
38464     this.pnode = Roo.get(els.el.parentNode, true);
38465 //    this.el.on("mousedown", this.onTabMouseDown, this);
38466     this.el.on("click", this.onTabClick, this);
38467     /** @private */
38468     if(config.closable){
38469         var c = Roo.get(els.close, true);
38470         c.dom.title = this.closeText;
38471         c.addClassOnOver("close-over");
38472         c.on("click", this.closeClick, this);
38473      }
38474
38475     this.addEvents({
38476          /**
38477          * @event activate
38478          * Fires when this tab becomes the active tab.
38479          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38480          * @param {Roo.TabPanelItem} this
38481          */
38482         "activate": true,
38483         /**
38484          * @event beforeclose
38485          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38486          * @param {Roo.TabPanelItem} this
38487          * @param {Object} e Set cancel to true on this object to cancel the close.
38488          */
38489         "beforeclose": true,
38490         /**
38491          * @event close
38492          * Fires when this tab is closed.
38493          * @param {Roo.TabPanelItem} this
38494          */
38495          "close": true,
38496         /**
38497          * @event deactivate
38498          * Fires when this tab is no longer the active tab.
38499          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38500          * @param {Roo.TabPanelItem} this
38501          */
38502          "deactivate" : true
38503     });
38504     this.hidden = false;
38505
38506     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38507 };
38508
38509 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38510            {
38511     purgeListeners : function(){
38512        Roo.util.Observable.prototype.purgeListeners.call(this);
38513        this.el.removeAllListeners();
38514     },
38515     /**
38516      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38517      */
38518     show : function(){
38519         this.pnode.addClass("active");
38520         this.showAction();
38521         if(Roo.isOpera){
38522             this.tabPanel.stripWrap.repaint();
38523         }
38524         this.fireEvent("activate", this.tabPanel, this);
38525     },
38526
38527     /**
38528      * Returns true if this tab is the active tab.
38529      * @return {Boolean}
38530      */
38531     isActive : function(){
38532         return this.tabPanel.getActiveTab() == this;
38533     },
38534
38535     /**
38536      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38537      */
38538     hide : function(){
38539         this.pnode.removeClass("active");
38540         this.hideAction();
38541         this.fireEvent("deactivate", this.tabPanel, this);
38542     },
38543
38544     hideAction : function(){
38545         this.bodyEl.hide();
38546         this.bodyEl.setStyle("position", "absolute");
38547         this.bodyEl.setLeft("-20000px");
38548         this.bodyEl.setTop("-20000px");
38549     },
38550
38551     showAction : function(){
38552         this.bodyEl.setStyle("position", "relative");
38553         this.bodyEl.setTop("");
38554         this.bodyEl.setLeft("");
38555         this.bodyEl.show();
38556     },
38557
38558     /**
38559      * Set the tooltip for the tab.
38560      * @param {String} tooltip The tab's tooltip
38561      */
38562     setTooltip : function(text){
38563         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38564             this.textEl.dom.qtip = text;
38565             this.textEl.dom.removeAttribute('title');
38566         }else{
38567             this.textEl.dom.title = text;
38568         }
38569     },
38570
38571     onTabClick : function(e){
38572         e.preventDefault();
38573         this.tabPanel.activate(this.id);
38574     },
38575
38576     onTabMouseDown : function(e){
38577         e.preventDefault();
38578         this.tabPanel.activate(this.id);
38579     },
38580 /*
38581     getWidth : function(){
38582         return this.inner.getWidth();
38583     },
38584
38585     setWidth : function(width){
38586         var iwidth = width - this.pnode.getPadding("lr");
38587         this.inner.setWidth(iwidth);
38588         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38589         this.pnode.setWidth(width);
38590     },
38591 */
38592     /**
38593      * Show or hide the tab
38594      * @param {Boolean} hidden True to hide or false to show.
38595      */
38596     setHidden : function(hidden){
38597         this.hidden = hidden;
38598         this.pnode.setStyle("display", hidden ? "none" : "");
38599     },
38600
38601     /**
38602      * Returns true if this tab is "hidden"
38603      * @return {Boolean}
38604      */
38605     isHidden : function(){
38606         return this.hidden;
38607     },
38608
38609     /**
38610      * Returns the text for this tab
38611      * @return {String}
38612      */
38613     getText : function(){
38614         return this.text;
38615     },
38616     /*
38617     autoSize : function(){
38618         //this.el.beginMeasure();
38619         this.textEl.setWidth(1);
38620         /*
38621          *  #2804 [new] Tabs in Roojs
38622          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38623          */
38624         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38625         //this.el.endMeasure();
38626     //},
38627
38628     /**
38629      * Sets the text for the tab (Note: this also sets the tooltip text)
38630      * @param {String} text The tab's text and tooltip
38631      */
38632     setText : function(text){
38633         this.text = text;
38634         this.textEl.update(text);
38635         this.setTooltip(text);
38636         //if(!this.tabPanel.resizeTabs){
38637         //    this.autoSize();
38638         //}
38639     },
38640     /**
38641      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38642      */
38643     activate : function(){
38644         this.tabPanel.activate(this.id);
38645     },
38646
38647     /**
38648      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38649      */
38650     disable : function(){
38651         if(this.tabPanel.active != this){
38652             this.disabled = true;
38653             this.pnode.addClass("disabled");
38654         }
38655     },
38656
38657     /**
38658      * Enables this TabPanelItem if it was previously disabled.
38659      */
38660     enable : function(){
38661         this.disabled = false;
38662         this.pnode.removeClass("disabled");
38663     },
38664
38665     /**
38666      * Sets the content for this TabPanelItem.
38667      * @param {String} content The content
38668      * @param {Boolean} loadScripts true to look for and load scripts
38669      */
38670     setContent : function(content, loadScripts){
38671         this.bodyEl.update(content, loadScripts);
38672     },
38673
38674     /**
38675      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38676      * @return {Roo.UpdateManager} The UpdateManager
38677      */
38678     getUpdateManager : function(){
38679         return this.bodyEl.getUpdateManager();
38680     },
38681
38682     /**
38683      * Set a URL to be used to load the content for this TabPanelItem.
38684      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38685      * @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)
38686      * @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)
38687      * @return {Roo.UpdateManager} The UpdateManager
38688      */
38689     setUrl : function(url, params, loadOnce){
38690         if(this.refreshDelegate){
38691             this.un('activate', this.refreshDelegate);
38692         }
38693         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38694         this.on("activate", this.refreshDelegate);
38695         return this.bodyEl.getUpdateManager();
38696     },
38697
38698     /** @private */
38699     _handleRefresh : function(url, params, loadOnce){
38700         if(!loadOnce || !this.loaded){
38701             var updater = this.bodyEl.getUpdateManager();
38702             updater.update(url, params, this._setLoaded.createDelegate(this));
38703         }
38704     },
38705
38706     /**
38707      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38708      *   Will fail silently if the setUrl method has not been called.
38709      *   This does not activate the panel, just updates its content.
38710      */
38711     refresh : function(){
38712         if(this.refreshDelegate){
38713            this.loaded = false;
38714            this.refreshDelegate();
38715         }
38716     },
38717
38718     /** @private */
38719     _setLoaded : function(){
38720         this.loaded = true;
38721     },
38722
38723     /** @private */
38724     closeClick : function(e){
38725         var o = {};
38726         e.stopEvent();
38727         this.fireEvent("beforeclose", this, o);
38728         if(o.cancel !== true){
38729             this.tabPanel.removeTab(this.id);
38730         }
38731     },
38732     /**
38733      * The text displayed in the tooltip for the close icon.
38734      * @type String
38735      */
38736     closeText : "Close this tab"
38737 });
38738 /**
38739 *    This script refer to:
38740 *    Title: International Telephone Input
38741 *    Author: Jack O'Connor
38742 *    Code version:  v12.1.12
38743 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38744 **/
38745
38746 Roo.bootstrap.PhoneInputData = function() {
38747     var d = [
38748       [
38749         "Afghanistan (‫افغانستان‬‎)",
38750         "af",
38751         "93"
38752       ],
38753       [
38754         "Albania (Shqipëri)",
38755         "al",
38756         "355"
38757       ],
38758       [
38759         "Algeria (‫الجزائر‬‎)",
38760         "dz",
38761         "213"
38762       ],
38763       [
38764         "American Samoa",
38765         "as",
38766         "1684"
38767       ],
38768       [
38769         "Andorra",
38770         "ad",
38771         "376"
38772       ],
38773       [
38774         "Angola",
38775         "ao",
38776         "244"
38777       ],
38778       [
38779         "Anguilla",
38780         "ai",
38781         "1264"
38782       ],
38783       [
38784         "Antigua and Barbuda",
38785         "ag",
38786         "1268"
38787       ],
38788       [
38789         "Argentina",
38790         "ar",
38791         "54"
38792       ],
38793       [
38794         "Armenia (Հայաստան)",
38795         "am",
38796         "374"
38797       ],
38798       [
38799         "Aruba",
38800         "aw",
38801         "297"
38802       ],
38803       [
38804         "Australia",
38805         "au",
38806         "61",
38807         0
38808       ],
38809       [
38810         "Austria (Österreich)",
38811         "at",
38812         "43"
38813       ],
38814       [
38815         "Azerbaijan (Azərbaycan)",
38816         "az",
38817         "994"
38818       ],
38819       [
38820         "Bahamas",
38821         "bs",
38822         "1242"
38823       ],
38824       [
38825         "Bahrain (‫البحرين‬‎)",
38826         "bh",
38827         "973"
38828       ],
38829       [
38830         "Bangladesh (বাংলাদেশ)",
38831         "bd",
38832         "880"
38833       ],
38834       [
38835         "Barbados",
38836         "bb",
38837         "1246"
38838       ],
38839       [
38840         "Belarus (Беларусь)",
38841         "by",
38842         "375"
38843       ],
38844       [
38845         "Belgium (België)",
38846         "be",
38847         "32"
38848       ],
38849       [
38850         "Belize",
38851         "bz",
38852         "501"
38853       ],
38854       [
38855         "Benin (Bénin)",
38856         "bj",
38857         "229"
38858       ],
38859       [
38860         "Bermuda",
38861         "bm",
38862         "1441"
38863       ],
38864       [
38865         "Bhutan (འབྲུག)",
38866         "bt",
38867         "975"
38868       ],
38869       [
38870         "Bolivia",
38871         "bo",
38872         "591"
38873       ],
38874       [
38875         "Bosnia and Herzegovina (Босна и Херцеговина)",
38876         "ba",
38877         "387"
38878       ],
38879       [
38880         "Botswana",
38881         "bw",
38882         "267"
38883       ],
38884       [
38885         "Brazil (Brasil)",
38886         "br",
38887         "55"
38888       ],
38889       [
38890         "British Indian Ocean Territory",
38891         "io",
38892         "246"
38893       ],
38894       [
38895         "British Virgin Islands",
38896         "vg",
38897         "1284"
38898       ],
38899       [
38900         "Brunei",
38901         "bn",
38902         "673"
38903       ],
38904       [
38905         "Bulgaria (България)",
38906         "bg",
38907         "359"
38908       ],
38909       [
38910         "Burkina Faso",
38911         "bf",
38912         "226"
38913       ],
38914       [
38915         "Burundi (Uburundi)",
38916         "bi",
38917         "257"
38918       ],
38919       [
38920         "Cambodia (កម្ពុជា)",
38921         "kh",
38922         "855"
38923       ],
38924       [
38925         "Cameroon (Cameroun)",
38926         "cm",
38927         "237"
38928       ],
38929       [
38930         "Canada",
38931         "ca",
38932         "1",
38933         1,
38934         ["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"]
38935       ],
38936       [
38937         "Cape Verde (Kabu Verdi)",
38938         "cv",
38939         "238"
38940       ],
38941       [
38942         "Caribbean Netherlands",
38943         "bq",
38944         "599",
38945         1
38946       ],
38947       [
38948         "Cayman Islands",
38949         "ky",
38950         "1345"
38951       ],
38952       [
38953         "Central African Republic (République centrafricaine)",
38954         "cf",
38955         "236"
38956       ],
38957       [
38958         "Chad (Tchad)",
38959         "td",
38960         "235"
38961       ],
38962       [
38963         "Chile",
38964         "cl",
38965         "56"
38966       ],
38967       [
38968         "China (中国)",
38969         "cn",
38970         "86"
38971       ],
38972       [
38973         "Christmas Island",
38974         "cx",
38975         "61",
38976         2
38977       ],
38978       [
38979         "Cocos (Keeling) Islands",
38980         "cc",
38981         "61",
38982         1
38983       ],
38984       [
38985         "Colombia",
38986         "co",
38987         "57"
38988       ],
38989       [
38990         "Comoros (‫جزر القمر‬‎)",
38991         "km",
38992         "269"
38993       ],
38994       [
38995         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38996         "cd",
38997         "243"
38998       ],
38999       [
39000         "Congo (Republic) (Congo-Brazzaville)",
39001         "cg",
39002         "242"
39003       ],
39004       [
39005         "Cook Islands",
39006         "ck",
39007         "682"
39008       ],
39009       [
39010         "Costa Rica",
39011         "cr",
39012         "506"
39013       ],
39014       [
39015         "Côte d’Ivoire",
39016         "ci",
39017         "225"
39018       ],
39019       [
39020         "Croatia (Hrvatska)",
39021         "hr",
39022         "385"
39023       ],
39024       [
39025         "Cuba",
39026         "cu",
39027         "53"
39028       ],
39029       [
39030         "Curaçao",
39031         "cw",
39032         "599",
39033         0
39034       ],
39035       [
39036         "Cyprus (Κύπρος)",
39037         "cy",
39038         "357"
39039       ],
39040       [
39041         "Czech Republic (Česká republika)",
39042         "cz",
39043         "420"
39044       ],
39045       [
39046         "Denmark (Danmark)",
39047         "dk",
39048         "45"
39049       ],
39050       [
39051         "Djibouti",
39052         "dj",
39053         "253"
39054       ],
39055       [
39056         "Dominica",
39057         "dm",
39058         "1767"
39059       ],
39060       [
39061         "Dominican Republic (República Dominicana)",
39062         "do",
39063         "1",
39064         2,
39065         ["809", "829", "849"]
39066       ],
39067       [
39068         "Ecuador",
39069         "ec",
39070         "593"
39071       ],
39072       [
39073         "Egypt (‫مصر‬‎)",
39074         "eg",
39075         "20"
39076       ],
39077       [
39078         "El Salvador",
39079         "sv",
39080         "503"
39081       ],
39082       [
39083         "Equatorial Guinea (Guinea Ecuatorial)",
39084         "gq",
39085         "240"
39086       ],
39087       [
39088         "Eritrea",
39089         "er",
39090         "291"
39091       ],
39092       [
39093         "Estonia (Eesti)",
39094         "ee",
39095         "372"
39096       ],
39097       [
39098         "Ethiopia",
39099         "et",
39100         "251"
39101       ],
39102       [
39103         "Falkland Islands (Islas Malvinas)",
39104         "fk",
39105         "500"
39106       ],
39107       [
39108         "Faroe Islands (Føroyar)",
39109         "fo",
39110         "298"
39111       ],
39112       [
39113         "Fiji",
39114         "fj",
39115         "679"
39116       ],
39117       [
39118         "Finland (Suomi)",
39119         "fi",
39120         "358",
39121         0
39122       ],
39123       [
39124         "France",
39125         "fr",
39126         "33"
39127       ],
39128       [
39129         "French Guiana (Guyane française)",
39130         "gf",
39131         "594"
39132       ],
39133       [
39134         "French Polynesia (Polynésie française)",
39135         "pf",
39136         "689"
39137       ],
39138       [
39139         "Gabon",
39140         "ga",
39141         "241"
39142       ],
39143       [
39144         "Gambia",
39145         "gm",
39146         "220"
39147       ],
39148       [
39149         "Georgia (საქართველო)",
39150         "ge",
39151         "995"
39152       ],
39153       [
39154         "Germany (Deutschland)",
39155         "de",
39156         "49"
39157       ],
39158       [
39159         "Ghana (Gaana)",
39160         "gh",
39161         "233"
39162       ],
39163       [
39164         "Gibraltar",
39165         "gi",
39166         "350"
39167       ],
39168       [
39169         "Greece (Ελλάδα)",
39170         "gr",
39171         "30"
39172       ],
39173       [
39174         "Greenland (Kalaallit Nunaat)",
39175         "gl",
39176         "299"
39177       ],
39178       [
39179         "Grenada",
39180         "gd",
39181         "1473"
39182       ],
39183       [
39184         "Guadeloupe",
39185         "gp",
39186         "590",
39187         0
39188       ],
39189       [
39190         "Guam",
39191         "gu",
39192         "1671"
39193       ],
39194       [
39195         "Guatemala",
39196         "gt",
39197         "502"
39198       ],
39199       [
39200         "Guernsey",
39201         "gg",
39202         "44",
39203         1
39204       ],
39205       [
39206         "Guinea (Guinée)",
39207         "gn",
39208         "224"
39209       ],
39210       [
39211         "Guinea-Bissau (Guiné Bissau)",
39212         "gw",
39213         "245"
39214       ],
39215       [
39216         "Guyana",
39217         "gy",
39218         "592"
39219       ],
39220       [
39221         "Haiti",
39222         "ht",
39223         "509"
39224       ],
39225       [
39226         "Honduras",
39227         "hn",
39228         "504"
39229       ],
39230       [
39231         "Hong Kong (香港)",
39232         "hk",
39233         "852"
39234       ],
39235       [
39236         "Hungary (Magyarország)",
39237         "hu",
39238         "36"
39239       ],
39240       [
39241         "Iceland (Ísland)",
39242         "is",
39243         "354"
39244       ],
39245       [
39246         "India (भारत)",
39247         "in",
39248         "91"
39249       ],
39250       [
39251         "Indonesia",
39252         "id",
39253         "62"
39254       ],
39255       [
39256         "Iran (‫ایران‬‎)",
39257         "ir",
39258         "98"
39259       ],
39260       [
39261         "Iraq (‫العراق‬‎)",
39262         "iq",
39263         "964"
39264       ],
39265       [
39266         "Ireland",
39267         "ie",
39268         "353"
39269       ],
39270       [
39271         "Isle of Man",
39272         "im",
39273         "44",
39274         2
39275       ],
39276       [
39277         "Israel (‫ישראל‬‎)",
39278         "il",
39279         "972"
39280       ],
39281       [
39282         "Italy (Italia)",
39283         "it",
39284         "39",
39285         0
39286       ],
39287       [
39288         "Jamaica",
39289         "jm",
39290         "1876"
39291       ],
39292       [
39293         "Japan (日本)",
39294         "jp",
39295         "81"
39296       ],
39297       [
39298         "Jersey",
39299         "je",
39300         "44",
39301         3
39302       ],
39303       [
39304         "Jordan (‫الأردن‬‎)",
39305         "jo",
39306         "962"
39307       ],
39308       [
39309         "Kazakhstan (Казахстан)",
39310         "kz",
39311         "7",
39312         1
39313       ],
39314       [
39315         "Kenya",
39316         "ke",
39317         "254"
39318       ],
39319       [
39320         "Kiribati",
39321         "ki",
39322         "686"
39323       ],
39324       [
39325         "Kosovo",
39326         "xk",
39327         "383"
39328       ],
39329       [
39330         "Kuwait (‫الكويت‬‎)",
39331         "kw",
39332         "965"
39333       ],
39334       [
39335         "Kyrgyzstan (Кыргызстан)",
39336         "kg",
39337         "996"
39338       ],
39339       [
39340         "Laos (ລາວ)",
39341         "la",
39342         "856"
39343       ],
39344       [
39345         "Latvia (Latvija)",
39346         "lv",
39347         "371"
39348       ],
39349       [
39350         "Lebanon (‫لبنان‬‎)",
39351         "lb",
39352         "961"
39353       ],
39354       [
39355         "Lesotho",
39356         "ls",
39357         "266"
39358       ],
39359       [
39360         "Liberia",
39361         "lr",
39362         "231"
39363       ],
39364       [
39365         "Libya (‫ليبيا‬‎)",
39366         "ly",
39367         "218"
39368       ],
39369       [
39370         "Liechtenstein",
39371         "li",
39372         "423"
39373       ],
39374       [
39375         "Lithuania (Lietuva)",
39376         "lt",
39377         "370"
39378       ],
39379       [
39380         "Luxembourg",
39381         "lu",
39382         "352"
39383       ],
39384       [
39385         "Macau (澳門)",
39386         "mo",
39387         "853"
39388       ],
39389       [
39390         "Macedonia (FYROM) (Македонија)",
39391         "mk",
39392         "389"
39393       ],
39394       [
39395         "Madagascar (Madagasikara)",
39396         "mg",
39397         "261"
39398       ],
39399       [
39400         "Malawi",
39401         "mw",
39402         "265"
39403       ],
39404       [
39405         "Malaysia",
39406         "my",
39407         "60"
39408       ],
39409       [
39410         "Maldives",
39411         "mv",
39412         "960"
39413       ],
39414       [
39415         "Mali",
39416         "ml",
39417         "223"
39418       ],
39419       [
39420         "Malta",
39421         "mt",
39422         "356"
39423       ],
39424       [
39425         "Marshall Islands",
39426         "mh",
39427         "692"
39428       ],
39429       [
39430         "Martinique",
39431         "mq",
39432         "596"
39433       ],
39434       [
39435         "Mauritania (‫موريتانيا‬‎)",
39436         "mr",
39437         "222"
39438       ],
39439       [
39440         "Mauritius (Moris)",
39441         "mu",
39442         "230"
39443       ],
39444       [
39445         "Mayotte",
39446         "yt",
39447         "262",
39448         1
39449       ],
39450       [
39451         "Mexico (México)",
39452         "mx",
39453         "52"
39454       ],
39455       [
39456         "Micronesia",
39457         "fm",
39458         "691"
39459       ],
39460       [
39461         "Moldova (Republica Moldova)",
39462         "md",
39463         "373"
39464       ],
39465       [
39466         "Monaco",
39467         "mc",
39468         "377"
39469       ],
39470       [
39471         "Mongolia (Монгол)",
39472         "mn",
39473         "976"
39474       ],
39475       [
39476         "Montenegro (Crna Gora)",
39477         "me",
39478         "382"
39479       ],
39480       [
39481         "Montserrat",
39482         "ms",
39483         "1664"
39484       ],
39485       [
39486         "Morocco (‫المغرب‬‎)",
39487         "ma",
39488         "212",
39489         0
39490       ],
39491       [
39492         "Mozambique (Moçambique)",
39493         "mz",
39494         "258"
39495       ],
39496       [
39497         "Myanmar (Burma) (မြန်မာ)",
39498         "mm",
39499         "95"
39500       ],
39501       [
39502         "Namibia (Namibië)",
39503         "na",
39504         "264"
39505       ],
39506       [
39507         "Nauru",
39508         "nr",
39509         "674"
39510       ],
39511       [
39512         "Nepal (नेपाल)",
39513         "np",
39514         "977"
39515       ],
39516       [
39517         "Netherlands (Nederland)",
39518         "nl",
39519         "31"
39520       ],
39521       [
39522         "New Caledonia (Nouvelle-Calédonie)",
39523         "nc",
39524         "687"
39525       ],
39526       [
39527         "New Zealand",
39528         "nz",
39529         "64"
39530       ],
39531       [
39532         "Nicaragua",
39533         "ni",
39534         "505"
39535       ],
39536       [
39537         "Niger (Nijar)",
39538         "ne",
39539         "227"
39540       ],
39541       [
39542         "Nigeria",
39543         "ng",
39544         "234"
39545       ],
39546       [
39547         "Niue",
39548         "nu",
39549         "683"
39550       ],
39551       [
39552         "Norfolk Island",
39553         "nf",
39554         "672"
39555       ],
39556       [
39557         "North Korea (조선 민주주의 인민 공화국)",
39558         "kp",
39559         "850"
39560       ],
39561       [
39562         "Northern Mariana Islands",
39563         "mp",
39564         "1670"
39565       ],
39566       [
39567         "Norway (Norge)",
39568         "no",
39569         "47",
39570         0
39571       ],
39572       [
39573         "Oman (‫عُمان‬‎)",
39574         "om",
39575         "968"
39576       ],
39577       [
39578         "Pakistan (‫پاکستان‬‎)",
39579         "pk",
39580         "92"
39581       ],
39582       [
39583         "Palau",
39584         "pw",
39585         "680"
39586       ],
39587       [
39588         "Palestine (‫فلسطين‬‎)",
39589         "ps",
39590         "970"
39591       ],
39592       [
39593         "Panama (Panamá)",
39594         "pa",
39595         "507"
39596       ],
39597       [
39598         "Papua New Guinea",
39599         "pg",
39600         "675"
39601       ],
39602       [
39603         "Paraguay",
39604         "py",
39605         "595"
39606       ],
39607       [
39608         "Peru (Perú)",
39609         "pe",
39610         "51"
39611       ],
39612       [
39613         "Philippines",
39614         "ph",
39615         "63"
39616       ],
39617       [
39618         "Poland (Polska)",
39619         "pl",
39620         "48"
39621       ],
39622       [
39623         "Portugal",
39624         "pt",
39625         "351"
39626       ],
39627       [
39628         "Puerto Rico",
39629         "pr",
39630         "1",
39631         3,
39632         ["787", "939"]
39633       ],
39634       [
39635         "Qatar (‫قطر‬‎)",
39636         "qa",
39637         "974"
39638       ],
39639       [
39640         "Réunion (La Réunion)",
39641         "re",
39642         "262",
39643         0
39644       ],
39645       [
39646         "Romania (România)",
39647         "ro",
39648         "40"
39649       ],
39650       [
39651         "Russia (Россия)",
39652         "ru",
39653         "7",
39654         0
39655       ],
39656       [
39657         "Rwanda",
39658         "rw",
39659         "250"
39660       ],
39661       [
39662         "Saint Barthélemy",
39663         "bl",
39664         "590",
39665         1
39666       ],
39667       [
39668         "Saint Helena",
39669         "sh",
39670         "290"
39671       ],
39672       [
39673         "Saint Kitts and Nevis",
39674         "kn",
39675         "1869"
39676       ],
39677       [
39678         "Saint Lucia",
39679         "lc",
39680         "1758"
39681       ],
39682       [
39683         "Saint Martin (Saint-Martin (partie française))",
39684         "mf",
39685         "590",
39686         2
39687       ],
39688       [
39689         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39690         "pm",
39691         "508"
39692       ],
39693       [
39694         "Saint Vincent and the Grenadines",
39695         "vc",
39696         "1784"
39697       ],
39698       [
39699         "Samoa",
39700         "ws",
39701         "685"
39702       ],
39703       [
39704         "San Marino",
39705         "sm",
39706         "378"
39707       ],
39708       [
39709         "São Tomé and Príncipe (São Tomé e Príncipe)",
39710         "st",
39711         "239"
39712       ],
39713       [
39714         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39715         "sa",
39716         "966"
39717       ],
39718       [
39719         "Senegal (Sénégal)",
39720         "sn",
39721         "221"
39722       ],
39723       [
39724         "Serbia (Србија)",
39725         "rs",
39726         "381"
39727       ],
39728       [
39729         "Seychelles",
39730         "sc",
39731         "248"
39732       ],
39733       [
39734         "Sierra Leone",
39735         "sl",
39736         "232"
39737       ],
39738       [
39739         "Singapore",
39740         "sg",
39741         "65"
39742       ],
39743       [
39744         "Sint Maarten",
39745         "sx",
39746         "1721"
39747       ],
39748       [
39749         "Slovakia (Slovensko)",
39750         "sk",
39751         "421"
39752       ],
39753       [
39754         "Slovenia (Slovenija)",
39755         "si",
39756         "386"
39757       ],
39758       [
39759         "Solomon Islands",
39760         "sb",
39761         "677"
39762       ],
39763       [
39764         "Somalia (Soomaaliya)",
39765         "so",
39766         "252"
39767       ],
39768       [
39769         "South Africa",
39770         "za",
39771         "27"
39772       ],
39773       [
39774         "South Korea (대한민국)",
39775         "kr",
39776         "82"
39777       ],
39778       [
39779         "South Sudan (‫جنوب السودان‬‎)",
39780         "ss",
39781         "211"
39782       ],
39783       [
39784         "Spain (España)",
39785         "es",
39786         "34"
39787       ],
39788       [
39789         "Sri Lanka (ශ්‍රී ලංකාව)",
39790         "lk",
39791         "94"
39792       ],
39793       [
39794         "Sudan (‫السودان‬‎)",
39795         "sd",
39796         "249"
39797       ],
39798       [
39799         "Suriname",
39800         "sr",
39801         "597"
39802       ],
39803       [
39804         "Svalbard and Jan Mayen",
39805         "sj",
39806         "47",
39807         1
39808       ],
39809       [
39810         "Swaziland",
39811         "sz",
39812         "268"
39813       ],
39814       [
39815         "Sweden (Sverige)",
39816         "se",
39817         "46"
39818       ],
39819       [
39820         "Switzerland (Schweiz)",
39821         "ch",
39822         "41"
39823       ],
39824       [
39825         "Syria (‫سوريا‬‎)",
39826         "sy",
39827         "963"
39828       ],
39829       [
39830         "Taiwan (台灣)",
39831         "tw",
39832         "886"
39833       ],
39834       [
39835         "Tajikistan",
39836         "tj",
39837         "992"
39838       ],
39839       [
39840         "Tanzania",
39841         "tz",
39842         "255"
39843       ],
39844       [
39845         "Thailand (ไทย)",
39846         "th",
39847         "66"
39848       ],
39849       [
39850         "Timor-Leste",
39851         "tl",
39852         "670"
39853       ],
39854       [
39855         "Togo",
39856         "tg",
39857         "228"
39858       ],
39859       [
39860         "Tokelau",
39861         "tk",
39862         "690"
39863       ],
39864       [
39865         "Tonga",
39866         "to",
39867         "676"
39868       ],
39869       [
39870         "Trinidad and Tobago",
39871         "tt",
39872         "1868"
39873       ],
39874       [
39875         "Tunisia (‫تونس‬‎)",
39876         "tn",
39877         "216"
39878       ],
39879       [
39880         "Turkey (Türkiye)",
39881         "tr",
39882         "90"
39883       ],
39884       [
39885         "Turkmenistan",
39886         "tm",
39887         "993"
39888       ],
39889       [
39890         "Turks and Caicos Islands",
39891         "tc",
39892         "1649"
39893       ],
39894       [
39895         "Tuvalu",
39896         "tv",
39897         "688"
39898       ],
39899       [
39900         "U.S. Virgin Islands",
39901         "vi",
39902         "1340"
39903       ],
39904       [
39905         "Uganda",
39906         "ug",
39907         "256"
39908       ],
39909       [
39910         "Ukraine (Україна)",
39911         "ua",
39912         "380"
39913       ],
39914       [
39915         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39916         "ae",
39917         "971"
39918       ],
39919       [
39920         "United Kingdom",
39921         "gb",
39922         "44",
39923         0
39924       ],
39925       [
39926         "United States",
39927         "us",
39928         "1",
39929         0
39930       ],
39931       [
39932         "Uruguay",
39933         "uy",
39934         "598"
39935       ],
39936       [
39937         "Uzbekistan (Oʻzbekiston)",
39938         "uz",
39939         "998"
39940       ],
39941       [
39942         "Vanuatu",
39943         "vu",
39944         "678"
39945       ],
39946       [
39947         "Vatican City (Città del Vaticano)",
39948         "va",
39949         "39",
39950         1
39951       ],
39952       [
39953         "Venezuela",
39954         "ve",
39955         "58"
39956       ],
39957       [
39958         "Vietnam (Việt Nam)",
39959         "vn",
39960         "84"
39961       ],
39962       [
39963         "Wallis and Futuna (Wallis-et-Futuna)",
39964         "wf",
39965         "681"
39966       ],
39967       [
39968         "Western Sahara (‫الصحراء الغربية‬‎)",
39969         "eh",
39970         "212",
39971         1
39972       ],
39973       [
39974         "Yemen (‫اليمن‬‎)",
39975         "ye",
39976         "967"
39977       ],
39978       [
39979         "Zambia",
39980         "zm",
39981         "260"
39982       ],
39983       [
39984         "Zimbabwe",
39985         "zw",
39986         "263"
39987       ],
39988       [
39989         "Åland Islands",
39990         "ax",
39991         "358",
39992         1
39993       ]
39994   ];
39995   
39996   return d;
39997 }/**
39998 *    This script refer to:
39999 *    Title: International Telephone Input
40000 *    Author: Jack O'Connor
40001 *    Code version:  v12.1.12
40002 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40003 **/
40004
40005 /**
40006  * @class Roo.bootstrap.PhoneInput
40007  * @extends Roo.bootstrap.TriggerField
40008  * An input with International dial-code selection
40009  
40010  * @cfg {String} defaultDialCode default '+852'
40011  * @cfg {Array} preferedCountries default []
40012   
40013  * @constructor
40014  * Create a new PhoneInput.
40015  * @param {Object} config Configuration options
40016  */
40017
40018 Roo.bootstrap.PhoneInput = function(config) {
40019     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40020 };
40021
40022 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40023         
40024         listWidth: undefined,
40025         
40026         selectedClass: 'active',
40027         
40028         invalidClass : "has-warning",
40029         
40030         validClass: 'has-success',
40031         
40032         allowed: '0123456789',
40033         
40034         max_length: 15,
40035         
40036         /**
40037          * @cfg {String} defaultDialCode The default dial code when initializing the input
40038          */
40039         defaultDialCode: '+852',
40040         
40041         /**
40042          * @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
40043          */
40044         preferedCountries: false,
40045         
40046         getAutoCreate : function()
40047         {
40048             var data = Roo.bootstrap.PhoneInputData();
40049             var align = this.labelAlign || this.parentLabelAlign();
40050             var id = Roo.id();
40051             
40052             this.allCountries = [];
40053             this.dialCodeMapping = [];
40054             
40055             for (var i = 0; i < data.length; i++) {
40056               var c = data[i];
40057               this.allCountries[i] = {
40058                 name: c[0],
40059                 iso2: c[1],
40060                 dialCode: c[2],
40061                 priority: c[3] || 0,
40062                 areaCodes: c[4] || null
40063               };
40064               this.dialCodeMapping[c[2]] = {
40065                   name: c[0],
40066                   iso2: c[1],
40067                   priority: c[3] || 0,
40068                   areaCodes: c[4] || null
40069               };
40070             }
40071             
40072             var cfg = {
40073                 cls: 'form-group',
40074                 cn: []
40075             };
40076             
40077             var input =  {
40078                 tag: 'input',
40079                 id : id,
40080                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40081                 maxlength: this.max_length,
40082                 cls : 'form-control tel-input',
40083                 autocomplete: 'new-password'
40084             };
40085             
40086             var hiddenInput = {
40087                 tag: 'input',
40088                 type: 'hidden',
40089                 cls: 'hidden-tel-input'
40090             };
40091             
40092             if (this.name) {
40093                 hiddenInput.name = this.name;
40094             }
40095             
40096             if (this.disabled) {
40097                 input.disabled = true;
40098             }
40099             
40100             var flag_container = {
40101                 tag: 'div',
40102                 cls: 'flag-box',
40103                 cn: [
40104                     {
40105                         tag: 'div',
40106                         cls: 'flag'
40107                     },
40108                     {
40109                         tag: 'div',
40110                         cls: 'caret'
40111                     }
40112                 ]
40113             };
40114             
40115             var box = {
40116                 tag: 'div',
40117                 cls: this.hasFeedback ? 'has-feedback' : '',
40118                 cn: [
40119                     hiddenInput,
40120                     input,
40121                     {
40122                         tag: 'input',
40123                         cls: 'dial-code-holder',
40124                         disabled: true
40125                     }
40126                 ]
40127             };
40128             
40129             var container = {
40130                 cls: 'roo-select2-container input-group',
40131                 cn: [
40132                     flag_container,
40133                     box
40134                 ]
40135             };
40136             
40137             if (this.fieldLabel.length) {
40138                 var indicator = {
40139                     tag: 'i',
40140                     tooltip: 'This field is required'
40141                 };
40142                 
40143                 var label = {
40144                     tag: 'label',
40145                     'for':  id,
40146                     cls: 'control-label',
40147                     cn: []
40148                 };
40149                 
40150                 var label_text = {
40151                     tag: 'span',
40152                     html: this.fieldLabel
40153                 };
40154                 
40155                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40156                 label.cn = [
40157                     indicator,
40158                     label_text
40159                 ];
40160                 
40161                 if(this.indicatorpos == 'right') {
40162                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40163                     label.cn = [
40164                         label_text,
40165                         indicator
40166                     ];
40167                 }
40168                 
40169                 if(align == 'left') {
40170                     container = {
40171                         tag: 'div',
40172                         cn: [
40173                             container
40174                         ]
40175                     };
40176                     
40177                     if(this.labelWidth > 12){
40178                         label.style = "width: " + this.labelWidth + 'px';
40179                     }
40180                     if(this.labelWidth < 13 && this.labelmd == 0){
40181                         this.labelmd = this.labelWidth;
40182                     }
40183                     if(this.labellg > 0){
40184                         label.cls += ' col-lg-' + this.labellg;
40185                         input.cls += ' col-lg-' + (12 - this.labellg);
40186                     }
40187                     if(this.labelmd > 0){
40188                         label.cls += ' col-md-' + this.labelmd;
40189                         container.cls += ' col-md-' + (12 - this.labelmd);
40190                     }
40191                     if(this.labelsm > 0){
40192                         label.cls += ' col-sm-' + this.labelsm;
40193                         container.cls += ' col-sm-' + (12 - this.labelsm);
40194                     }
40195                     if(this.labelxs > 0){
40196                         label.cls += ' col-xs-' + this.labelxs;
40197                         container.cls += ' col-xs-' + (12 - this.labelxs);
40198                     }
40199                 }
40200             }
40201             
40202             cfg.cn = [
40203                 label,
40204                 container
40205             ];
40206             
40207             var settings = this;
40208             
40209             ['xs','sm','md','lg'].map(function(size){
40210                 if (settings[size]) {
40211                     cfg.cls += ' col-' + size + '-' + settings[size];
40212                 }
40213             });
40214             
40215             this.store = new Roo.data.Store({
40216                 proxy : new Roo.data.MemoryProxy({}),
40217                 reader : new Roo.data.JsonReader({
40218                     fields : [
40219                         {
40220                             'name' : 'name',
40221                             'type' : 'string'
40222                         },
40223                         {
40224                             'name' : 'iso2',
40225                             'type' : 'string'
40226                         },
40227                         {
40228                             'name' : 'dialCode',
40229                             'type' : 'string'
40230                         },
40231                         {
40232                             'name' : 'priority',
40233                             'type' : 'string'
40234                         },
40235                         {
40236                             'name' : 'areaCodes',
40237                             'type' : 'string'
40238                         }
40239                     ]
40240                 })
40241             });
40242             
40243             if(!this.preferedCountries) {
40244                 this.preferedCountries = [
40245                     'hk',
40246                     'gb',
40247                     'us'
40248                 ];
40249             }
40250             
40251             var p = this.preferedCountries.reverse();
40252             
40253             if(p) {
40254                 for (var i = 0; i < p.length; i++) {
40255                     for (var j = 0; j < this.allCountries.length; j++) {
40256                         if(this.allCountries[j].iso2 == p[i]) {
40257                             var t = this.allCountries[j];
40258                             this.allCountries.splice(j,1);
40259                             this.allCountries.unshift(t);
40260                         }
40261                     } 
40262                 }
40263             }
40264             
40265             this.store.proxy.data = {
40266                 success: true,
40267                 data: this.allCountries
40268             };
40269             
40270             return cfg;
40271         },
40272         
40273         initEvents : function()
40274         {
40275             this.createList();
40276             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40277             
40278             this.indicator = this.indicatorEl();
40279             this.flag = this.flagEl();
40280             this.dialCodeHolder = this.dialCodeHolderEl();
40281             
40282             this.trigger = this.el.select('div.flag-box',true).first();
40283             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40284             
40285             var _this = this;
40286             
40287             (function(){
40288                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40289                 _this.list.setWidth(lw);
40290             }).defer(100);
40291             
40292             this.list.on('mouseover', this.onViewOver, this);
40293             this.list.on('mousemove', this.onViewMove, this);
40294             this.inputEl().on("keyup", this.onKeyUp, this);
40295             this.inputEl().on("keypress", this.onKeyPress, this);
40296             
40297             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40298
40299             this.view = new Roo.View(this.list, this.tpl, {
40300                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40301             });
40302             
40303             this.view.on('click', this.onViewClick, this);
40304             this.setValue(this.defaultDialCode);
40305         },
40306         
40307         onTriggerClick : function(e)
40308         {
40309             Roo.log('trigger click');
40310             if(this.disabled){
40311                 return;
40312             }
40313             
40314             if(this.isExpanded()){
40315                 this.collapse();
40316                 this.hasFocus = false;
40317             }else {
40318                 this.store.load({});
40319                 this.hasFocus = true;
40320                 this.expand();
40321             }
40322         },
40323         
40324         isExpanded : function()
40325         {
40326             return this.list.isVisible();
40327         },
40328         
40329         collapse : function()
40330         {
40331             if(!this.isExpanded()){
40332                 return;
40333             }
40334             this.list.hide();
40335             Roo.get(document).un('mousedown', this.collapseIf, this);
40336             Roo.get(document).un('mousewheel', this.collapseIf, this);
40337             this.fireEvent('collapse', this);
40338             this.validate();
40339         },
40340         
40341         expand : function()
40342         {
40343             Roo.log('expand');
40344
40345             if(this.isExpanded() || !this.hasFocus){
40346                 return;
40347             }
40348             
40349             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40350             this.list.setWidth(lw);
40351             
40352             this.list.show();
40353             this.restrictHeight();
40354             
40355             Roo.get(document).on('mousedown', this.collapseIf, this);
40356             Roo.get(document).on('mousewheel', this.collapseIf, this);
40357             
40358             this.fireEvent('expand', this);
40359         },
40360         
40361         restrictHeight : function()
40362         {
40363             this.list.alignTo(this.inputEl(), this.listAlign);
40364             this.list.alignTo(this.inputEl(), this.listAlign);
40365         },
40366         
40367         onViewOver : function(e, t)
40368         {
40369             if(this.inKeyMode){
40370                 return;
40371             }
40372             var item = this.view.findItemFromChild(t);
40373             
40374             if(item){
40375                 var index = this.view.indexOf(item);
40376                 this.select(index, false);
40377             }
40378         },
40379
40380         // private
40381         onViewClick : function(view, doFocus, el, e)
40382         {
40383             var index = this.view.getSelectedIndexes()[0];
40384             
40385             var r = this.store.getAt(index);
40386             
40387             if(r){
40388                 this.onSelect(r, index);
40389             }
40390             if(doFocus !== false && !this.blockFocus){
40391                 this.inputEl().focus();
40392             }
40393         },
40394         
40395         onViewMove : function(e, t)
40396         {
40397             this.inKeyMode = false;
40398         },
40399         
40400         select : function(index, scrollIntoView)
40401         {
40402             this.selectedIndex = index;
40403             this.view.select(index);
40404             if(scrollIntoView !== false){
40405                 var el = this.view.getNode(index);
40406                 if(el){
40407                     this.list.scrollChildIntoView(el, false);
40408                 }
40409             }
40410         },
40411         
40412         createList : function()
40413         {
40414             this.list = Roo.get(document.body).createChild({
40415                 tag: 'ul',
40416                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40417                 style: 'display:none'
40418             });
40419             
40420             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40421         },
40422         
40423         collapseIf : function(e)
40424         {
40425             var in_combo  = e.within(this.el);
40426             var in_list =  e.within(this.list);
40427             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40428             
40429             if (in_combo || in_list || is_list) {
40430                 return;
40431             }
40432             this.collapse();
40433         },
40434         
40435         onSelect : function(record, index)
40436         {
40437             if(this.fireEvent('beforeselect', this, record, index) !== false){
40438                 
40439                 this.setFlagClass(record.data.iso2);
40440                 this.setDialCode(record.data.dialCode);
40441                 this.hasFocus = false;
40442                 this.collapse();
40443                 this.fireEvent('select', this, record, index);
40444             }
40445         },
40446         
40447         flagEl : function()
40448         {
40449             var flag = this.el.select('div.flag',true).first();
40450             if(!flag){
40451                 return false;
40452             }
40453             return flag;
40454         },
40455         
40456         dialCodeHolderEl : function()
40457         {
40458             var d = this.el.select('input.dial-code-holder',true).first();
40459             if(!d){
40460                 return false;
40461             }
40462             return d;
40463         },
40464         
40465         setDialCode : function(v)
40466         {
40467             this.dialCodeHolder.dom.value = '+'+v;
40468         },
40469         
40470         setFlagClass : function(n)
40471         {
40472             this.flag.dom.className = 'flag '+n;
40473         },
40474         
40475         getValue : function()
40476         {
40477             var v = this.inputEl().getValue();
40478             if(this.dialCodeHolder) {
40479                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40480             }
40481             return v;
40482         },
40483         
40484         setValue : function(v)
40485         {
40486             var d = this.getDialCode(v);
40487             
40488             //invalid dial code
40489             if(v.length == 0 || !d || d.length == 0) {
40490                 if(this.rendered){
40491                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40492                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40493                 }
40494                 return;
40495             }
40496             
40497             //valid dial code
40498             this.setFlagClass(this.dialCodeMapping[d].iso2);
40499             this.setDialCode(d);
40500             this.inputEl().dom.value = v.replace('+'+d,'');
40501             this.hiddenEl().dom.value = this.getValue();
40502             
40503             this.validate();
40504         },
40505         
40506         getDialCode : function(v)
40507         {
40508             v = v ||  '';
40509             
40510             if (v.length == 0) {
40511                 return this.dialCodeHolder.dom.value;
40512             }
40513             
40514             var dialCode = "";
40515             if (v.charAt(0) != "+") {
40516                 return false;
40517             }
40518             var numericChars = "";
40519             for (var i = 1; i < v.length; i++) {
40520               var c = v.charAt(i);
40521               if (!isNaN(c)) {
40522                 numericChars += c;
40523                 if (this.dialCodeMapping[numericChars]) {
40524                   dialCode = v.substr(1, i);
40525                 }
40526                 if (numericChars.length == 4) {
40527                   break;
40528                 }
40529               }
40530             }
40531             return dialCode;
40532         },
40533         
40534         reset : function()
40535         {
40536             this.setValue(this.defaultDialCode);
40537             this.validate();
40538         },
40539         
40540         hiddenEl : function()
40541         {
40542             return this.el.select('input.hidden-tel-input',true).first();
40543         },
40544         
40545         // after setting val
40546         onKeyUp : function(e){
40547             this.setValue(this.getValue());
40548         },
40549         
40550         onKeyPress : function(e){
40551             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40552                 e.stopEvent();
40553             }
40554         }
40555         
40556 });
40557 /**
40558  * @class Roo.bootstrap.MoneyField
40559  * @extends Roo.bootstrap.ComboBox
40560  * Bootstrap MoneyField class
40561  * 
40562  * @constructor
40563  * Create a new MoneyField.
40564  * @param {Object} config Configuration options
40565  */
40566
40567 Roo.bootstrap.MoneyField = function(config) {
40568     
40569     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40570     
40571 };
40572
40573 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40574     
40575     /**
40576      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40577      */
40578     allowDecimals : true,
40579     /**
40580      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40581      */
40582     decimalSeparator : ".",
40583     /**
40584      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40585      */
40586     decimalPrecision : 0,
40587     /**
40588      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40589      */
40590     allowNegative : true,
40591     /**
40592      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40593      */
40594     allowZero: true,
40595     /**
40596      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40597      */
40598     minValue : Number.NEGATIVE_INFINITY,
40599     /**
40600      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40601      */
40602     maxValue : Number.MAX_VALUE,
40603     /**
40604      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40605      */
40606     minText : "The minimum value for this field is {0}",
40607     /**
40608      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40609      */
40610     maxText : "The maximum value for this field is {0}",
40611     /**
40612      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40613      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40614      */
40615     nanText : "{0} is not a valid number",
40616     /**
40617      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40618      */
40619     castInt : true,
40620     /**
40621      * @cfg {String} defaults currency of the MoneyField
40622      * value should be in lkey
40623      */
40624     defaultCurrency : false,
40625     /**
40626      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40627      */
40628     thousandsDelimiter : false,
40629     /**
40630      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40631      */
40632     max_length: false,
40633     
40634     inputlg : 9,
40635     inputmd : 9,
40636     inputsm : 9,
40637     inputxs : 6,
40638     
40639     store : false,
40640     
40641     getAutoCreate : function()
40642     {
40643         var align = this.labelAlign || this.parentLabelAlign();
40644         
40645         var id = Roo.id();
40646
40647         var cfg = {
40648             cls: 'form-group',
40649             cn: []
40650         };
40651
40652         var input =  {
40653             tag: 'input',
40654             id : id,
40655             cls : 'form-control roo-money-amount-input',
40656             autocomplete: 'new-password'
40657         };
40658         
40659         var hiddenInput = {
40660             tag: 'input',
40661             type: 'hidden',
40662             id: Roo.id(),
40663             cls: 'hidden-number-input'
40664         };
40665         
40666         if(this.max_length) {
40667             input.maxlength = this.max_length; 
40668         }
40669         
40670         if (this.name) {
40671             hiddenInput.name = this.name;
40672         }
40673
40674         if (this.disabled) {
40675             input.disabled = true;
40676         }
40677
40678         var clg = 12 - this.inputlg;
40679         var cmd = 12 - this.inputmd;
40680         var csm = 12 - this.inputsm;
40681         var cxs = 12 - this.inputxs;
40682         
40683         var container = {
40684             tag : 'div',
40685             cls : 'row roo-money-field',
40686             cn : [
40687                 {
40688                     tag : 'div',
40689                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40690                     cn : [
40691                         {
40692                             tag : 'div',
40693                             cls: 'roo-select2-container input-group',
40694                             cn: [
40695                                 {
40696                                     tag : 'input',
40697                                     cls : 'form-control roo-money-currency-input',
40698                                     autocomplete: 'new-password',
40699                                     readOnly : 1,
40700                                     name : this.currencyName
40701                                 },
40702                                 {
40703                                     tag :'span',
40704                                     cls : 'input-group-addon',
40705                                     cn : [
40706                                         {
40707                                             tag: 'span',
40708                                             cls: 'caret'
40709                                         }
40710                                     ]
40711                                 }
40712                             ]
40713                         }
40714                     ]
40715                 },
40716                 {
40717                     tag : 'div',
40718                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40719                     cn : [
40720                         {
40721                             tag: 'div',
40722                             cls: this.hasFeedback ? 'has-feedback' : '',
40723                             cn: [
40724                                 input
40725                             ]
40726                         }
40727                     ]
40728                 }
40729             ]
40730             
40731         };
40732         
40733         if (this.fieldLabel.length) {
40734             var indicator = {
40735                 tag: 'i',
40736                 tooltip: 'This field is required'
40737             };
40738
40739             var label = {
40740                 tag: 'label',
40741                 'for':  id,
40742                 cls: 'control-label',
40743                 cn: []
40744             };
40745
40746             var label_text = {
40747                 tag: 'span',
40748                 html: this.fieldLabel
40749             };
40750
40751             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40752             label.cn = [
40753                 indicator,
40754                 label_text
40755             ];
40756
40757             if(this.indicatorpos == 'right') {
40758                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40759                 label.cn = [
40760                     label_text,
40761                     indicator
40762                 ];
40763             }
40764
40765             if(align == 'left') {
40766                 container = {
40767                     tag: 'div',
40768                     cn: [
40769                         container
40770                     ]
40771                 };
40772
40773                 if(this.labelWidth > 12){
40774                     label.style = "width: " + this.labelWidth + 'px';
40775                 }
40776                 if(this.labelWidth < 13 && this.labelmd == 0){
40777                     this.labelmd = this.labelWidth;
40778                 }
40779                 if(this.labellg > 0){
40780                     label.cls += ' col-lg-' + this.labellg;
40781                     input.cls += ' col-lg-' + (12 - this.labellg);
40782                 }
40783                 if(this.labelmd > 0){
40784                     label.cls += ' col-md-' + this.labelmd;
40785                     container.cls += ' col-md-' + (12 - this.labelmd);
40786                 }
40787                 if(this.labelsm > 0){
40788                     label.cls += ' col-sm-' + this.labelsm;
40789                     container.cls += ' col-sm-' + (12 - this.labelsm);
40790                 }
40791                 if(this.labelxs > 0){
40792                     label.cls += ' col-xs-' + this.labelxs;
40793                     container.cls += ' col-xs-' + (12 - this.labelxs);
40794                 }
40795             }
40796         }
40797
40798         cfg.cn = [
40799             label,
40800             container,
40801             hiddenInput
40802         ];
40803         
40804         var settings = this;
40805
40806         ['xs','sm','md','lg'].map(function(size){
40807             if (settings[size]) {
40808                 cfg.cls += ' col-' + size + '-' + settings[size];
40809             }
40810         });
40811         
40812         return cfg;
40813     },
40814     
40815     initEvents : function()
40816     {
40817         this.indicator = this.indicatorEl();
40818         
40819         this.initCurrencyEvent();
40820         
40821         this.initNumberEvent();
40822     },
40823     
40824     initCurrencyEvent : function()
40825     {
40826         if (!this.store) {
40827             throw "can not find store for combo";
40828         }
40829         
40830         this.store = Roo.factory(this.store, Roo.data);
40831         this.store.parent = this;
40832         
40833         this.createList();
40834         
40835         this.triggerEl = this.el.select('.input-group-addon', true).first();
40836         
40837         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40838         
40839         var _this = this;
40840         
40841         (function(){
40842             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40843             _this.list.setWidth(lw);
40844         }).defer(100);
40845         
40846         this.list.on('mouseover', this.onViewOver, this);
40847         this.list.on('mousemove', this.onViewMove, this);
40848         this.list.on('scroll', this.onViewScroll, this);
40849         
40850         if(!this.tpl){
40851             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40852         }
40853         
40854         this.view = new Roo.View(this.list, this.tpl, {
40855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40856         });
40857         
40858         this.view.on('click', this.onViewClick, this);
40859         
40860         this.store.on('beforeload', this.onBeforeLoad, this);
40861         this.store.on('load', this.onLoad, this);
40862         this.store.on('loadexception', this.onLoadException, this);
40863         
40864         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40865             "up" : function(e){
40866                 this.inKeyMode = true;
40867                 this.selectPrev();
40868             },
40869
40870             "down" : function(e){
40871                 if(!this.isExpanded()){
40872                     this.onTriggerClick();
40873                 }else{
40874                     this.inKeyMode = true;
40875                     this.selectNext();
40876                 }
40877             },
40878
40879             "enter" : function(e){
40880                 this.collapse();
40881                 
40882                 if(this.fireEvent("specialkey", this, e)){
40883                     this.onViewClick(false);
40884                 }
40885                 
40886                 return true;
40887             },
40888
40889             "esc" : function(e){
40890                 this.collapse();
40891             },
40892
40893             "tab" : function(e){
40894                 this.collapse();
40895                 
40896                 if(this.fireEvent("specialkey", this, e)){
40897                     this.onViewClick(false);
40898                 }
40899                 
40900                 return true;
40901             },
40902
40903             scope : this,
40904
40905             doRelay : function(foo, bar, hname){
40906                 if(hname == 'down' || this.scope.isExpanded()){
40907                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40908                 }
40909                 return true;
40910             },
40911
40912             forceKeyDown: true
40913         });
40914         
40915         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40916         
40917     },
40918     
40919     initNumberEvent : function(e)
40920     {
40921         this.inputEl().on("keydown" , this.fireKey,  this);
40922         this.inputEl().on("focus", this.onFocus,  this);
40923         this.inputEl().on("blur", this.onBlur,  this);
40924         
40925         this.inputEl().relayEvent('keyup', this);
40926         
40927         if(this.indicator){
40928             this.indicator.addClass('invisible');
40929         }
40930  
40931         this.originalValue = this.getValue();
40932         
40933         if(this.validationEvent == 'keyup'){
40934             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40935             this.inputEl().on('keyup', this.filterValidation, this);
40936         }
40937         else if(this.validationEvent !== false){
40938             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40939         }
40940         
40941         if(this.selectOnFocus){
40942             this.on("focus", this.preFocus, this);
40943             
40944         }
40945         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40946             this.inputEl().on("keypress", this.filterKeys, this);
40947         } else {
40948             this.inputEl().relayEvent('keypress', this);
40949         }
40950         
40951         var allowed = "0123456789";
40952         
40953         if(this.allowDecimals){
40954             allowed += this.decimalSeparator;
40955         }
40956         
40957         if(this.allowNegative){
40958             allowed += "-";
40959         }
40960         
40961         if(this.thousandsDelimiter) {
40962             allowed += ",";
40963         }
40964         
40965         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40966         
40967         var keyPress = function(e){
40968             
40969             var k = e.getKey();
40970             
40971             var c = e.getCharCode();
40972             
40973             if(
40974                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40975                     allowed.indexOf(String.fromCharCode(c)) === -1
40976             ){
40977                 e.stopEvent();
40978                 return;
40979             }
40980             
40981             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40982                 return;
40983             }
40984             
40985             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40986                 e.stopEvent();
40987             }
40988         };
40989         
40990         this.inputEl().on("keypress", keyPress, this);
40991         
40992     },
40993     
40994     onTriggerClick : function(e)
40995     {   
40996         if(this.disabled){
40997             return;
40998         }
40999         
41000         this.page = 0;
41001         this.loadNext = false;
41002         
41003         if(this.isExpanded()){
41004             this.collapse();
41005             return;
41006         }
41007         
41008         this.hasFocus = true;
41009         
41010         if(this.triggerAction == 'all') {
41011             this.doQuery(this.allQuery, true);
41012             return;
41013         }
41014         
41015         this.doQuery(this.getRawValue());
41016     },
41017     
41018     getCurrency : function()
41019     {   
41020         var v = this.currencyEl().getValue();
41021         
41022         return v;
41023     },
41024     
41025     restrictHeight : function()
41026     {
41027         this.list.alignTo(this.currencyEl(), this.listAlign);
41028         this.list.alignTo(this.currencyEl(), this.listAlign);
41029     },
41030     
41031     onViewClick : function(view, doFocus, el, e)
41032     {
41033         var index = this.view.getSelectedIndexes()[0];
41034         
41035         var r = this.store.getAt(index);
41036         
41037         if(r){
41038             this.onSelect(r, index);
41039         }
41040     },
41041     
41042     onSelect : function(record, index){
41043         
41044         if(this.fireEvent('beforeselect', this, record, index) !== false){
41045         
41046             this.setFromCurrencyData(index > -1 ? record.data : false);
41047             
41048             this.collapse();
41049             
41050             this.fireEvent('select', this, record, index);
41051         }
41052     },
41053     
41054     setFromCurrencyData : function(o)
41055     {
41056         var currency = '';
41057         
41058         this.lastCurrency = o;
41059         
41060         if (this.currencyField) {
41061             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41062         } else {
41063             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41064         }
41065         
41066         this.lastSelectionText = currency;
41067         
41068         //setting default currency
41069         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41070             this.setCurrency(this.defaultCurrency);
41071             return;
41072         }
41073         
41074         this.setCurrency(currency);
41075     },
41076     
41077     setFromData : function(o)
41078     {
41079         var c = {};
41080         
41081         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41082         
41083         this.setFromCurrencyData(c);
41084         
41085         var value = '';
41086         
41087         if (this.name) {
41088             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41089         } else {
41090             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41091         }
41092         
41093         this.setValue(value);
41094         
41095     },
41096     
41097     setCurrency : function(v)
41098     {   
41099         this.currencyValue = v;
41100         
41101         if(this.rendered){
41102             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41103             this.validate();
41104         }
41105     },
41106     
41107     setValue : function(v)
41108     {
41109         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41110         
41111         this.value = v;
41112         
41113         if(this.rendered){
41114             
41115             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41116             
41117             this.inputEl().dom.value = (v == '') ? '' :
41118                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41119             
41120             if(!this.allowZero && v === '0') {
41121                 this.hiddenEl().dom.value = '';
41122                 this.inputEl().dom.value = '';
41123             }
41124             
41125             this.validate();
41126         }
41127     },
41128     
41129     getRawValue : function()
41130     {
41131         var v = this.inputEl().getValue();
41132         
41133         return v;
41134     },
41135     
41136     getValue : function()
41137     {
41138         return this.fixPrecision(this.parseValue(this.getRawValue()));
41139     },
41140     
41141     parseValue : function(value)
41142     {
41143         if(this.thousandsDelimiter) {
41144             value += "";
41145             r = new RegExp(",", "g");
41146             value = value.replace(r, "");
41147         }
41148         
41149         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41150         return isNaN(value) ? '' : value;
41151         
41152     },
41153     
41154     fixPrecision : function(value)
41155     {
41156         if(this.thousandsDelimiter) {
41157             value += "";
41158             r = new RegExp(",", "g");
41159             value = value.replace(r, "");
41160         }
41161         
41162         var nan = isNaN(value);
41163         
41164         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41165             return nan ? '' : value;
41166         }
41167         return parseFloat(value).toFixed(this.decimalPrecision);
41168     },
41169     
41170     decimalPrecisionFcn : function(v)
41171     {
41172         return Math.floor(v);
41173     },
41174     
41175     validateValue : function(value)
41176     {
41177         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41178             return false;
41179         }
41180         
41181         var num = this.parseValue(value);
41182         
41183         if(isNaN(num)){
41184             this.markInvalid(String.format(this.nanText, value));
41185             return false;
41186         }
41187         
41188         if(num < this.minValue){
41189             this.markInvalid(String.format(this.minText, this.minValue));
41190             return false;
41191         }
41192         
41193         if(num > this.maxValue){
41194             this.markInvalid(String.format(this.maxText, this.maxValue));
41195             return false;
41196         }
41197         
41198         return true;
41199     },
41200     
41201     validate : function()
41202     {
41203         if(this.disabled || this.allowBlank){
41204             this.markValid();
41205             return true;
41206         }
41207         
41208         var currency = this.getCurrency();
41209         
41210         if(this.validateValue(this.getRawValue()) && currency.length){
41211             this.markValid();
41212             return true;
41213         }
41214         
41215         this.markInvalid();
41216         return false;
41217     },
41218     
41219     getName: function()
41220     {
41221         return this.name;
41222     },
41223     
41224     beforeBlur : function()
41225     {
41226         if(!this.castInt){
41227             return;
41228         }
41229         
41230         var v = this.parseValue(this.getRawValue());
41231         
41232         if(v || v == 0){
41233             this.setValue(v);
41234         }
41235     },
41236     
41237     onBlur : function()
41238     {
41239         this.beforeBlur();
41240         
41241         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41242             //this.el.removeClass(this.focusClass);
41243         }
41244         
41245         this.hasFocus = false;
41246         
41247         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41248             this.validate();
41249         }
41250         
41251         var v = this.getValue();
41252         
41253         if(String(v) !== String(this.startValue)){
41254             this.fireEvent('change', this, v, this.startValue);
41255         }
41256         
41257         this.fireEvent("blur", this);
41258     },
41259     
41260     inputEl : function()
41261     {
41262         return this.el.select('.roo-money-amount-input', true).first();
41263     },
41264     
41265     currencyEl : function()
41266     {
41267         return this.el.select('.roo-money-currency-input', true).first();
41268     },
41269     
41270     hiddenEl : function()
41271     {
41272         return this.el.select('input.hidden-number-input',true).first();
41273     }
41274     
41275 });