roojs-bootstrap.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','d-none']);
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','d-none']);
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);
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.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         };
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911
2912     resize : function()
2913     {
2914         this.maskEl.setSize(
2915             Roo.lib.Dom.getViewWidth(true),
2916             Roo.lib.Dom.getViewHeight(true)
2917         );
2918         
2919         if (this.fitwindow) {
2920             this.setSize(
2921                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2922                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2923             );
2924             return;
2925         }
2926         
2927         if(this.max_width !== 0) {
2928             
2929             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2930             
2931             if(this.height) {
2932                 this.setSize(w, this.height);
2933                 return;
2934             }
2935             
2936             if(this.max_height) {
2937                 this.setSize(w,Math.min(
2938                     this.max_height,
2939                     Roo.lib.Dom.getViewportHeight(true) - 60
2940                 ));
2941                 
2942                 return;
2943             }
2944             
2945             if(!this.fit_content) {
2946                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2947                 return;
2948             }
2949             
2950             this.setSize(w, Math.min(
2951                 60 +
2952                 this.headerEl.getHeight() + 
2953                 this.footerEl.getHeight() + 
2954                 this.getChildHeight(this.bodyEl.dom.childNodes),
2955                 Roo.lib.Dom.getViewportHeight(true) - 60)
2956             );
2957         }
2958         
2959     },
2960
2961     setSize : function(w,h)
2962     {
2963         if (!w && !h) {
2964             return;
2965         }
2966         
2967         this.resizeTo(w,h);
2968     },
2969
2970     show : function() {
2971
2972         if (!this.rendered) {
2973             this.render();
2974         }
2975
2976         //this.el.setStyle('display', 'block');
2977         this.el.removeClass('hideing');
2978         this.el.dom.style.display='block';
2979         
2980         Roo.get(document.body).addClass('modal-open');
2981  
2982         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2983             var _this = this;
2984             (function(){
2985                 this.el.addClass('show');
2986                 this.el.addClass('in');
2987             }).defer(50, this);
2988         }else{
2989             this.el.addClass('show');
2990             this.el.addClass('in');
2991         }
2992
2993         // not sure how we can show data in here..
2994         //if (this.tmpl) {
2995         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2996         //}
2997
2998         Roo.get(document.body).addClass("x-body-masked");
2999         
3000         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3001         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3002         this.maskEl.dom.style.display = 'block';
3003         this.maskEl.addClass('show');
3004         
3005         
3006         this.resize();
3007         
3008         this.fireEvent('show', this);
3009
3010         // set zindex here - otherwise it appears to be ignored...
3011         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3012
3013         (function () {
3014             this.items.forEach( function(e) {
3015                 e.layout ? e.layout() : false;
3016
3017             });
3018         }).defer(100,this);
3019
3020     },
3021     hide : function()
3022     {
3023         if(this.fireEvent("beforehide", this) !== false){
3024             
3025             this.maskEl.removeClass('show');
3026             
3027             this.maskEl.dom.style.display = '';
3028             Roo.get(document.body).removeClass("x-body-masked");
3029             this.el.removeClass('in');
3030             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3031
3032             if(this.animate){ // why
3033                 this.el.addClass('hideing');
3034                 this.el.removeClass('show');
3035                 (function(){
3036                     if (!this.el.hasClass('hideing')) {
3037                         return; // it's been shown again...
3038                     }
3039                     
3040                     this.el.dom.style.display='';
3041
3042                     Roo.get(document.body).removeClass('modal-open');
3043                     this.el.removeClass('hideing');
3044                 }).defer(150,this);
3045                 
3046             }else{
3047                 this.el.removeClass('show');
3048                 this.el.dom.style.display='';
3049                 Roo.get(document.body).removeClass('modal-open');
3050
3051             }
3052             this.fireEvent('hide', this);
3053         }
3054     },
3055     isVisible : function()
3056     {
3057         
3058         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3059         
3060     },
3061
3062     addButton : function(str, cb)
3063     {
3064
3065
3066         var b = Roo.apply({}, { html : str } );
3067         b.xns = b.xns || Roo.bootstrap;
3068         b.xtype = b.xtype || 'Button';
3069         if (typeof(b.listeners) == 'undefined') {
3070             b.listeners = { click : cb.createDelegate(this)  };
3071         }
3072
3073         var btn = Roo.factory(b);
3074
3075         btn.render(this.getButtonContainer());
3076
3077         return btn;
3078
3079     },
3080
3081     setDefaultButton : function(btn)
3082     {
3083         //this.el.select('.modal-footer').()
3084     },
3085     diff : false,
3086
3087     resizeTo: function(w,h)
3088     {
3089         // skip.. ?? why??
3090
3091         this.dialogEl.setWidth(w);
3092         if (this.diff === false) {
3093             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3094         }
3095
3096         this.bodyEl.setHeight(h - this.diff);
3097
3098         this.fireEvent('resize', this);
3099
3100     },
3101     setContentSize  : function(w, h)
3102     {
3103
3104     },
3105     onButtonClick: function(btn,e)
3106     {
3107         //Roo.log([a,b,c]);
3108         this.fireEvent('btnclick', btn.name, e);
3109     },
3110      /**
3111      * Set the title of the Dialog
3112      * @param {String} str new Title
3113      */
3114     setTitle: function(str) {
3115         this.titleEl.dom.innerHTML = str;
3116     },
3117     /**
3118      * Set the body of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setBody: function(str) {
3122         this.bodyEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog using the template
3126      * @param {Obj} data - apply this data to the template and replace the body contents.
3127      */
3128     applyBody: function(obj)
3129     {
3130         if (!this.tmpl) {
3131             Roo.log("Error - using apply Body without a template");
3132             //code
3133         }
3134         this.tmpl.overwrite(this.bodyEl, obj);
3135     },
3136     
3137     getChildHeight : function(child_nodes)
3138     {
3139         if(
3140             !child_nodes ||
3141             child_nodes.length == 0
3142         ) {
3143             return;
3144         }
3145         
3146         var child_height = 0;
3147         
3148         for(var i = 0; i < child_nodes.length; i++) {
3149             
3150             /*
3151             * for modal with tabs...
3152             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3153                 
3154                 var layout_childs = child_nodes[i].childNodes;
3155                 
3156                 for(var j = 0; j < layout_childs.length; j++) {
3157                     
3158                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3159                         
3160                         var layout_body_childs = layout_childs[j].childNodes;
3161                         
3162                         for(var k = 0; k < layout_body_childs.length; k++) {
3163                             
3164                             if(layout_body_childs[k].classList.contains('navbar')) {
3165                                 child_height += layout_body_childs[k].offsetHeight;
3166                                 continue;
3167                             }
3168                             
3169                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3170                                 
3171                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3172                                 
3173                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3174                                     
3175                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3176                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3177                                         continue;
3178                                     }
3179                                     
3180                                 }
3181                                 
3182                             }
3183                             
3184                         }
3185                     }
3186                 }
3187                 continue;
3188             }
3189             */
3190             
3191             child_height += child_nodes[i].offsetHeight;
3192             // Roo.log(child_nodes[i].offsetHeight);
3193         }
3194         
3195         return child_height;
3196     }
3197
3198 });
3199
3200
3201 Roo.apply(Roo.bootstrap.Modal,  {
3202     /**
3203          * Button config that displays a single OK button
3204          * @type Object
3205          */
3206         OK :  [{
3207             name : 'ok',
3208             weight : 'primary',
3209             html : 'OK'
3210         }],
3211         /**
3212          * Button config that displays Yes and No buttons
3213          * @type Object
3214          */
3215         YESNO : [
3216             {
3217                 name  : 'no',
3218                 html : 'No'
3219             },
3220             {
3221                 name  :'yes',
3222                 weight : 'primary',
3223                 html : 'Yes'
3224             }
3225         ],
3226
3227         /**
3228          * Button config that displays OK and Cancel buttons
3229          * @type Object
3230          */
3231         OKCANCEL : [
3232             {
3233                name : 'cancel',
3234                 html : 'Cancel'
3235             },
3236             {
3237                 name : 'ok',
3238                 weight : 'primary',
3239                 html : 'OK'
3240             }
3241         ],
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : [
3247             {
3248                 name : 'yes',
3249                 weight : 'primary',
3250                 html : 'Yes'
3251             },
3252             {
3253                 name : 'no',
3254                 html : 'No'
3255             },
3256             {
3257                 name : 'cancel',
3258                 html : 'Cancel'
3259             }
3260         ],
3261         
3262         zIndex : 10001
3263 });
3264 /*
3265  * - LGPL
3266  *
3267  * messagebox - can be used as a replace
3268  * 
3269  */
3270 /**
3271  * @class Roo.MessageBox
3272  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3273  * Example usage:
3274  *<pre><code>
3275 // Basic alert:
3276 Roo.Msg.alert('Status', 'Changes saved successfully.');
3277
3278 // Prompt for user data:
3279 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3280     if (btn == 'ok'){
3281         // process text value...
3282     }
3283 });
3284
3285 // Show a dialog using config options:
3286 Roo.Msg.show({
3287    title:'Save Changes?',
3288    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3289    buttons: Roo.Msg.YESNOCANCEL,
3290    fn: processResult,
3291    animEl: 'elId'
3292 });
3293 </code></pre>
3294  * @singleton
3295  */
3296 Roo.bootstrap.MessageBox = function(){
3297     var dlg, opt, mask, waitTimer;
3298     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3299     var buttons, activeTextEl, bwidth;
3300
3301     
3302     // private
3303     var handleButton = function(button){
3304         dlg.hide();
3305         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3306     };
3307
3308     // private
3309     var handleHide = function(){
3310         if(opt && opt.cls){
3311             dlg.el.removeClass(opt.cls);
3312         }
3313         //if(waitTimer){
3314         //    Roo.TaskMgr.stop(waitTimer);
3315         //    waitTimer = null;
3316         //}
3317     };
3318
3319     // private
3320     var updateButtons = function(b){
3321         var width = 0;
3322         if(!b){
3323             buttons["ok"].hide();
3324             buttons["cancel"].hide();
3325             buttons["yes"].hide();
3326             buttons["no"].hide();
3327             dlg.footerEl.hide();
3328             
3329             return width;
3330         }
3331         dlg.footerEl.show();
3332         for(var k in buttons){
3333             if(typeof buttons[k] != "function"){
3334                 if(b[k]){
3335                     buttons[k].show();
3336                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3337                     width += buttons[k].el.getWidth()+15;
3338                 }else{
3339                     buttons[k].hide();
3340                 }
3341             }
3342         }
3343         return width;
3344     };
3345
3346     // private
3347     var handleEsc = function(d, k, e){
3348         if(opt && opt.closable !== false){
3349             dlg.hide();
3350         }
3351         if(e){
3352             e.stopEvent();
3353         }
3354     };
3355
3356     return {
3357         /**
3358          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3359          * @return {Roo.BasicDialog} The BasicDialog element
3360          */
3361         getDialog : function(){
3362            if(!dlg){
3363                 dlg = new Roo.bootstrap.Modal( {
3364                     //draggable: true,
3365                     //resizable:false,
3366                     //constraintoviewport:false,
3367                     //fixedcenter:true,
3368                     //collapsible : false,
3369                     //shim:true,
3370                     //modal: true,
3371                 //    width: 'auto',
3372                   //  height:100,
3373                     //buttonAlign:"center",
3374                     closeClick : function(){
3375                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3376                             handleButton("no");
3377                         }else{
3378                             handleButton("cancel");
3379                         }
3380                     }
3381                 });
3382                 dlg.render();
3383                 dlg.on("hide", handleHide);
3384                 mask = dlg.mask;
3385                 //dlg.addKeyListener(27, handleEsc);
3386                 buttons = {};
3387                 this.buttons = buttons;
3388                 var bt = this.buttonText;
3389                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3390                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3391                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3392                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3393                 //Roo.log(buttons);
3394                 bodyEl = dlg.bodyEl.createChild({
3395
3396                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3397                         '<textarea class="roo-mb-textarea"></textarea>' +
3398                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3399                 });
3400                 msgEl = bodyEl.dom.firstChild;
3401                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3402                 textboxEl.enableDisplayMode();
3403                 textboxEl.addKeyListener([10,13], function(){
3404                     if(dlg.isVisible() && opt && opt.buttons){
3405                         if(opt.buttons.ok){
3406                             handleButton("ok");
3407                         }else if(opt.buttons.yes){
3408                             handleButton("yes");
3409                         }
3410                     }
3411                 });
3412                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3413                 textareaEl.enableDisplayMode();
3414                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3415                 progressEl.enableDisplayMode();
3416                 
3417                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3418                 var pf = progressEl.dom.firstChild;
3419                 if (pf) {
3420                     pp = Roo.get(pf.firstChild);
3421                     pp.setHeight(pf.offsetHeight);
3422                 }
3423                 
3424             }
3425             return dlg;
3426         },
3427
3428         /**
3429          * Updates the message box body text
3430          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3431          * the XHTML-compliant non-breaking space character '&amp;#160;')
3432          * @return {Roo.MessageBox} This message box
3433          */
3434         updateText : function(text)
3435         {
3436             if(!dlg.isVisible() && !opt.width){
3437                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3438                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3439             }
3440             msgEl.innerHTML = text || '&#160;';
3441       
3442             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3443             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3444             var w = Math.max(
3445                     Math.min(opt.width || cw , this.maxWidth), 
3446                     Math.max(opt.minWidth || this.minWidth, bwidth)
3447             );
3448             if(opt.prompt){
3449                 activeTextEl.setWidth(w);
3450             }
3451             if(dlg.isVisible()){
3452                 dlg.fixedcenter = false;
3453             }
3454             // to big, make it scroll. = But as usual stupid IE does not support
3455             // !important..
3456             
3457             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3458                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3459                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3460             } else {
3461                 bodyEl.dom.style.height = '';
3462                 bodyEl.dom.style.overflowY = '';
3463             }
3464             if (cw > w) {
3465                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3466             } else {
3467                 bodyEl.dom.style.overflowX = '';
3468             }
3469             
3470             dlg.setContentSize(w, bodyEl.getHeight());
3471             if(dlg.isVisible()){
3472                 dlg.fixedcenter = true;
3473             }
3474             return this;
3475         },
3476
3477         /**
3478          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3479          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3480          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3481          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3482          * @return {Roo.MessageBox} This message box
3483          */
3484         updateProgress : function(value, text){
3485             if(text){
3486                 this.updateText(text);
3487             }
3488             
3489             if (pp) { // weird bug on my firefox - for some reason this is not defined
3490                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3491                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3492             }
3493             return this;
3494         },        
3495
3496         /**
3497          * Returns true if the message box is currently displayed
3498          * @return {Boolean} True if the message box is visible, else false
3499          */
3500         isVisible : function(){
3501             return dlg && dlg.isVisible();  
3502         },
3503
3504         /**
3505          * Hides the message box if it is displayed
3506          */
3507         hide : function(){
3508             if(this.isVisible()){
3509                 dlg.hide();
3510             }  
3511         },
3512
3513         /**
3514          * Displays a new message box, or reinitializes an existing message box, based on the config options
3515          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3516          * The following config object properties are supported:
3517          * <pre>
3518 Property    Type             Description
3519 ----------  ---------------  ------------------------------------------------------------------------------------
3520 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3521                                    closes (defaults to undefined)
3522 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3523                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3524 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3525                                    progress and wait dialogs will ignore this property and always hide the
3526                                    close button as they can only be closed programmatically.
3527 cls               String           A custom CSS class to apply to the message box element
3528 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3529                                    displayed (defaults to 75)
3530 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3531                                    function will be btn (the name of the button that was clicked, if applicable,
3532                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3533                                    Progress and wait dialogs will ignore this option since they do not respond to
3534                                    user actions and can only be closed programmatically, so any required function
3535                                    should be called by the same code after it closes the dialog.
3536 icon              String           A CSS class that provides a background image to be used as an icon for
3537                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3538 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3539 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3540 modal             Boolean          False to allow user interaction with the page while the message box is
3541                                    displayed (defaults to true)
3542 msg               String           A string that will replace the existing message box body text (defaults
3543                                    to the XHTML-compliant non-breaking space character '&#160;')
3544 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3545 progress          Boolean          True to display a progress bar (defaults to false)
3546 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3547 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3548 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3549 title             String           The title text
3550 value             String           The string value to set into the active textbox element if displayed
3551 wait              Boolean          True to display a progress bar (defaults to false)
3552 width             Number           The width of the dialog in pixels
3553 </pre>
3554          *
3555          * Example usage:
3556          * <pre><code>
3557 Roo.Msg.show({
3558    title: 'Address',
3559    msg: 'Please enter your address:',
3560    width: 300,
3561    buttons: Roo.MessageBox.OKCANCEL,
3562    multiline: true,
3563    fn: saveAddress,
3564    animEl: 'addAddressBtn'
3565 });
3566 </code></pre>
3567          * @param {Object} config Configuration options
3568          * @return {Roo.MessageBox} This message box
3569          */
3570         show : function(options)
3571         {
3572             
3573             // this causes nightmares if you show one dialog after another
3574             // especially on callbacks..
3575              
3576             if(this.isVisible()){
3577                 
3578                 this.hide();
3579                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3580                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3581                 Roo.log("New Dialog Message:" +  options.msg )
3582                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3583                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3584                 
3585             }
3586             var d = this.getDialog();
3587             opt = options;
3588             d.setTitle(opt.title || "&#160;");
3589             d.closeEl.setDisplayed(opt.closable !== false);
3590             activeTextEl = textboxEl;
3591             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3592             if(opt.prompt){
3593                 if(opt.multiline){
3594                     textboxEl.hide();
3595                     textareaEl.show();
3596                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3597                         opt.multiline : this.defaultTextHeight);
3598                     activeTextEl = textareaEl;
3599                 }else{
3600                     textboxEl.show();
3601                     textareaEl.hide();
3602                 }
3603             }else{
3604                 textboxEl.hide();
3605                 textareaEl.hide();
3606             }
3607             progressEl.setDisplayed(opt.progress === true);
3608             if (opt.progress) {
3609                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3610             }
3611             this.updateProgress(0);
3612             activeTextEl.dom.value = opt.value || "";
3613             if(opt.prompt){
3614                 dlg.setDefaultButton(activeTextEl);
3615             }else{
3616                 var bs = opt.buttons;
3617                 var db = null;
3618                 if(bs && bs.ok){
3619                     db = buttons["ok"];
3620                 }else if(bs && bs.yes){
3621                     db = buttons["yes"];
3622                 }
3623                 dlg.setDefaultButton(db);
3624             }
3625             bwidth = updateButtons(opt.buttons);
3626             this.updateText(opt.msg);
3627             if(opt.cls){
3628                 d.el.addClass(opt.cls);
3629             }
3630             d.proxyDrag = opt.proxyDrag === true;
3631             d.modal = opt.modal !== false;
3632             d.mask = opt.modal !== false ? mask : false;
3633             if(!d.isVisible()){
3634                 // force it to the end of the z-index stack so it gets a cursor in FF
3635                 document.body.appendChild(dlg.el.dom);
3636                 d.animateTarget = null;
3637                 d.show(options.animEl);
3638             }
3639             return this;
3640         },
3641
3642         /**
3643          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3644          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3645          * and closing the message box when the process is complete.
3646          * @param {String} title The title bar text
3647          * @param {String} msg The message box body text
3648          * @return {Roo.MessageBox} This message box
3649          */
3650         progress : function(title, msg){
3651             this.show({
3652                 title : title,
3653                 msg : msg,
3654                 buttons: false,
3655                 progress:true,
3656                 closable:false,
3657                 minWidth: this.minProgressWidth,
3658                 modal : true
3659             });
3660             return this;
3661         },
3662
3663         /**
3664          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3665          * If a callback function is passed it will be called after the user clicks the button, and the
3666          * id of the button that was clicked will be passed as the only parameter to the callback
3667          * (could also be the top-right close button).
3668          * @param {String} title The title bar text
3669          * @param {String} msg The message box body text
3670          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3671          * @param {Object} scope (optional) The scope of the callback function
3672          * @return {Roo.MessageBox} This message box
3673          */
3674         alert : function(title, msg, fn, scope)
3675         {
3676             this.show({
3677                 title : title,
3678                 msg : msg,
3679                 buttons: this.OK,
3680                 fn: fn,
3681                 closable : false,
3682                 scope : scope,
3683                 modal : true
3684             });
3685             return this;
3686         },
3687
3688         /**
3689          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3690          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3691          * You are responsible for closing the message box when the process is complete.
3692          * @param {String} msg The message box body text
3693          * @param {String} title (optional) The title bar text
3694          * @return {Roo.MessageBox} This message box
3695          */
3696         wait : function(msg, title){
3697             this.show({
3698                 title : title,
3699                 msg : msg,
3700                 buttons: false,
3701                 closable:false,
3702                 progress:true,
3703                 modal:true,
3704                 width:300,
3705                 wait:true
3706             });
3707             waitTimer = Roo.TaskMgr.start({
3708                 run: function(i){
3709                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3710                 },
3711                 interval: 1000
3712             });
3713             return this;
3714         },
3715
3716         /**
3717          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3718          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3719          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3720          * @param {String} title The title bar text
3721          * @param {String} msg The message box body text
3722          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3723          * @param {Object} scope (optional) The scope of the callback function
3724          * @return {Roo.MessageBox} This message box
3725          */
3726         confirm : function(title, msg, fn, scope){
3727             this.show({
3728                 title : title,
3729                 msg : msg,
3730                 buttons: this.YESNO,
3731                 fn: fn,
3732                 scope : scope,
3733                 modal : true
3734             });
3735             return this;
3736         },
3737
3738         /**
3739          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3740          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3741          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3742          * (could also be the top-right close button) and the text that was entered will be passed as the two
3743          * parameters to the callback.
3744          * @param {String} title The title bar text
3745          * @param {String} msg The message box body text
3746          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3747          * @param {Object} scope (optional) The scope of the callback function
3748          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3749          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3750          * @return {Roo.MessageBox} This message box
3751          */
3752         prompt : function(title, msg, fn, scope, multiline){
3753             this.show({
3754                 title : title,
3755                 msg : msg,
3756                 buttons: this.OKCANCEL,
3757                 fn: fn,
3758                 minWidth:250,
3759                 scope : scope,
3760                 prompt:true,
3761                 multiline: multiline,
3762                 modal : true
3763             });
3764             return this;
3765         },
3766
3767         /**
3768          * Button config that displays a single OK button
3769          * @type Object
3770          */
3771         OK : {ok:true},
3772         /**
3773          * Button config that displays Yes and No buttons
3774          * @type Object
3775          */
3776         YESNO : {yes:true, no:true},
3777         /**
3778          * Button config that displays OK and Cancel buttons
3779          * @type Object
3780          */
3781         OKCANCEL : {ok:true, cancel:true},
3782         /**
3783          * Button config that displays Yes, No and Cancel buttons
3784          * @type Object
3785          */
3786         YESNOCANCEL : {yes:true, no:true, cancel:true},
3787
3788         /**
3789          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3790          * @type Number
3791          */
3792         defaultTextHeight : 75,
3793         /**
3794          * The maximum width in pixels of the message box (defaults to 600)
3795          * @type Number
3796          */
3797         maxWidth : 600,
3798         /**
3799          * The minimum width in pixels of the message box (defaults to 100)
3800          * @type Number
3801          */
3802         minWidth : 100,
3803         /**
3804          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3805          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3806          * @type Number
3807          */
3808         minProgressWidth : 250,
3809         /**
3810          * An object containing the default button text strings that can be overriden for localized language support.
3811          * Supported properties are: ok, cancel, yes and no.
3812          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3813          * @type Object
3814          */
3815         buttonText : {
3816             ok : "OK",
3817             cancel : "Cancel",
3818             yes : "Yes",
3819             no : "No"
3820         }
3821     };
3822 }();
3823
3824 /**
3825  * Shorthand for {@link Roo.MessageBox}
3826  */
3827 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3828 Roo.Msg = Roo.Msg || Roo.MessageBox;
3829 /*
3830  * - LGPL
3831  *
3832  * navbar
3833  * 
3834  */
3835
3836 /**
3837  * @class Roo.bootstrap.Navbar
3838  * @extends Roo.bootstrap.Component
3839  * Bootstrap Navbar class
3840
3841  * @constructor
3842  * Create a new Navbar
3843  * @param {Object} config The config object
3844  */
3845
3846
3847 Roo.bootstrap.Navbar = function(config){
3848     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3849     this.addEvents({
3850         // raw events
3851         /**
3852          * @event beforetoggle
3853          * Fire before toggle the menu
3854          * @param {Roo.EventObject} e
3855          */
3856         "beforetoggle" : true
3857     });
3858 };
3859
3860 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3861     
3862     
3863    
3864     // private
3865     navItems : false,
3866     loadMask : false,
3867     
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3873         
3874     },
3875     
3876     initEvents :function ()
3877     {
3878         //Roo.log(this.el.select('.navbar-toggle',true));
3879         this.el.select('.navbar-toggle',true).on('click', function() {
3880             if(this.fireEvent('beforetoggle', this) !== false){
3881                 var ce = this.el.select('.navbar-collapse',true).first();
3882                 ce.toggleClass('in'); // old...
3883                 if (ce.hasClass('collapse')) {
3884                     // show it...
3885                     ce.removeClass('collapse');
3886                     ce.addClass('show');
3887                     var h = ce.getHeight();
3888                     Roo.log(h);
3889                     ce.removeClass('show');
3890                     // at this point we should be able to see it..
3891                     ce.addClass('collapsing');
3892                     
3893                     ce.setHeight(0); // resize it ...
3894                     ce.on('transitionend', function() {
3895                         Roo.log('done transition');
3896                         ce.removeClass('collapsing');
3897                         ce.addClass('show');
3898                         ce.removeClass('collapse');
3899
3900                         ce.dom.style.height = '';
3901                     }, this, { single: true} );
3902                     ce.setHeight(h);
3903                     
3904                 } else {
3905                     ce.setHeight(ce.getHeight());
3906                     ce.removeClass('show');
3907                     ce.addClass('collapsing');
3908                     
3909                     ce.on('transitionend', function() {
3910                         ce.dom.style.height = '';
3911                         ce.removeClass('collapsing');
3912                         ce.addClass('collapse');
3913                     }, this, { single: true} );
3914                     ce.setHeight(0);
3915                 }
3916             }
3917             
3918         }, this);
3919         
3920         var mark = {
3921             tag: "div",
3922             cls:"x-dlg-mask"
3923         };
3924         
3925         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3926         
3927         var size = this.el.getSize();
3928         this.maskEl.setSize(size.width, size.height);
3929         this.maskEl.enableDisplayMode("block");
3930         this.maskEl.hide();
3931         
3932         if(this.loadMask){
3933             this.maskEl.show();
3934         }
3935     },
3936     
3937     
3938     getChildContainer : function()
3939     {
3940         if (this.el.select('.collapse').getCount()) {
3941             return this.el.select('.collapse',true).first();
3942         }
3943         
3944         return this.el;
3945     },
3946     
3947     mask : function()
3948     {
3949         this.maskEl.show();
3950     },
3951     
3952     unmask : function()
3953     {
3954         this.maskEl.hide();
3955     } 
3956     
3957     
3958     
3959     
3960 });
3961
3962
3963
3964  
3965
3966  /*
3967  * - LGPL
3968  *
3969  * navbar
3970  * 
3971  */
3972
3973 /**
3974  * @class Roo.bootstrap.NavSimplebar
3975  * @extends Roo.bootstrap.Navbar
3976  * Bootstrap Sidebar class
3977  *
3978  * @cfg {Boolean} inverse is inverted color
3979  * 
3980  * @cfg {String} type (nav | pills | tabs)
3981  * @cfg {Boolean} arrangement stacked | justified
3982  * @cfg {String} align (left | right) alignment
3983  * 
3984  * @cfg {Boolean} main (true|false) main nav bar? default false
3985  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3986  * 
3987  * @cfg {String} tag (header|footer|nav|div) default is nav 
3988
3989  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3990  * 
3991  * 
3992  * @constructor
3993  * Create a new Sidebar
3994  * @param {Object} config The config object
3995  */
3996
3997
3998 Roo.bootstrap.NavSimplebar = function(config){
3999     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4000 };
4001
4002 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4003     
4004     inverse: false,
4005     
4006     type: false,
4007     arrangement: '',
4008     align : false,
4009     
4010     weight : 'light',
4011     
4012     main : false,
4013     
4014     
4015     tag : false,
4016     
4017     
4018     getAutoCreate : function(){
4019         
4020         
4021         var cfg = {
4022             tag : this.tag || 'div',
4023             cls : 'navbar navbar-expand-lg'
4024         };
4025         if (['light','white'].indexOf(this.weight) > -1) {
4026             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4027         }
4028         cfg.cls += ' bg-' + this.weight;
4029         
4030         if (this.inverse) {
4031             cfg.cls += ' navbar-inverse';
4032             
4033         }
4034         
4035         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4036         
4037         if (Roo.bootstrap.version == 4) {
4038             return cfg;
4039         }
4040         
4041         cfg.cn = [
4042             {
4043                 cls: 'nav',
4044                 tag : 'ul'
4045             }
4046         ];
4047         
4048          
4049         this.type = this.type || 'nav';
4050         if (['tabs','pills'].indexOf(this.type)!==-1) {
4051             cfg.cn[0].cls += ' nav-' + this.type
4052         
4053         
4054         } else {
4055             if (this.type!=='nav') {
4056                 Roo.log('nav type must be nav/tabs/pills')
4057             }
4058             cfg.cn[0].cls += ' navbar-nav'
4059         }
4060         
4061         
4062         
4063         
4064         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4065             cfg.cn[0].cls += ' nav-' + this.arrangement;
4066         }
4067         
4068         
4069         if (this.align === 'right') {
4070             cfg.cn[0].cls += ' navbar-right';
4071         }
4072         
4073         
4074         
4075         
4076         return cfg;
4077     
4078         
4079     }
4080     
4081     
4082     
4083 });
4084
4085
4086
4087  
4088
4089  
4090        /*
4091  * - LGPL
4092  *
4093  * navbar
4094  * navbar-fixed-top
4095  * navbar-expand-md  fixed-top 
4096  */
4097
4098 /**
4099  * @class Roo.bootstrap.NavHeaderbar
4100  * @extends Roo.bootstrap.NavSimplebar
4101  * Bootstrap Sidebar class
4102  *
4103  * @cfg {String} brand what is brand
4104  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4105  * @cfg {String} brand_href href of the brand
4106  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4107  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4108  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4109  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4110  * 
4111  * @constructor
4112  * Create a new Sidebar
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.NavHeaderbar = function(config){
4118     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4119       
4120 };
4121
4122 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4123     
4124     position: '',
4125     brand: '',
4126     brand_href: false,
4127     srButton : true,
4128     autohide : false,
4129     desktopCenter : false,
4130    
4131     
4132     getAutoCreate : function(){
4133         
4134         var   cfg = {
4135             tag: this.nav || 'nav',
4136             cls: 'navbar navbar-expand-md',
4137             role: 'navigation',
4138             cn: []
4139         };
4140         
4141         var cn = cfg.cn;
4142         if (this.desktopCenter) {
4143             cn.push({cls : 'container', cn : []});
4144             cn = cn[0].cn;
4145         }
4146         
4147         if(this.srButton){
4148             var btn = {
4149                 tag: 'button',
4150                 type: 'button',
4151                 cls: 'navbar-toggle navbar-toggler',
4152                 'data-toggle': 'collapse',
4153                 cn: [
4154                     {
4155                         tag: 'span',
4156                         cls: 'sr-only',
4157                         html: 'Toggle navigation'
4158                     },
4159                     {
4160                         tag: 'span',
4161                         cls: 'icon-bar navbar-toggler-icon'
4162                     },
4163                     {
4164                         tag: 'span',
4165                         cls: 'icon-bar'
4166                     },
4167                     {
4168                         tag: 'span',
4169                         cls: 'icon-bar'
4170                     }
4171                 ]
4172             };
4173             
4174             cn.push( Roo.bootstrap.version == 4 ? btn : {
4175                 tag: 'div',
4176                 cls: 'navbar-header',
4177                 cn: [
4178                     btn
4179                 ]
4180             });
4181         }
4182         
4183         cn.push({
4184             tag: 'div',
4185             cls: 'collapse navbar-collapse',
4186             cn : []
4187         });
4188         
4189         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4190         
4191         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4192             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4193             
4194             // tag can override this..
4195             
4196             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4197         }
4198         
4199         if (this.brand !== '') {
4200             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4201             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4202                 tag: 'a',
4203                 href: this.brand_href ? this.brand_href : '#',
4204                 cls: 'navbar-brand',
4205                 cn: [
4206                 this.brand
4207                 ]
4208             });
4209         }
4210         
4211         if(this.main){
4212             cfg.cls += ' main-nav';
4213         }
4214         
4215         
4216         return cfg;
4217
4218         
4219     },
4220     getHeaderChildContainer : function()
4221     {
4222         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4223             return this.el.select('.navbar-header',true).first();
4224         }
4225         
4226         return this.getChildContainer();
4227     },
4228     
4229     
4230     initEvents : function()
4231     {
4232         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4233         
4234         if (this.autohide) {
4235             
4236             var prevScroll = 0;
4237             var ft = this.el;
4238             
4239             Roo.get(document).on('scroll',function(e) {
4240                 var ns = Roo.get(document).getScroll().top;
4241                 var os = prevScroll;
4242                 prevScroll = ns;
4243                 
4244                 if(ns > os){
4245                     ft.removeClass('slideDown');
4246                     ft.addClass('slideUp');
4247                     return;
4248                 }
4249                 ft.removeClass('slideUp');
4250                 ft.addClass('slideDown');
4251                  
4252               
4253           },this);
4254         }
4255     }    
4256     
4257 });
4258
4259
4260
4261  
4262
4263  /*
4264  * - LGPL
4265  *
4266  * navbar
4267  * 
4268  */
4269
4270 /**
4271  * @class Roo.bootstrap.NavSidebar
4272  * @extends Roo.bootstrap.Navbar
4273  * Bootstrap Sidebar class
4274  * 
4275  * @constructor
4276  * Create a new Sidebar
4277  * @param {Object} config The config object
4278  */
4279
4280
4281 Roo.bootstrap.NavSidebar = function(config){
4282     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4283 };
4284
4285 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4286     
4287     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4288     
4289     getAutoCreate : function(){
4290         
4291         
4292         return  {
4293             tag: 'div',
4294             cls: 'sidebar sidebar-nav'
4295         };
4296     
4297         
4298     }
4299     
4300     
4301     
4302 });
4303
4304
4305
4306  
4307
4308  /*
4309  * - LGPL
4310  *
4311  * nav group
4312  * 
4313  */
4314
4315 /**
4316  * @class Roo.bootstrap.NavGroup
4317  * @extends Roo.bootstrap.Component
4318  * Bootstrap NavGroup class
4319  * @cfg {String} align (left|right)
4320  * @cfg {Boolean} inverse
4321  * @cfg {String} type (nav|pills|tab) default nav
4322  * @cfg {String} navId - reference Id for navbar.
4323
4324  * 
4325  * @constructor
4326  * Create a new nav group
4327  * @param {Object} config The config object
4328  */
4329
4330 Roo.bootstrap.NavGroup = function(config){
4331     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4332     this.navItems = [];
4333    
4334     Roo.bootstrap.NavGroup.register(this);
4335      this.addEvents({
4336         /**
4337              * @event changed
4338              * Fires when the active item changes
4339              * @param {Roo.bootstrap.NavGroup} this
4340              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4341              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4342          */
4343         'changed': true
4344      });
4345     
4346 };
4347
4348 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4349     
4350     align: '',
4351     inverse: false,
4352     form: false,
4353     type: 'nav',
4354     navId : '',
4355     // private
4356     
4357     navItems : false, 
4358     
4359     getAutoCreate : function()
4360     {
4361         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4362         
4363         cfg = {
4364             tag : 'ul',
4365             cls: 'nav' 
4366         };
4367         if (Roo.bootstrap.version == 4) {
4368             if (this.type == 'pills') {
4369                 cfg.cls = ' nav-pills';
4370             }
4371         } else {
4372             if (['tabs','pills'].indexOf(this.type)!==-1) {
4373                 cfg.cls += ' nav-' + this.type
4374             } else {
4375                 if (this.type !== 'nav') {
4376                     Roo.log('nav type must be nav/tabs/pills')
4377                 }
4378                 cfg.cls += ' navbar-nav'
4379             }
4380         }
4381         
4382         if (this.parent() && this.parent().sidebar) {
4383             cfg = {
4384                 tag: 'ul',
4385                 cls: 'dashboard-menu sidebar-menu'
4386             };
4387             
4388             return cfg;
4389         }
4390         
4391         if (this.form === true) {
4392             cfg = {
4393                 tag: 'form',
4394                 cls: 'navbar-form form-inline'
4395             };
4396             
4397             if (this.align === 'right') {
4398                 cfg.cls += ' navbar-right ml-md-auto';
4399             } else {
4400                 cfg.cls += ' navbar-left';
4401             }
4402         }
4403         
4404         if (this.align === 'right') {
4405             cfg.cls += ' navbar-right ml-md-auto';
4406         } else {
4407             cfg.cls += ' mr-auto';
4408         }
4409         
4410         if (this.inverse) {
4411             cfg.cls += ' navbar-inverse';
4412             
4413         }
4414         
4415         
4416         return cfg;
4417     },
4418     /**
4419     * sets the active Navigation item
4420     * @param {Roo.bootstrap.NavItem} the new current navitem
4421     */
4422     setActiveItem : function(item)
4423     {
4424         var prev = false;
4425         Roo.each(this.navItems, function(v){
4426             if (v == item) {
4427                 return ;
4428             }
4429             if (v.isActive()) {
4430                 v.setActive(false, true);
4431                 prev = v;
4432                 
4433             }
4434             
4435         });
4436
4437         item.setActive(true, true);
4438         this.fireEvent('changed', this, item, prev);
4439         
4440         
4441     },
4442     /**
4443     * gets the active Navigation item
4444     * @return {Roo.bootstrap.NavItem} the current navitem
4445     */
4446     getActive : function()
4447     {
4448         
4449         var prev = false;
4450         Roo.each(this.navItems, function(v){
4451             
4452             if (v.isActive()) {
4453                 prev = v;
4454                 
4455             }
4456             
4457         });
4458         return prev;
4459     },
4460     
4461     indexOfNav : function()
4462     {
4463         
4464         var prev = false;
4465         Roo.each(this.navItems, function(v,i){
4466             
4467             if (v.isActive()) {
4468                 prev = i;
4469                 
4470             }
4471             
4472         });
4473         return prev;
4474     },
4475     /**
4476     * adds a Navigation item
4477     * @param {Roo.bootstrap.NavItem} the navitem to add
4478     */
4479     addItem : function(cfg)
4480     {
4481         if (this.form && Roo.bootstrap.version == 4) {
4482             cfg.tag = 'div';
4483         }
4484         var cn = new Roo.bootstrap.NavItem(cfg);
4485         this.register(cn);
4486         cn.parentId = this.id;
4487         cn.onRender(this.el, null);
4488         return cn;
4489     },
4490     /**
4491     * register a Navigation item
4492     * @param {Roo.bootstrap.NavItem} the navitem to add
4493     */
4494     register : function(item)
4495     {
4496         this.navItems.push( item);
4497         item.navId = this.navId;
4498     
4499     },
4500     
4501     /**
4502     * clear all the Navigation item
4503     */
4504    
4505     clearAll : function()
4506     {
4507         this.navItems = [];
4508         this.el.dom.innerHTML = '';
4509     },
4510     
4511     getNavItem: function(tabId)
4512     {
4513         var ret = false;
4514         Roo.each(this.navItems, function(e) {
4515             if (e.tabId == tabId) {
4516                ret =  e;
4517                return false;
4518             }
4519             return true;
4520             
4521         });
4522         return ret;
4523     },
4524     
4525     setActiveNext : function()
4526     {
4527         var i = this.indexOfNav(this.getActive());
4528         if (i > this.navItems.length) {
4529             return;
4530         }
4531         this.setActiveItem(this.navItems[i+1]);
4532     },
4533     setActivePrev : function()
4534     {
4535         var i = this.indexOfNav(this.getActive());
4536         if (i  < 1) {
4537             return;
4538         }
4539         this.setActiveItem(this.navItems[i-1]);
4540     },
4541     clearWasActive : function(except) {
4542         Roo.each(this.navItems, function(e) {
4543             if (e.tabId != except.tabId && e.was_active) {
4544                e.was_active = false;
4545                return false;
4546             }
4547             return true;
4548             
4549         });
4550     },
4551     getWasActive : function ()
4552     {
4553         var r = false;
4554         Roo.each(this.navItems, function(e) {
4555             if (e.was_active) {
4556                r = e;
4557                return false;
4558             }
4559             return true;
4560             
4561         });
4562         return r;
4563     }
4564     
4565     
4566 });
4567
4568  
4569 Roo.apply(Roo.bootstrap.NavGroup, {
4570     
4571     groups: {},
4572      /**
4573     * register a Navigation Group
4574     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4575     */
4576     register : function(navgrp)
4577     {
4578         this.groups[navgrp.navId] = navgrp;
4579         
4580     },
4581     /**
4582     * fetch a Navigation Group based on the navigation ID
4583     * @param {string} the navgroup to add
4584     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4585     */
4586     get: function(navId) {
4587         if (typeof(this.groups[navId]) == 'undefined') {
4588             return false;
4589             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4590         }
4591         return this.groups[navId] ;
4592     }
4593     
4594     
4595     
4596 });
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.NavItem
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Navbar.NavItem class
4609  * @cfg {String} href  link to
4610  * @cfg {String} html content of button
4611  * @cfg {String} badge text inside badge
4612  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4613  * @cfg {String} glyphicon DEPRICATED - use fa
4614  * @cfg {String} icon DEPRICATED - use fa
4615  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4616  * @cfg {Boolean} active Is item active
4617  * @cfg {Boolean} disabled Is item disabled
4618  
4619  * @cfg {Boolean} preventDefault (true | false) default false
4620  * @cfg {String} tabId the tab that this item activates.
4621  * @cfg {String} tagtype (a|span) render as a href or span?
4622  * @cfg {Boolean} animateRef (true|false) link to element default false  
4623   
4624  * @constructor
4625  * Create a new Navbar Item
4626  * @param {Object} config The config object
4627  */
4628 Roo.bootstrap.NavItem = function(config){
4629     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4630     this.addEvents({
4631         // raw events
4632         /**
4633          * @event click
4634          * The raw click event for the entire grid.
4635          * @param {Roo.EventObject} e
4636          */
4637         "click" : true,
4638          /**
4639             * @event changed
4640             * Fires when the active item active state changes
4641             * @param {Roo.bootstrap.NavItem} this
4642             * @param {boolean} state the new state
4643              
4644          */
4645         'changed': true,
4646         /**
4647             * @event scrollto
4648             * Fires when scroll to element
4649             * @param {Roo.bootstrap.NavItem} this
4650             * @param {Object} options
4651             * @param {Roo.EventObject} e
4652              
4653          */
4654         'scrollto': true
4655     });
4656    
4657 };
4658
4659 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4660     
4661     href: false,
4662     html: '',
4663     badge: '',
4664     icon: false,
4665     fa : false,
4666     glyphicon: false,
4667     active: false,
4668     preventDefault : false,
4669     tabId : false,
4670     tagtype : 'a',
4671     tag: 'li',
4672     disabled : false,
4673     animateRef : false,
4674     was_active : false,
4675     
4676     getAutoCreate : function(){
4677          
4678         var cfg = {
4679             tag: this.tag,
4680             cls: 'nav-item'
4681             
4682         };
4683         
4684         if (this.active) {
4685             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4686         }
4687         if (this.disabled) {
4688             cfg.cls += ' disabled';
4689         }
4690         
4691         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4692             cfg.cn = [
4693                 {
4694                     tag: this.tagtype,
4695                     href : this.href || "#",
4696                     html: this.html || ''
4697                 }
4698             ];
4699             if (this.tagtype == 'a') {
4700                 cfg.cn[0].cls = 'nav-link';
4701             }
4702             if (this.icon) {
4703                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4704             }
4705             if (this.fa) {
4706                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4707             }
4708             if(this.glyphicon) {
4709                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4710             }
4711             
4712             if (this.menu) {
4713                 
4714                 cfg.cn[0].html += " <span class='caret'></span>";
4715              
4716             }
4717             
4718             if (this.badge !== '') {
4719                  
4720                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4721             }
4722         }
4723         
4724         
4725         
4726         return cfg;
4727     },
4728     onRender : function(ct, position)
4729     {
4730        // Roo.log("Call onRender: " + this.xtype);
4731         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4732             this.tag = 'div';
4733         }
4734         
4735         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4736     },
4737       
4738     
4739     initEvents: function() 
4740     {
4741         if (typeof (this.menu) != 'undefined') {
4742             this.menu.parentType = this.xtype;
4743             this.menu.triggerEl = this.el;
4744             this.menu = this.addxtype(Roo.apply({}, this.menu));
4745         }
4746         
4747         this.el.select('a',true).on('click', this.onClick, this);
4748         
4749         if(this.tagtype == 'span'){
4750             this.el.select('span',true).on('click', this.onClick, this);
4751         }
4752        
4753         // at this point parent should be available..
4754         this.parent().register(this);
4755     },
4756     
4757     onClick : function(e)
4758     {
4759         if (e.getTarget('.dropdown-menu-item')) {
4760             // did you click on a menu itemm.... - then don't trigger onclick..
4761             return;
4762         }
4763         
4764         if(
4765                 this.preventDefault || 
4766                 this.href == '#' 
4767         ){
4768             Roo.log("NavItem - prevent Default?");
4769             e.preventDefault();
4770         }
4771         
4772         if (this.disabled) {
4773             return;
4774         }
4775         
4776         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4777         if (tg && tg.transition) {
4778             Roo.log("waiting for the transitionend");
4779             return;
4780         }
4781         
4782         
4783         
4784         //Roo.log("fire event clicked");
4785         if(this.fireEvent('click', this, e) === false){
4786             return;
4787         };
4788         
4789         if(this.tagtype == 'span'){
4790             return;
4791         }
4792         
4793         //Roo.log(this.href);
4794         var ael = this.el.select('a',true).first();
4795         //Roo.log(ael);
4796         
4797         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4798             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4799             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4800                 return; // ignore... - it's a 'hash' to another page.
4801             }
4802             Roo.log("NavItem - prevent Default?");
4803             e.preventDefault();
4804             this.scrollToElement(e);
4805         }
4806         
4807         
4808         var p =  this.parent();
4809    
4810         if (['tabs','pills'].indexOf(p.type)!==-1) {
4811             if (typeof(p.setActiveItem) !== 'undefined') {
4812                 p.setActiveItem(this);
4813             }
4814         }
4815         
4816         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4817         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4818             // remove the collapsed menu expand...
4819             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4820         }
4821     },
4822     
4823     isActive: function () {
4824         return this.active
4825     },
4826     setActive : function(state, fire, is_was_active)
4827     {
4828         if (this.active && !state && this.navId) {
4829             this.was_active = true;
4830             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4831             if (nv) {
4832                 nv.clearWasActive(this);
4833             }
4834             
4835         }
4836         this.active = state;
4837         
4838         if (!state ) {
4839             this.el.removeClass('active');
4840         } else if (!this.el.hasClass('active')) {
4841             this.el.addClass('active');
4842         }
4843         if (fire) {
4844             this.fireEvent('changed', this, state);
4845         }
4846         
4847         // show a panel if it's registered and related..
4848         
4849         if (!this.navId || !this.tabId || !state || is_was_active) {
4850             return;
4851         }
4852         
4853         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4854         if (!tg) {
4855             return;
4856         }
4857         var pan = tg.getPanelByName(this.tabId);
4858         if (!pan) {
4859             return;
4860         }
4861         // if we can not flip to new panel - go back to old nav highlight..
4862         if (false == tg.showPanel(pan)) {
4863             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4864             if (nv) {
4865                 var onav = nv.getWasActive();
4866                 if (onav) {
4867                     onav.setActive(true, false, true);
4868                 }
4869             }
4870             
4871         }
4872         
4873         
4874         
4875     },
4876      // this should not be here...
4877     setDisabled : function(state)
4878     {
4879         this.disabled = state;
4880         if (!state ) {
4881             this.el.removeClass('disabled');
4882         } else if (!this.el.hasClass('disabled')) {
4883             this.el.addClass('disabled');
4884         }
4885         
4886     },
4887     
4888     /**
4889      * Fetch the element to display the tooltip on.
4890      * @return {Roo.Element} defaults to this.el
4891      */
4892     tooltipEl : function()
4893     {
4894         return this.el.select('' + this.tagtype + '', true).first();
4895     },
4896     
4897     scrollToElement : function(e)
4898     {
4899         var c = document.body;
4900         
4901         /*
4902          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4903          */
4904         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4905             c = document.documentElement;
4906         }
4907         
4908         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4909         
4910         if(!target){
4911             return;
4912         }
4913
4914         var o = target.calcOffsetsTo(c);
4915         
4916         var options = {
4917             target : target,
4918             value : o[1]
4919         };
4920         
4921         this.fireEvent('scrollto', this, options, e);
4922         
4923         Roo.get(c).scrollTo('top', options.value, true);
4924         
4925         return;
4926     }
4927 });
4928  
4929
4930  /*
4931  * - LGPL
4932  *
4933  * sidebar item
4934  *
4935  *  li
4936  *    <span> icon </span>
4937  *    <span> text </span>
4938  *    <span>badge </span>
4939  */
4940
4941 /**
4942  * @class Roo.bootstrap.NavSidebarItem
4943  * @extends Roo.bootstrap.NavItem
4944  * Bootstrap Navbar.NavSidebarItem class
4945  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4946  * {Boolean} open is the menu open
4947  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4948  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4949  * {String} buttonSize (sm|md|lg)the extra classes for the button
4950  * {Boolean} showArrow show arrow next to the text (default true)
4951  * @constructor
4952  * Create a new Navbar Button
4953  * @param {Object} config The config object
4954  */
4955 Roo.bootstrap.NavSidebarItem = function(config){
4956     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4957     this.addEvents({
4958         // raw events
4959         /**
4960          * @event click
4961          * The raw click event for the entire grid.
4962          * @param {Roo.EventObject} e
4963          */
4964         "click" : true,
4965          /**
4966             * @event changed
4967             * Fires when the active item active state changes
4968             * @param {Roo.bootstrap.NavSidebarItem} this
4969             * @param {boolean} state the new state
4970              
4971          */
4972         'changed': true
4973     });
4974    
4975 };
4976
4977 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4978     
4979     badgeWeight : 'default',
4980     
4981     open: false,
4982     
4983     buttonView : false,
4984     
4985     buttonWeight : 'default',
4986     
4987     buttonSize : 'md',
4988     
4989     showArrow : true,
4990     
4991     getAutoCreate : function(){
4992         
4993         
4994         var a = {
4995                 tag: 'a',
4996                 href : this.href || '#',
4997                 cls: '',
4998                 html : '',
4999                 cn : []
5000         };
5001         
5002         if(this.buttonView){
5003             a = {
5004                 tag: 'button',
5005                 href : this.href || '#',
5006                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5007                 html : this.html,
5008                 cn : []
5009             };
5010         }
5011         
5012         var cfg = {
5013             tag: 'li',
5014             cls: '',
5015             cn: [ a ]
5016         };
5017         
5018         if (this.active) {
5019             cfg.cls += ' active';
5020         }
5021         
5022         if (this.disabled) {
5023             cfg.cls += ' disabled';
5024         }
5025         if (this.open) {
5026             cfg.cls += ' open x-open';
5027         }
5028         // left icon..
5029         if (this.glyphicon || this.icon) {
5030             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5031             a.cn.push({ tag : 'i', cls : c }) ;
5032         }
5033         
5034         if(!this.buttonView){
5035             var span = {
5036                 tag: 'span',
5037                 html : this.html || ''
5038             };
5039
5040             a.cn.push(span);
5041             
5042         }
5043         
5044         if (this.badge !== '') {
5045             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5046         }
5047         
5048         if (this.menu) {
5049             
5050             if(this.showArrow){
5051                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5052             }
5053             
5054             a.cls += ' dropdown-toggle treeview' ;
5055         }
5056         
5057         return cfg;
5058     },
5059     
5060     initEvents : function()
5061     { 
5062         if (typeof (this.menu) != 'undefined') {
5063             this.menu.parentType = this.xtype;
5064             this.menu.triggerEl = this.el;
5065             this.menu = this.addxtype(Roo.apply({}, this.menu));
5066         }
5067         
5068         this.el.on('click', this.onClick, this);
5069         
5070         if(this.badge !== ''){
5071             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5072         }
5073         
5074     },
5075     
5076     onClick : function(e)
5077     {
5078         if(this.disabled){
5079             e.preventDefault();
5080             return;
5081         }
5082         
5083         if(this.preventDefault){
5084             e.preventDefault();
5085         }
5086         
5087         this.fireEvent('click', this);
5088     },
5089     
5090     disable : function()
5091     {
5092         this.setDisabled(true);
5093     },
5094     
5095     enable : function()
5096     {
5097         this.setDisabled(false);
5098     },
5099     
5100     setDisabled : function(state)
5101     {
5102         if(this.disabled == state){
5103             return;
5104         }
5105         
5106         this.disabled = state;
5107         
5108         if (state) {
5109             this.el.addClass('disabled');
5110             return;
5111         }
5112         
5113         this.el.removeClass('disabled');
5114         
5115         return;
5116     },
5117     
5118     setActive : function(state)
5119     {
5120         if(this.active == state){
5121             return;
5122         }
5123         
5124         this.active = state;
5125         
5126         if (state) {
5127             this.el.addClass('active');
5128             return;
5129         }
5130         
5131         this.el.removeClass('active');
5132         
5133         return;
5134     },
5135     
5136     isActive: function () 
5137     {
5138         return this.active;
5139     },
5140     
5141     setBadge : function(str)
5142     {
5143         if(!this.badgeEl){
5144             return;
5145         }
5146         
5147         this.badgeEl.dom.innerHTML = str;
5148     }
5149     
5150    
5151      
5152  
5153 });
5154  
5155
5156  /*
5157  * - LGPL
5158  *
5159  * row
5160  * 
5161  */
5162
5163 /**
5164  * @class Roo.bootstrap.Row
5165  * @extends Roo.bootstrap.Component
5166  * Bootstrap Row class (contains columns...)
5167  * 
5168  * @constructor
5169  * Create a new Row
5170  * @param {Object} config The config object
5171  */
5172
5173 Roo.bootstrap.Row = function(config){
5174     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5175 };
5176
5177 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5178     
5179     getAutoCreate : function(){
5180        return {
5181             cls: 'row clearfix'
5182        };
5183     }
5184     
5185     
5186 });
5187
5188  
5189
5190  /*
5191  * - LGPL
5192  *
5193  * element
5194  * 
5195  */
5196
5197 /**
5198  * @class Roo.bootstrap.Element
5199  * @extends Roo.bootstrap.Component
5200  * Bootstrap Element class
5201  * @cfg {String} html contents of the element
5202  * @cfg {String} tag tag of the element
5203  * @cfg {String} cls class of the element
5204  * @cfg {Boolean} preventDefault (true|false) default false
5205  * @cfg {Boolean} clickable (true|false) default false
5206  * 
5207  * @constructor
5208  * Create a new Element
5209  * @param {Object} config The config object
5210  */
5211
5212 Roo.bootstrap.Element = function(config){
5213     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5214     
5215     this.addEvents({
5216         // raw events
5217         /**
5218          * @event click
5219          * When a element is chick
5220          * @param {Roo.bootstrap.Element} this
5221          * @param {Roo.EventObject} e
5222          */
5223         "click" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5228     
5229     tag: 'div',
5230     cls: '',
5231     html: '',
5232     preventDefault: false, 
5233     clickable: false,
5234     
5235     getAutoCreate : function(){
5236         
5237         var cfg = {
5238             tag: this.tag,
5239             // cls: this.cls, double assign in parent class Component.js :: onRender
5240             html: this.html
5241         };
5242         
5243         return cfg;
5244     },
5245     
5246     initEvents: function() 
5247     {
5248         Roo.bootstrap.Element.superclass.initEvents.call(this);
5249         
5250         if(this.clickable){
5251             this.el.on('click', this.onClick, this);
5252         }
5253         
5254     },
5255     
5256     onClick : function(e)
5257     {
5258         if(this.preventDefault){
5259             e.preventDefault();
5260         }
5261         
5262         this.fireEvent('click', this, e);
5263     },
5264     
5265     getValue : function()
5266     {
5267         return this.el.dom.innerHTML;
5268     },
5269     
5270     setValue : function(value)
5271     {
5272         this.el.dom.innerHTML = value;
5273     }
5274    
5275 });
5276
5277  
5278
5279  /*
5280  * - LGPL
5281  *
5282  * pagination
5283  * 
5284  */
5285
5286 /**
5287  * @class Roo.bootstrap.Pagination
5288  * @extends Roo.bootstrap.Component
5289  * Bootstrap Pagination class
5290  * @cfg {String} size xs | sm | md | lg
5291  * @cfg {Boolean} inverse false | true
5292  * 
5293  * @constructor
5294  * Create a new Pagination
5295  * @param {Object} config The config object
5296  */
5297
5298 Roo.bootstrap.Pagination = function(config){
5299     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5300 };
5301
5302 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5303     
5304     cls: false,
5305     size: false,
5306     inverse: false,
5307     
5308     getAutoCreate : function(){
5309         var cfg = {
5310             tag: 'ul',
5311                 cls: 'pagination'
5312         };
5313         if (this.inverse) {
5314             cfg.cls += ' inverse';
5315         }
5316         if (this.html) {
5317             cfg.html=this.html;
5318         }
5319         if (this.cls) {
5320             cfg.cls += " " + this.cls;
5321         }
5322         return cfg;
5323     }
5324    
5325 });
5326
5327  
5328
5329  /*
5330  * - LGPL
5331  *
5332  * Pagination item
5333  * 
5334  */
5335
5336
5337 /**
5338  * @class Roo.bootstrap.PaginationItem
5339  * @extends Roo.bootstrap.Component
5340  * Bootstrap PaginationItem class
5341  * @cfg {String} html text
5342  * @cfg {String} href the link
5343  * @cfg {Boolean} preventDefault (true | false) default true
5344  * @cfg {Boolean} active (true | false) default false
5345  * @cfg {Boolean} disabled default false
5346  * 
5347  * 
5348  * @constructor
5349  * Create a new PaginationItem
5350  * @param {Object} config The config object
5351  */
5352
5353
5354 Roo.bootstrap.PaginationItem = function(config){
5355     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5356     this.addEvents({
5357         // raw events
5358         /**
5359          * @event click
5360          * The raw click event for the entire grid.
5361          * @param {Roo.EventObject} e
5362          */
5363         "click" : true
5364     });
5365 };
5366
5367 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5368     
5369     href : false,
5370     html : false,
5371     preventDefault: true,
5372     active : false,
5373     cls : false,
5374     disabled: false,
5375     
5376     getAutoCreate : function(){
5377         var cfg= {
5378             tag: 'li',
5379             cn: [
5380                 {
5381                     tag : 'a',
5382                     href : this.href ? this.href : '#',
5383                     html : this.html ? this.html : ''
5384                 }
5385             ]
5386         };
5387         
5388         if(this.cls){
5389             cfg.cls = this.cls;
5390         }
5391         
5392         if(this.disabled){
5393             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5394         }
5395         
5396         if(this.active){
5397             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5398         }
5399         
5400         return cfg;
5401     },
5402     
5403     initEvents: function() {
5404         
5405         this.el.on('click', this.onClick, this);
5406         
5407     },
5408     onClick : function(e)
5409     {
5410         Roo.log('PaginationItem on click ');
5411         if(this.preventDefault){
5412             e.preventDefault();
5413         }
5414         
5415         if(this.disabled){
5416             return;
5417         }
5418         
5419         this.fireEvent('click', this, e);
5420     }
5421    
5422 });
5423
5424  
5425
5426  /*
5427  * - LGPL
5428  *
5429  * slider
5430  * 
5431  */
5432
5433
5434 /**
5435  * @class Roo.bootstrap.Slider
5436  * @extends Roo.bootstrap.Component
5437  * Bootstrap Slider class
5438  *    
5439  * @constructor
5440  * Create a new Slider
5441  * @param {Object} config The config object
5442  */
5443
5444 Roo.bootstrap.Slider = function(config){
5445     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5446 };
5447
5448 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5449     
5450     getAutoCreate : function(){
5451         
5452         var cfg = {
5453             tag: 'div',
5454             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5455             cn: [
5456                 {
5457                     tag: 'a',
5458                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5459                 }
5460             ]
5461         };
5462         
5463         return cfg;
5464     }
5465    
5466 });
5467
5468  /*
5469  * Based on:
5470  * Ext JS Library 1.1.1
5471  * Copyright(c) 2006-2007, Ext JS, LLC.
5472  *
5473  * Originally Released Under LGPL - original licence link has changed is not relivant.
5474  *
5475  * Fork - LGPL
5476  * <script type="text/javascript">
5477  */
5478  
5479
5480 /**
5481  * @class Roo.grid.ColumnModel
5482  * @extends Roo.util.Observable
5483  * This is the default implementation of a ColumnModel used by the Grid. It defines
5484  * the columns in the grid.
5485  * <br>Usage:<br>
5486  <pre><code>
5487  var colModel = new Roo.grid.ColumnModel([
5488         {header: "Ticker", width: 60, sortable: true, locked: true},
5489         {header: "Company Name", width: 150, sortable: true},
5490         {header: "Market Cap.", width: 100, sortable: true},
5491         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5492         {header: "Employees", width: 100, sortable: true, resizable: false}
5493  ]);
5494  </code></pre>
5495  * <p>
5496  
5497  * The config options listed for this class are options which may appear in each
5498  * individual column definition.
5499  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5500  * @constructor
5501  * @param {Object} config An Array of column config objects. See this class's
5502  * config objects for details.
5503 */
5504 Roo.grid.ColumnModel = function(config){
5505         /**
5506      * The config passed into the constructor
5507      */
5508     this.config = config;
5509     this.lookup = {};
5510
5511     // if no id, create one
5512     // if the column does not have a dataIndex mapping,
5513     // map it to the order it is in the config
5514     for(var i = 0, len = config.length; i < len; i++){
5515         var c = config[i];
5516         if(typeof c.dataIndex == "undefined"){
5517             c.dataIndex = i;
5518         }
5519         if(typeof c.renderer == "string"){
5520             c.renderer = Roo.util.Format[c.renderer];
5521         }
5522         if(typeof c.id == "undefined"){
5523             c.id = Roo.id();
5524         }
5525         if(c.editor && c.editor.xtype){
5526             c.editor  = Roo.factory(c.editor, Roo.grid);
5527         }
5528         if(c.editor && c.editor.isFormField){
5529             c.editor = new Roo.grid.GridEditor(c.editor);
5530         }
5531         this.lookup[c.id] = c;
5532     }
5533
5534     /**
5535      * The width of columns which have no width specified (defaults to 100)
5536      * @type Number
5537      */
5538     this.defaultWidth = 100;
5539
5540     /**
5541      * Default sortable of columns which have no sortable specified (defaults to false)
5542      * @type Boolean
5543      */
5544     this.defaultSortable = false;
5545
5546     this.addEvents({
5547         /**
5548              * @event widthchange
5549              * Fires when the width of a column changes.
5550              * @param {ColumnModel} this
5551              * @param {Number} columnIndex The column index
5552              * @param {Number} newWidth The new width
5553              */
5554             "widthchange": true,
5555         /**
5556              * @event headerchange
5557              * Fires when the text of a header changes.
5558              * @param {ColumnModel} this
5559              * @param {Number} columnIndex The column index
5560              * @param {Number} newText The new header text
5561              */
5562             "headerchange": true,
5563         /**
5564              * @event hiddenchange
5565              * Fires when a column is hidden or "unhidden".
5566              * @param {ColumnModel} this
5567              * @param {Number} columnIndex The column index
5568              * @param {Boolean} hidden true if hidden, false otherwise
5569              */
5570             "hiddenchange": true,
5571             /**
5572          * @event columnmoved
5573          * Fires when a column is moved.
5574          * @param {ColumnModel} this
5575          * @param {Number} oldIndex
5576          * @param {Number} newIndex
5577          */
5578         "columnmoved" : true,
5579         /**
5580          * @event columlockchange
5581          * Fires when a column's locked state is changed
5582          * @param {ColumnModel} this
5583          * @param {Number} colIndex
5584          * @param {Boolean} locked true if locked
5585          */
5586         "columnlockchange" : true
5587     });
5588     Roo.grid.ColumnModel.superclass.constructor.call(this);
5589 };
5590 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5591     /**
5592      * @cfg {String} header The header text to display in the Grid view.
5593      */
5594     /**
5595      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5596      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5597      * specified, the column's index is used as an index into the Record's data Array.
5598      */
5599     /**
5600      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5601      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5602      */
5603     /**
5604      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5605      * Defaults to the value of the {@link #defaultSortable} property.
5606      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5607      */
5608     /**
5609      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5610      */
5611     /**
5612      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5613      */
5614     /**
5615      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5616      */
5617     /**
5618      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5619      */
5620     /**
5621      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5622      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5623      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5624      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5625      */
5626        /**
5627      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5628      */
5629     /**
5630      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5631      */
5632     /**
5633      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5634      */
5635     /**
5636      * @cfg {String} cursor (Optional)
5637      */
5638     /**
5639      * @cfg {String} tooltip (Optional)
5640      */
5641     /**
5642      * @cfg {Number} xs (Optional)
5643      */
5644     /**
5645      * @cfg {Number} sm (Optional)
5646      */
5647     /**
5648      * @cfg {Number} md (Optional)
5649      */
5650     /**
5651      * @cfg {Number} lg (Optional)
5652      */
5653     /**
5654      * Returns the id of the column at the specified index.
5655      * @param {Number} index The column index
5656      * @return {String} the id
5657      */
5658     getColumnId : function(index){
5659         return this.config[index].id;
5660     },
5661
5662     /**
5663      * Returns the column for a specified id.
5664      * @param {String} id The column id
5665      * @return {Object} the column
5666      */
5667     getColumnById : function(id){
5668         return this.lookup[id];
5669     },
5670
5671     
5672     /**
5673      * Returns the column for a specified dataIndex.
5674      * @param {String} dataIndex The column dataIndex
5675      * @return {Object|Boolean} the column or false if not found
5676      */
5677     getColumnByDataIndex: function(dataIndex){
5678         var index = this.findColumnIndex(dataIndex);
5679         return index > -1 ? this.config[index] : false;
5680     },
5681     
5682     /**
5683      * Returns the index for a specified column id.
5684      * @param {String} id The column id
5685      * @return {Number} the index, or -1 if not found
5686      */
5687     getIndexById : function(id){
5688         for(var i = 0, len = this.config.length; i < len; i++){
5689             if(this.config[i].id == id){
5690                 return i;
5691             }
5692         }
5693         return -1;
5694     },
5695     
5696     /**
5697      * Returns the index for a specified column dataIndex.
5698      * @param {String} dataIndex The column dataIndex
5699      * @return {Number} the index, or -1 if not found
5700      */
5701     
5702     findColumnIndex : function(dataIndex){
5703         for(var i = 0, len = this.config.length; i < len; i++){
5704             if(this.config[i].dataIndex == dataIndex){
5705                 return i;
5706             }
5707         }
5708         return -1;
5709     },
5710     
5711     
5712     moveColumn : function(oldIndex, newIndex){
5713         var c = this.config[oldIndex];
5714         this.config.splice(oldIndex, 1);
5715         this.config.splice(newIndex, 0, c);
5716         this.dataMap = null;
5717         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5718     },
5719
5720     isLocked : function(colIndex){
5721         return this.config[colIndex].locked === true;
5722     },
5723
5724     setLocked : function(colIndex, value, suppressEvent){
5725         if(this.isLocked(colIndex) == value){
5726             return;
5727         }
5728         this.config[colIndex].locked = value;
5729         if(!suppressEvent){
5730             this.fireEvent("columnlockchange", this, colIndex, value);
5731         }
5732     },
5733
5734     getTotalLockedWidth : function(){
5735         var totalWidth = 0;
5736         for(var i = 0; i < this.config.length; i++){
5737             if(this.isLocked(i) && !this.isHidden(i)){
5738                 this.totalWidth += this.getColumnWidth(i);
5739             }
5740         }
5741         return totalWidth;
5742     },
5743
5744     getLockedCount : function(){
5745         for(var i = 0, len = this.config.length; i < len; i++){
5746             if(!this.isLocked(i)){
5747                 return i;
5748             }
5749         }
5750         
5751         return this.config.length;
5752     },
5753
5754     /**
5755      * Returns the number of columns.
5756      * @return {Number}
5757      */
5758     getColumnCount : function(visibleOnly){
5759         if(visibleOnly === true){
5760             var c = 0;
5761             for(var i = 0, len = this.config.length; i < len; i++){
5762                 if(!this.isHidden(i)){
5763                     c++;
5764                 }
5765             }
5766             return c;
5767         }
5768         return this.config.length;
5769     },
5770
5771     /**
5772      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5773      * @param {Function} fn
5774      * @param {Object} scope (optional)
5775      * @return {Array} result
5776      */
5777     getColumnsBy : function(fn, scope){
5778         var r = [];
5779         for(var i = 0, len = this.config.length; i < len; i++){
5780             var c = this.config[i];
5781             if(fn.call(scope||this, c, i) === true){
5782                 r[r.length] = c;
5783             }
5784         }
5785         return r;
5786     },
5787
5788     /**
5789      * Returns true if the specified column is sortable.
5790      * @param {Number} col The column index
5791      * @return {Boolean}
5792      */
5793     isSortable : function(col){
5794         if(typeof this.config[col].sortable == "undefined"){
5795             return this.defaultSortable;
5796         }
5797         return this.config[col].sortable;
5798     },
5799
5800     /**
5801      * Returns the rendering (formatting) function defined for the column.
5802      * @param {Number} col The column index.
5803      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5804      */
5805     getRenderer : function(col){
5806         if(!this.config[col].renderer){
5807             return Roo.grid.ColumnModel.defaultRenderer;
5808         }
5809         return this.config[col].renderer;
5810     },
5811
5812     /**
5813      * Sets the rendering (formatting) function for a column.
5814      * @param {Number} col The column index
5815      * @param {Function} fn The function to use to process the cell's raw data
5816      * to return HTML markup for the grid view. The render function is called with
5817      * the following parameters:<ul>
5818      * <li>Data value.</li>
5819      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5820      * <li>css A CSS style string to apply to the table cell.</li>
5821      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5822      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5823      * <li>Row index</li>
5824      * <li>Column index</li>
5825      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5826      */
5827     setRenderer : function(col, fn){
5828         this.config[col].renderer = fn;
5829     },
5830
5831     /**
5832      * Returns the width for the specified column.
5833      * @param {Number} col The column index
5834      * @return {Number}
5835      */
5836     getColumnWidth : function(col){
5837         return this.config[col].width * 1 || this.defaultWidth;
5838     },
5839
5840     /**
5841      * Sets the width for a column.
5842      * @param {Number} col The column index
5843      * @param {Number} width The new width
5844      */
5845     setColumnWidth : function(col, width, suppressEvent){
5846         this.config[col].width = width;
5847         this.totalWidth = null;
5848         if(!suppressEvent){
5849              this.fireEvent("widthchange", this, col, width);
5850         }
5851     },
5852
5853     /**
5854      * Returns the total width of all columns.
5855      * @param {Boolean} includeHidden True to include hidden column widths
5856      * @return {Number}
5857      */
5858     getTotalWidth : function(includeHidden){
5859         if(!this.totalWidth){
5860             this.totalWidth = 0;
5861             for(var i = 0, len = this.config.length; i < len; i++){
5862                 if(includeHidden || !this.isHidden(i)){
5863                     this.totalWidth += this.getColumnWidth(i);
5864                 }
5865             }
5866         }
5867         return this.totalWidth;
5868     },
5869
5870     /**
5871      * Returns the header for the specified column.
5872      * @param {Number} col The column index
5873      * @return {String}
5874      */
5875     getColumnHeader : function(col){
5876         return this.config[col].header;
5877     },
5878
5879     /**
5880      * Sets the header for a column.
5881      * @param {Number} col The column index
5882      * @param {String} header The new header
5883      */
5884     setColumnHeader : function(col, header){
5885         this.config[col].header = header;
5886         this.fireEvent("headerchange", this, col, header);
5887     },
5888
5889     /**
5890      * Returns the tooltip for the specified column.
5891      * @param {Number} col The column index
5892      * @return {String}
5893      */
5894     getColumnTooltip : function(col){
5895             return this.config[col].tooltip;
5896     },
5897     /**
5898      * Sets the tooltip for a column.
5899      * @param {Number} col The column index
5900      * @param {String} tooltip The new tooltip
5901      */
5902     setColumnTooltip : function(col, tooltip){
5903             this.config[col].tooltip = tooltip;
5904     },
5905
5906     /**
5907      * Returns the dataIndex for the specified column.
5908      * @param {Number} col The column index
5909      * @return {Number}
5910      */
5911     getDataIndex : function(col){
5912         return this.config[col].dataIndex;
5913     },
5914
5915     /**
5916      * Sets the dataIndex for a column.
5917      * @param {Number} col The column index
5918      * @param {Number} dataIndex The new dataIndex
5919      */
5920     setDataIndex : function(col, dataIndex){
5921         this.config[col].dataIndex = dataIndex;
5922     },
5923
5924     
5925     
5926     /**
5927      * Returns true if the cell is editable.
5928      * @param {Number} colIndex The column index
5929      * @param {Number} rowIndex The row index - this is nto actually used..?
5930      * @return {Boolean}
5931      */
5932     isCellEditable : function(colIndex, rowIndex){
5933         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5934     },
5935
5936     /**
5937      * Returns the editor defined for the cell/column.
5938      * return false or null to disable editing.
5939      * @param {Number} colIndex The column index
5940      * @param {Number} rowIndex The row index
5941      * @return {Object}
5942      */
5943     getCellEditor : function(colIndex, rowIndex){
5944         return this.config[colIndex].editor;
5945     },
5946
5947     /**
5948      * Sets if a column is editable.
5949      * @param {Number} col The column index
5950      * @param {Boolean} editable True if the column is editable
5951      */
5952     setEditable : function(col, editable){
5953         this.config[col].editable = editable;
5954     },
5955
5956
5957     /**
5958      * Returns true if the column is hidden.
5959      * @param {Number} colIndex The column index
5960      * @return {Boolean}
5961      */
5962     isHidden : function(colIndex){
5963         return this.config[colIndex].hidden;
5964     },
5965
5966
5967     /**
5968      * Returns true if the column width cannot be changed
5969      */
5970     isFixed : function(colIndex){
5971         return this.config[colIndex].fixed;
5972     },
5973
5974     /**
5975      * Returns true if the column can be resized
5976      * @return {Boolean}
5977      */
5978     isResizable : function(colIndex){
5979         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5980     },
5981     /**
5982      * Sets if a column is hidden.
5983      * @param {Number} colIndex The column index
5984      * @param {Boolean} hidden True if the column is hidden
5985      */
5986     setHidden : function(colIndex, hidden){
5987         this.config[colIndex].hidden = hidden;
5988         this.totalWidth = null;
5989         this.fireEvent("hiddenchange", this, colIndex, hidden);
5990     },
5991
5992     /**
5993      * Sets the editor for a column.
5994      * @param {Number} col The column index
5995      * @param {Object} editor The editor object
5996      */
5997     setEditor : function(col, editor){
5998         this.config[col].editor = editor;
5999     }
6000 });
6001
6002 Roo.grid.ColumnModel.defaultRenderer = function(value)
6003 {
6004     if(typeof value == "object") {
6005         return value;
6006     }
6007         if(typeof value == "string" && value.length < 1){
6008             return "&#160;";
6009         }
6010     
6011         return String.format("{0}", value);
6012 };
6013
6014 // Alias for backwards compatibility
6015 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6016 /*
6017  * Based on:
6018  * Ext JS Library 1.1.1
6019  * Copyright(c) 2006-2007, Ext JS, LLC.
6020  *
6021  * Originally Released Under LGPL - original licence link has changed is not relivant.
6022  *
6023  * Fork - LGPL
6024  * <script type="text/javascript">
6025  */
6026  
6027 /**
6028  * @class Roo.LoadMask
6029  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6030  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6031  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6032  * element's UpdateManager load indicator and will be destroyed after the initial load.
6033  * @constructor
6034  * Create a new LoadMask
6035  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6036  * @param {Object} config The config object
6037  */
6038 Roo.LoadMask = function(el, config){
6039     this.el = Roo.get(el);
6040     Roo.apply(this, config);
6041     if(this.store){
6042         this.store.on('beforeload', this.onBeforeLoad, this);
6043         this.store.on('load', this.onLoad, this);
6044         this.store.on('loadexception', this.onLoadException, this);
6045         this.removeMask = false;
6046     }else{
6047         var um = this.el.getUpdateManager();
6048         um.showLoadIndicator = false; // disable the default indicator
6049         um.on('beforeupdate', this.onBeforeLoad, this);
6050         um.on('update', this.onLoad, this);
6051         um.on('failure', this.onLoad, this);
6052         this.removeMask = true;
6053     }
6054 };
6055
6056 Roo.LoadMask.prototype = {
6057     /**
6058      * @cfg {Boolean} removeMask
6059      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6060      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6061      */
6062     /**
6063      * @cfg {String} msg
6064      * The text to display in a centered loading message box (defaults to 'Loading...')
6065      */
6066     msg : 'Loading...',
6067     /**
6068      * @cfg {String} msgCls
6069      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6070      */
6071     msgCls : 'x-mask-loading',
6072
6073     /**
6074      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6075      * @type Boolean
6076      */
6077     disabled: false,
6078
6079     /**
6080      * Disables the mask to prevent it from being displayed
6081      */
6082     disable : function(){
6083        this.disabled = true;
6084     },
6085
6086     /**
6087      * Enables the mask so that it can be displayed
6088      */
6089     enable : function(){
6090         this.disabled = false;
6091     },
6092     
6093     onLoadException : function()
6094     {
6095         Roo.log(arguments);
6096         
6097         if (typeof(arguments[3]) != 'undefined') {
6098             Roo.MessageBox.alert("Error loading",arguments[3]);
6099         } 
6100         /*
6101         try {
6102             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6103                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6104             }   
6105         } catch(e) {
6106             
6107         }
6108         */
6109     
6110         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6111     },
6112     // private
6113     onLoad : function()
6114     {
6115         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6116     },
6117
6118     // private
6119     onBeforeLoad : function(){
6120         if(!this.disabled){
6121             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6122         }
6123     },
6124
6125     // private
6126     destroy : function(){
6127         if(this.store){
6128             this.store.un('beforeload', this.onBeforeLoad, this);
6129             this.store.un('load', this.onLoad, this);
6130             this.store.un('loadexception', this.onLoadException, this);
6131         }else{
6132             var um = this.el.getUpdateManager();
6133             um.un('beforeupdate', this.onBeforeLoad, this);
6134             um.un('update', this.onLoad, this);
6135             um.un('failure', this.onLoad, this);
6136         }
6137     }
6138 };/*
6139  * - LGPL
6140  *
6141  * table
6142  * 
6143  */
6144
6145 /**
6146  * @class Roo.bootstrap.Table
6147  * @extends Roo.bootstrap.Component
6148  * Bootstrap Table class
6149  * @cfg {String} cls table class
6150  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6151  * @cfg {String} bgcolor Specifies the background color for a table
6152  * @cfg {Number} border Specifies whether the table cells should have borders or not
6153  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6154  * @cfg {Number} cellspacing Specifies the space between cells
6155  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6156  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6157  * @cfg {String} sortable Specifies that the table should be sortable
6158  * @cfg {String} summary Specifies a summary of the content of a table
6159  * @cfg {Number} width Specifies the width of a table
6160  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6161  * 
6162  * @cfg {boolean} striped Should the rows be alternative striped
6163  * @cfg {boolean} bordered Add borders to the table
6164  * @cfg {boolean} hover Add hover highlighting
6165  * @cfg {boolean} condensed Format condensed
6166  * @cfg {boolean} responsive Format condensed
6167  * @cfg {Boolean} loadMask (true|false) default false
6168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6169  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6170  * @cfg {Boolean} rowSelection (true|false) default false
6171  * @cfg {Boolean} cellSelection (true|false) default false
6172  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6173  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6174  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6175  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6176  
6177  * 
6178  * @constructor
6179  * Create a new Table
6180  * @param {Object} config The config object
6181  */
6182
6183 Roo.bootstrap.Table = function(config){
6184     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6185     
6186   
6187     
6188     // BC...
6189     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6190     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6191     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6192     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6193     
6194     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6195     if (this.sm) {
6196         this.sm.grid = this;
6197         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6198         this.sm = this.selModel;
6199         this.sm.xmodule = this.xmodule || false;
6200     }
6201     
6202     if (this.cm && typeof(this.cm.config) == 'undefined') {
6203         this.colModel = new Roo.grid.ColumnModel(this.cm);
6204         this.cm = this.colModel;
6205         this.cm.xmodule = this.xmodule || false;
6206     }
6207     if (this.store) {
6208         this.store= Roo.factory(this.store, Roo.data);
6209         this.ds = this.store;
6210         this.ds.xmodule = this.xmodule || false;
6211          
6212     }
6213     if (this.footer && this.store) {
6214         this.footer.dataSource = this.ds;
6215         this.footer = Roo.factory(this.footer);
6216     }
6217     
6218     /** @private */
6219     this.addEvents({
6220         /**
6221          * @event cellclick
6222          * Fires when a cell is clicked
6223          * @param {Roo.bootstrap.Table} this
6224          * @param {Roo.Element} el
6225          * @param {Number} rowIndex
6226          * @param {Number} columnIndex
6227          * @param {Roo.EventObject} e
6228          */
6229         "cellclick" : true,
6230         /**
6231          * @event celldblclick
6232          * Fires when a cell is double clicked
6233          * @param {Roo.bootstrap.Table} this
6234          * @param {Roo.Element} el
6235          * @param {Number} rowIndex
6236          * @param {Number} columnIndex
6237          * @param {Roo.EventObject} e
6238          */
6239         "celldblclick" : true,
6240         /**
6241          * @event rowclick
6242          * Fires when a row is clicked
6243          * @param {Roo.bootstrap.Table} this
6244          * @param {Roo.Element} el
6245          * @param {Number} rowIndex
6246          * @param {Roo.EventObject} e
6247          */
6248         "rowclick" : true,
6249         /**
6250          * @event rowdblclick
6251          * Fires when a row is double clicked
6252          * @param {Roo.bootstrap.Table} this
6253          * @param {Roo.Element} el
6254          * @param {Number} rowIndex
6255          * @param {Roo.EventObject} e
6256          */
6257         "rowdblclick" : true,
6258         /**
6259          * @event mouseover
6260          * Fires when a mouseover occur
6261          * @param {Roo.bootstrap.Table} this
6262          * @param {Roo.Element} el
6263          * @param {Number} rowIndex
6264          * @param {Number} columnIndex
6265          * @param {Roo.EventObject} e
6266          */
6267         "mouseover" : true,
6268         /**
6269          * @event mouseout
6270          * Fires when a mouseout occur
6271          * @param {Roo.bootstrap.Table} this
6272          * @param {Roo.Element} el
6273          * @param {Number} rowIndex
6274          * @param {Number} columnIndex
6275          * @param {Roo.EventObject} e
6276          */
6277         "mouseout" : true,
6278         /**
6279          * @event rowclass
6280          * Fires when a row is rendered, so you can change add a style to it.
6281          * @param {Roo.bootstrap.Table} this
6282          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6283          */
6284         'rowclass' : true,
6285           /**
6286          * @event rowsrendered
6287          * Fires when all the  rows have been rendered
6288          * @param {Roo.bootstrap.Table} this
6289          */
6290         'rowsrendered' : true,
6291         /**
6292          * @event contextmenu
6293          * The raw contextmenu event for the entire grid.
6294          * @param {Roo.EventObject} e
6295          */
6296         "contextmenu" : true,
6297         /**
6298          * @event rowcontextmenu
6299          * Fires when a row is right clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Number} rowIndex
6302          * @param {Roo.EventObject} e
6303          */
6304         "rowcontextmenu" : true,
6305         /**
6306          * @event cellcontextmenu
6307          * Fires when a cell is right clicked
6308          * @param {Roo.bootstrap.Table} this
6309          * @param {Number} rowIndex
6310          * @param {Number} cellIndex
6311          * @param {Roo.EventObject} e
6312          */
6313          "cellcontextmenu" : true,
6314          /**
6315          * @event headercontextmenu
6316          * Fires when a header is right clicked
6317          * @param {Roo.bootstrap.Table} this
6318          * @param {Number} columnIndex
6319          * @param {Roo.EventObject} e
6320          */
6321         "headercontextmenu" : true
6322     });
6323 };
6324
6325 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6326     
6327     cls: false,
6328     align: false,
6329     bgcolor: false,
6330     border: false,
6331     cellpadding: false,
6332     cellspacing: false,
6333     frame: false,
6334     rules: false,
6335     sortable: false,
6336     summary: false,
6337     width: false,
6338     striped : false,
6339     scrollBody : false,
6340     bordered: false,
6341     hover:  false,
6342     condensed : false,
6343     responsive : false,
6344     sm : false,
6345     cm : false,
6346     store : false,
6347     loadMask : false,
6348     footerShow : true,
6349     headerShow : true,
6350   
6351     rowSelection : false,
6352     cellSelection : false,
6353     layout : false,
6354     
6355     // Roo.Element - the tbody
6356     mainBody: false,
6357     // Roo.Element - thead element
6358     mainHead: false,
6359     
6360     container: false, // used by gridpanel...
6361     
6362     lazyLoad : false,
6363     
6364     CSS : Roo.util.CSS,
6365     
6366     auto_hide_footer : false,
6367     
6368     getAutoCreate : function()
6369     {
6370         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6371         
6372         cfg = {
6373             tag: 'table',
6374             cls : 'table',
6375             cn : []
6376         };
6377         if (this.scrollBody) {
6378             cfg.cls += ' table-body-fixed';
6379         }    
6380         if (this.striped) {
6381             cfg.cls += ' table-striped';
6382         }
6383         
6384         if (this.hover) {
6385             cfg.cls += ' table-hover';
6386         }
6387         if (this.bordered) {
6388             cfg.cls += ' table-bordered';
6389         }
6390         if (this.condensed) {
6391             cfg.cls += ' table-condensed';
6392         }
6393         if (this.responsive) {
6394             cfg.cls += ' table-responsive';
6395         }
6396         
6397         if (this.cls) {
6398             cfg.cls+=  ' ' +this.cls;
6399         }
6400         
6401         // this lot should be simplifed...
6402         var _t = this;
6403         var cp = [
6404             'align',
6405             'bgcolor',
6406             'border',
6407             'cellpadding',
6408             'cellspacing',
6409             'frame',
6410             'rules',
6411             'sortable',
6412             'summary',
6413             'width'
6414         ].forEach(function(k) {
6415             if (_t[k]) {
6416                 cfg[k] = _t[k];
6417             }
6418         });
6419         
6420         
6421         if (this.layout) {
6422             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6423         }
6424         
6425         if(this.store || this.cm){
6426             if(this.headerShow){
6427                 cfg.cn.push(this.renderHeader());
6428             }
6429             
6430             cfg.cn.push(this.renderBody());
6431             
6432             if(this.footerShow){
6433                 cfg.cn.push(this.renderFooter());
6434             }
6435             // where does this come from?
6436             //cfg.cls+=  ' TableGrid';
6437         }
6438         
6439         return { cn : [ cfg ] };
6440     },
6441     
6442     initEvents : function()
6443     {   
6444         if(!this.store || !this.cm){
6445             return;
6446         }
6447         if (this.selModel) {
6448             this.selModel.initEvents();
6449         }
6450         
6451         
6452         //Roo.log('initEvents with ds!!!!');
6453         
6454         this.mainBody = this.el.select('tbody', true).first();
6455         this.mainHead = this.el.select('thead', true).first();
6456         this.mainFoot = this.el.select('tfoot', true).first();
6457         
6458         
6459         
6460         var _this = this;
6461         
6462         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6463             e.on('click', _this.sort, _this);
6464         });
6465         
6466         this.mainBody.on("click", this.onClick, this);
6467         this.mainBody.on("dblclick", this.onDblClick, this);
6468         
6469         // why is this done????? = it breaks dialogs??
6470         //this.parent().el.setStyle('position', 'relative');
6471         
6472         
6473         if (this.footer) {
6474             this.footer.parentId = this.id;
6475             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6476             
6477             if(this.lazyLoad){
6478                 this.el.select('tfoot tr td').first().addClass('hide');
6479             }
6480         } 
6481         
6482         if(this.loadMask) {
6483             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6484         }
6485         
6486         this.store.on('load', this.onLoad, this);
6487         this.store.on('beforeload', this.onBeforeLoad, this);
6488         this.store.on('update', this.onUpdate, this);
6489         this.store.on('add', this.onAdd, this);
6490         this.store.on("clear", this.clear, this);
6491         
6492         this.el.on("contextmenu", this.onContextMenu, this);
6493         
6494         this.mainBody.on('scroll', this.onBodyScroll, this);
6495         
6496         this.cm.on("headerchange", this.onHeaderChange, this);
6497         
6498         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6499         
6500     },
6501     
6502     onContextMenu : function(e, t)
6503     {
6504         this.processEvent("contextmenu", e);
6505     },
6506     
6507     processEvent : function(name, e)
6508     {
6509         if (name != 'touchstart' ) {
6510             this.fireEvent(name, e);    
6511         }
6512         
6513         var t = e.getTarget();
6514         
6515         var cell = Roo.get(t);
6516         
6517         if(!cell){
6518             return;
6519         }
6520         
6521         if(cell.findParent('tfoot', false, true)){
6522             return;
6523         }
6524         
6525         if(cell.findParent('thead', false, true)){
6526             
6527             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6528                 cell = Roo.get(t).findParent('th', false, true);
6529                 if (!cell) {
6530                     Roo.log("failed to find th in thead?");
6531                     Roo.log(e.getTarget());
6532                     return;
6533                 }
6534             }
6535             
6536             var cellIndex = cell.dom.cellIndex;
6537             
6538             var ename = name == 'touchstart' ? 'click' : name;
6539             this.fireEvent("header" + ename, this, cellIndex, e);
6540             
6541             return;
6542         }
6543         
6544         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6545             cell = Roo.get(t).findParent('td', false, true);
6546             if (!cell) {
6547                 Roo.log("failed to find th in tbody?");
6548                 Roo.log(e.getTarget());
6549                 return;
6550             }
6551         }
6552         
6553         var row = cell.findParent('tr', false, true);
6554         var cellIndex = cell.dom.cellIndex;
6555         var rowIndex = row.dom.rowIndex - 1;
6556         
6557         if(row !== false){
6558             
6559             this.fireEvent("row" + name, this, rowIndex, e);
6560             
6561             if(cell !== false){
6562             
6563                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6564             }
6565         }
6566         
6567     },
6568     
6569     onMouseover : function(e, el)
6570     {
6571         var cell = Roo.get(el);
6572         
6573         if(!cell){
6574             return;
6575         }
6576         
6577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578             cell = cell.findParent('td', false, true);
6579         }
6580         
6581         var row = cell.findParent('tr', false, true);
6582         var cellIndex = cell.dom.cellIndex;
6583         var rowIndex = row.dom.rowIndex - 1; // start from 0
6584         
6585         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6586         
6587     },
6588     
6589     onMouseout : function(e, el)
6590     {
6591         var cell = Roo.get(el);
6592         
6593         if(!cell){
6594             return;
6595         }
6596         
6597         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6598             cell = cell.findParent('td', false, true);
6599         }
6600         
6601         var row = cell.findParent('tr', false, true);
6602         var cellIndex = cell.dom.cellIndex;
6603         var rowIndex = row.dom.rowIndex - 1; // start from 0
6604         
6605         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6606         
6607     },
6608     
6609     onClick : function(e, el)
6610     {
6611         var cell = Roo.get(el);
6612         
6613         if(!cell || (!this.cellSelection && !this.rowSelection)){
6614             return;
6615         }
6616         
6617         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6618             cell = cell.findParent('td', false, true);
6619         }
6620         
6621         if(!cell || typeof(cell) == 'undefined'){
6622             return;
6623         }
6624         
6625         var row = cell.findParent('tr', false, true);
6626         
6627         if(!row || typeof(row) == 'undefined'){
6628             return;
6629         }
6630         
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = this.getRowIndex(row);
6633         
6634         // why??? - should these not be based on SelectionModel?
6635         if(this.cellSelection){
6636             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6637         }
6638         
6639         if(this.rowSelection){
6640             this.fireEvent('rowclick', this, row, rowIndex, e);
6641         }
6642         
6643         
6644     },
6645         
6646     onDblClick : function(e,el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell || (!this.cellSelection && !this.rowSelection)){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         if(!cell || typeof(cell) == 'undefined'){
6659             return;
6660         }
6661         
6662         var row = cell.findParent('tr', false, true);
6663         
6664         if(!row || typeof(row) == 'undefined'){
6665             return;
6666         }
6667         
6668         var cellIndex = cell.dom.cellIndex;
6669         var rowIndex = this.getRowIndex(row);
6670         
6671         if(this.cellSelection){
6672             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6673         }
6674         
6675         if(this.rowSelection){
6676             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6677         }
6678     },
6679     
6680     sort : function(e,el)
6681     {
6682         var col = Roo.get(el);
6683         
6684         if(!col.hasClass('sortable')){
6685             return;
6686         }
6687         
6688         var sort = col.attr('sort');
6689         var dir = 'ASC';
6690         
6691         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6692             dir = 'DESC';
6693         }
6694         
6695         this.store.sortInfo = {field : sort, direction : dir};
6696         
6697         if (this.footer) {
6698             Roo.log("calling footer first");
6699             this.footer.onClick('first');
6700         } else {
6701         
6702             this.store.load({ params : { start : 0 } });
6703         }
6704     },
6705     
6706     renderHeader : function()
6707     {
6708         var header = {
6709             tag: 'thead',
6710             cn : []
6711         };
6712         
6713         var cm = this.cm;
6714         this.totalWidth = 0;
6715         
6716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6717             
6718             var config = cm.config[i];
6719             
6720             var c = {
6721                 tag: 'th',
6722                 cls : 'x-hcol-' + i,
6723                 style : '',
6724                 html: cm.getColumnHeader(i)
6725             };
6726             
6727             var hh = '';
6728             
6729             if(typeof(config.sortable) != 'undefined' && config.sortable){
6730                 c.cls = 'sortable';
6731                 c.html = '<i class="glyphicon"></i>' + c.html;
6732             }
6733             
6734             if(typeof(config.lgHeader) != 'undefined'){
6735                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6736             }
6737             
6738             if(typeof(config.mdHeader) != 'undefined'){
6739                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6740             }
6741             
6742             if(typeof(config.smHeader) != 'undefined'){
6743                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6744             }
6745             
6746             if(typeof(config.xsHeader) != 'undefined'){
6747                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6748             }
6749             
6750             if(hh.length){
6751                 c.html = hh;
6752             }
6753             
6754             if(typeof(config.tooltip) != 'undefined'){
6755                 c.tooltip = config.tooltip;
6756             }
6757             
6758             if(typeof(config.colspan) != 'undefined'){
6759                 c.colspan = config.colspan;
6760             }
6761             
6762             if(typeof(config.hidden) != 'undefined' && config.hidden){
6763                 c.style += ' display:none;';
6764             }
6765             
6766             if(typeof(config.dataIndex) != 'undefined'){
6767                 c.sort = config.dataIndex;
6768             }
6769             
6770            
6771             
6772             if(typeof(config.align) != 'undefined' && config.align.length){
6773                 c.style += ' text-align:' + config.align + ';';
6774             }
6775             
6776             if(typeof(config.width) != 'undefined'){
6777                 c.style += ' width:' + config.width + 'px;';
6778                 this.totalWidth += config.width;
6779             } else {
6780                 this.totalWidth += 100; // assume minimum of 100 per column?
6781             }
6782             
6783             if(typeof(config.cls) != 'undefined'){
6784                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6785             }
6786             
6787             ['xs','sm','md','lg'].map(function(size){
6788                 
6789                 if(typeof(config[size]) == 'undefined'){
6790                     return;
6791                 }
6792                 
6793                 if (!config[size]) { // 0 = hidden
6794                     c.cls += ' hidden-' + size;
6795                     return;
6796                 }
6797                 
6798                 c.cls += ' col-' + size + '-' + config[size];
6799
6800             });
6801             
6802             header.cn.push(c)
6803         }
6804         
6805         return header;
6806     },
6807     
6808     renderBody : function()
6809     {
6810         var body = {
6811             tag: 'tbody',
6812             cn : [
6813                 {
6814                     tag: 'tr',
6815                     cn : [
6816                         {
6817                             tag : 'td',
6818                             colspan :  this.cm.getColumnCount()
6819                         }
6820                     ]
6821                 }
6822             ]
6823         };
6824         
6825         return body;
6826     },
6827     
6828     renderFooter : function()
6829     {
6830         var footer = {
6831             tag: 'tfoot',
6832             cn : [
6833                 {
6834                     tag: 'tr',
6835                     cn : [
6836                         {
6837                             tag : 'td',
6838                             colspan :  this.cm.getColumnCount()
6839                         }
6840                     ]
6841                 }
6842             ]
6843         };
6844         
6845         return footer;
6846     },
6847     
6848     
6849     
6850     onLoad : function()
6851     {
6852 //        Roo.log('ds onload');
6853         this.clear();
6854         
6855         var _this = this;
6856         var cm = this.cm;
6857         var ds = this.store;
6858         
6859         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6860             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6861             if (_this.store.sortInfo) {
6862                     
6863                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6864                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6865                 }
6866                 
6867                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6868                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6869                 }
6870             }
6871         });
6872         
6873         var tbody =  this.mainBody;
6874               
6875         if(ds.getCount() > 0){
6876             ds.data.each(function(d,rowIndex){
6877                 var row =  this.renderRow(cm, ds, rowIndex);
6878                 
6879                 tbody.createChild(row);
6880                 
6881                 var _this = this;
6882                 
6883                 if(row.cellObjects.length){
6884                     Roo.each(row.cellObjects, function(r){
6885                         _this.renderCellObject(r);
6886                     })
6887                 }
6888                 
6889             }, this);
6890         }
6891         
6892         var tfoot = this.el.select('tfoot', true).first();
6893         
6894         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6895             
6896             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6897             
6898             var total = this.ds.getTotalCount();
6899             
6900             if(this.footer.pageSize < total){
6901                 this.mainFoot.show();
6902             }
6903         }
6904         
6905         Roo.each(this.el.select('tbody td', true).elements, function(e){
6906             e.on('mouseover', _this.onMouseover, _this);
6907         });
6908         
6909         Roo.each(this.el.select('tbody td', true).elements, function(e){
6910             e.on('mouseout', _this.onMouseout, _this);
6911         });
6912         this.fireEvent('rowsrendered', this);
6913         
6914         this.autoSize();
6915     },
6916     
6917     
6918     onUpdate : function(ds,record)
6919     {
6920         this.refreshRow(record);
6921         this.autoSize();
6922     },
6923     
6924     onRemove : function(ds, record, index, isUpdate){
6925         if(isUpdate !== true){
6926             this.fireEvent("beforerowremoved", this, index, record);
6927         }
6928         var bt = this.mainBody.dom;
6929         
6930         var rows = this.el.select('tbody > tr', true).elements;
6931         
6932         if(typeof(rows[index]) != 'undefined'){
6933             bt.removeChild(rows[index].dom);
6934         }
6935         
6936 //        if(bt.rows[index]){
6937 //            bt.removeChild(bt.rows[index]);
6938 //        }
6939         
6940         if(isUpdate !== true){
6941             //this.stripeRows(index);
6942             //this.syncRowHeights(index, index);
6943             //this.layout();
6944             this.fireEvent("rowremoved", this, index, record);
6945         }
6946     },
6947     
6948     onAdd : function(ds, records, rowIndex)
6949     {
6950         //Roo.log('on Add called');
6951         // - note this does not handle multiple adding very well..
6952         var bt = this.mainBody.dom;
6953         for (var i =0 ; i < records.length;i++) {
6954             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6955             //Roo.log(records[i]);
6956             //Roo.log(this.store.getAt(rowIndex+i));
6957             this.insertRow(this.store, rowIndex + i, false);
6958             return;
6959         }
6960         
6961     },
6962     
6963     
6964     refreshRow : function(record){
6965         var ds = this.store, index;
6966         if(typeof record == 'number'){
6967             index = record;
6968             record = ds.getAt(index);
6969         }else{
6970             index = ds.indexOf(record);
6971         }
6972         this.insertRow(ds, index, true);
6973         this.autoSize();
6974         this.onRemove(ds, record, index+1, true);
6975         this.autoSize();
6976         //this.syncRowHeights(index, index);
6977         //this.layout();
6978         this.fireEvent("rowupdated", this, index, record);
6979     },
6980     
6981     insertRow : function(dm, rowIndex, isUpdate){
6982         
6983         if(!isUpdate){
6984             this.fireEvent("beforerowsinserted", this, rowIndex);
6985         }
6986             //var s = this.getScrollState();
6987         var row = this.renderRow(this.cm, this.store, rowIndex);
6988         // insert before rowIndex..
6989         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6990         
6991         var _this = this;
6992                 
6993         if(row.cellObjects.length){
6994             Roo.each(row.cellObjects, function(r){
6995                 _this.renderCellObject(r);
6996             })
6997         }
6998             
6999         if(!isUpdate){
7000             this.fireEvent("rowsinserted", this, rowIndex);
7001             //this.syncRowHeights(firstRow, lastRow);
7002             //this.stripeRows(firstRow);
7003             //this.layout();
7004         }
7005         
7006     },
7007     
7008     
7009     getRowDom : function(rowIndex)
7010     {
7011         var rows = this.el.select('tbody > tr', true).elements;
7012         
7013         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7014         
7015     },
7016     // returns the object tree for a tr..
7017   
7018     
7019     renderRow : function(cm, ds, rowIndex) 
7020     {
7021         var d = ds.getAt(rowIndex);
7022         
7023         var row = {
7024             tag : 'tr',
7025             cls : 'x-row-' + rowIndex,
7026             cn : []
7027         };
7028             
7029         var cellObjects = [];
7030         
7031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7032             var config = cm.config[i];
7033             
7034             var renderer = cm.getRenderer(i);
7035             var value = '';
7036             var id = false;
7037             
7038             if(typeof(renderer) !== 'undefined'){
7039                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7040             }
7041             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7042             // and are rendered into the cells after the row is rendered - using the id for the element.
7043             
7044             if(typeof(value) === 'object'){
7045                 id = Roo.id();
7046                 cellObjects.push({
7047                     container : id,
7048                     cfg : value 
7049                 })
7050             }
7051             
7052             var rowcfg = {
7053                 record: d,
7054                 rowIndex : rowIndex,
7055                 colIndex : i,
7056                 rowClass : ''
7057             };
7058
7059             this.fireEvent('rowclass', this, rowcfg);
7060             
7061             var td = {
7062                 tag: 'td',
7063                 cls : rowcfg.rowClass + ' x-col-' + i,
7064                 style: '',
7065                 html: (typeof(value) === 'object') ? '' : value
7066             };
7067             
7068             if (id) {
7069                 td.id = id;
7070             }
7071             
7072             if(typeof(config.colspan) != 'undefined'){
7073                 td.colspan = config.colspan;
7074             }
7075             
7076             if(typeof(config.hidden) != 'undefined' && config.hidden){
7077                 td.style += ' display:none;';
7078             }
7079             
7080             if(typeof(config.align) != 'undefined' && config.align.length){
7081                 td.style += ' text-align:' + config.align + ';';
7082             }
7083             if(typeof(config.valign) != 'undefined' && config.valign.length){
7084                 td.style += ' vertical-align:' + config.valign + ';';
7085             }
7086             
7087             if(typeof(config.width) != 'undefined'){
7088                 td.style += ' width:' +  config.width + 'px;';
7089             }
7090             
7091             if(typeof(config.cursor) != 'undefined'){
7092                 td.style += ' cursor:' +  config.cursor + ';';
7093             }
7094             
7095             if(typeof(config.cls) != 'undefined'){
7096                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7097             }
7098             
7099             ['xs','sm','md','lg'].map(function(size){
7100                 
7101                 if(typeof(config[size]) == 'undefined'){
7102                     return;
7103                 }
7104                 
7105                 if (!config[size]) { // 0 = hidden
7106                     td.cls += ' hidden-' + size;
7107                     return;
7108                 }
7109                 
7110                 td.cls += ' col-' + size + '-' + config[size];
7111
7112             });
7113             
7114             row.cn.push(td);
7115            
7116         }
7117         
7118         row.cellObjects = cellObjects;
7119         
7120         return row;
7121           
7122     },
7123     
7124     
7125     
7126     onBeforeLoad : function()
7127     {
7128         
7129     },
7130      /**
7131      * Remove all rows
7132      */
7133     clear : function()
7134     {
7135         this.el.select('tbody', true).first().dom.innerHTML = '';
7136     },
7137     /**
7138      * Show or hide a row.
7139      * @param {Number} rowIndex to show or hide
7140      * @param {Boolean} state hide
7141      */
7142     setRowVisibility : function(rowIndex, state)
7143     {
7144         var bt = this.mainBody.dom;
7145         
7146         var rows = this.el.select('tbody > tr', true).elements;
7147         
7148         if(typeof(rows[rowIndex]) == 'undefined'){
7149             return;
7150         }
7151         rows[rowIndex].dom.style.display = state ? '' : 'none';
7152     },
7153     
7154     
7155     getSelectionModel : function(){
7156         if(!this.selModel){
7157             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7158         }
7159         return this.selModel;
7160     },
7161     /*
7162      * Render the Roo.bootstrap object from renderder
7163      */
7164     renderCellObject : function(r)
7165     {
7166         var _this = this;
7167         
7168         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7169         
7170         var t = r.cfg.render(r.container);
7171         
7172         if(r.cfg.cn){
7173             Roo.each(r.cfg.cn, function(c){
7174                 var child = {
7175                     container: t.getChildContainer(),
7176                     cfg: c
7177                 };
7178                 _this.renderCellObject(child);
7179             })
7180         }
7181     },
7182     
7183     getRowIndex : function(row)
7184     {
7185         var rowIndex = -1;
7186         
7187         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7188             if(el != row){
7189                 return;
7190             }
7191             
7192             rowIndex = index;
7193         });
7194         
7195         return rowIndex;
7196     },
7197      /**
7198      * Returns the grid's underlying element = used by panel.Grid
7199      * @return {Element} The element
7200      */
7201     getGridEl : function(){
7202         return this.el;
7203     },
7204      /**
7205      * Forces a resize - used by panel.Grid
7206      * @return {Element} The element
7207      */
7208     autoSize : function()
7209     {
7210         //var ctr = Roo.get(this.container.dom.parentElement);
7211         var ctr = Roo.get(this.el.dom);
7212         
7213         var thd = this.getGridEl().select('thead',true).first();
7214         var tbd = this.getGridEl().select('tbody', true).first();
7215         var tfd = this.getGridEl().select('tfoot', true).first();
7216         
7217         var cw = ctr.getWidth();
7218         
7219         if (tbd) {
7220             
7221             tbd.setSize(ctr.getWidth(),
7222                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7223             );
7224             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7225             cw -= barsize;
7226         }
7227         cw = Math.max(cw, this.totalWidth);
7228         this.getGridEl().select('tr',true).setWidth(cw);
7229         // resize 'expandable coloumn?
7230         
7231         return; // we doe not have a view in this design..
7232         
7233     },
7234     onBodyScroll: function()
7235     {
7236         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7237         if(this.mainHead){
7238             this.mainHead.setStyle({
7239                 'position' : 'relative',
7240                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7241             });
7242         }
7243         
7244         if(this.lazyLoad){
7245             
7246             var scrollHeight = this.mainBody.dom.scrollHeight;
7247             
7248             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7249             
7250             var height = this.mainBody.getHeight();
7251             
7252             if(scrollHeight - height == scrollTop) {
7253                 
7254                 var total = this.ds.getTotalCount();
7255                 
7256                 if(this.footer.cursor + this.footer.pageSize < total){
7257                     
7258                     this.footer.ds.load({
7259                         params : {
7260                             start : this.footer.cursor + this.footer.pageSize,
7261                             limit : this.footer.pageSize
7262                         },
7263                         add : true
7264                     });
7265                 }
7266             }
7267             
7268         }
7269     },
7270     
7271     onHeaderChange : function()
7272     {
7273         var header = this.renderHeader();
7274         var table = this.el.select('table', true).first();
7275         
7276         this.mainHead.remove();
7277         this.mainHead = table.createChild(header, this.mainBody, false);
7278     },
7279     
7280     onHiddenChange : function(colModel, colIndex, hidden)
7281     {
7282         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7283         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7284         
7285         this.CSS.updateRule(thSelector, "display", "");
7286         this.CSS.updateRule(tdSelector, "display", "");
7287         
7288         if(hidden){
7289             this.CSS.updateRule(thSelector, "display", "none");
7290             this.CSS.updateRule(tdSelector, "display", "none");
7291         }
7292         
7293         this.onHeaderChange();
7294         this.onLoad();
7295     },
7296     
7297     setColumnWidth: function(col_index, width)
7298     {
7299         // width = "md-2 xs-2..."
7300         if(!this.colModel.config[col_index]) {
7301             return;
7302         }
7303         
7304         var w = width.split(" ");
7305         
7306         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7307         
7308         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7309         
7310         
7311         for(var j = 0; j < w.length; j++) {
7312             
7313             if(!w[j]) {
7314                 continue;
7315             }
7316             
7317             var size_cls = w[j].split("-");
7318             
7319             if(!Number.isInteger(size_cls[1] * 1)) {
7320                 continue;
7321             }
7322             
7323             if(!this.colModel.config[col_index][size_cls[0]]) {
7324                 continue;
7325             }
7326             
7327             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7328                 continue;
7329             }
7330             
7331             h_row[0].classList.replace(
7332                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7333                 "col-"+size_cls[0]+"-"+size_cls[1]
7334             );
7335             
7336             for(var i = 0; i < rows.length; i++) {
7337                 
7338                 var size_cls = w[j].split("-");
7339                 
7340                 if(!Number.isInteger(size_cls[1] * 1)) {
7341                     continue;
7342                 }
7343                 
7344                 if(!this.colModel.config[col_index][size_cls[0]]) {
7345                     continue;
7346                 }
7347                 
7348                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7349                     continue;
7350                 }
7351                 
7352                 rows[i].classList.replace(
7353                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7354                     "col-"+size_cls[0]+"-"+size_cls[1]
7355                 );
7356             }
7357             
7358             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7359         }
7360     }
7361 });
7362
7363  
7364
7365  /*
7366  * - LGPL
7367  *
7368  * table cell
7369  * 
7370  */
7371
7372 /**
7373  * @class Roo.bootstrap.TableCell
7374  * @extends Roo.bootstrap.Component
7375  * Bootstrap TableCell class
7376  * @cfg {String} html cell contain text
7377  * @cfg {String} cls cell class
7378  * @cfg {String} tag cell tag (td|th) default td
7379  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7380  * @cfg {String} align Aligns the content in a cell
7381  * @cfg {String} axis Categorizes cells
7382  * @cfg {String} bgcolor Specifies the background color of a cell
7383  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7384  * @cfg {Number} colspan Specifies the number of columns a cell should span
7385  * @cfg {String} headers Specifies one or more header cells a cell is related to
7386  * @cfg {Number} height Sets the height of a cell
7387  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7388  * @cfg {Number} rowspan Sets the number of rows a cell should span
7389  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7390  * @cfg {String} valign Vertical aligns the content in a cell
7391  * @cfg {Number} width Specifies the width of a cell
7392  * 
7393  * @constructor
7394  * Create a new TableCell
7395  * @param {Object} config The config object
7396  */
7397
7398 Roo.bootstrap.TableCell = function(config){
7399     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7400 };
7401
7402 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7403     
7404     html: false,
7405     cls: false,
7406     tag: false,
7407     abbr: false,
7408     align: false,
7409     axis: false,
7410     bgcolor: false,
7411     charoff: false,
7412     colspan: false,
7413     headers: false,
7414     height: false,
7415     nowrap: false,
7416     rowspan: false,
7417     scope: false,
7418     valign: false,
7419     width: false,
7420     
7421     
7422     getAutoCreate : function(){
7423         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7424         
7425         cfg = {
7426             tag: 'td'
7427         };
7428         
7429         if(this.tag){
7430             cfg.tag = this.tag;
7431         }
7432         
7433         if (this.html) {
7434             cfg.html=this.html
7435         }
7436         if (this.cls) {
7437             cfg.cls=this.cls
7438         }
7439         if (this.abbr) {
7440             cfg.abbr=this.abbr
7441         }
7442         if (this.align) {
7443             cfg.align=this.align
7444         }
7445         if (this.axis) {
7446             cfg.axis=this.axis
7447         }
7448         if (this.bgcolor) {
7449             cfg.bgcolor=this.bgcolor
7450         }
7451         if (this.charoff) {
7452             cfg.charoff=this.charoff
7453         }
7454         if (this.colspan) {
7455             cfg.colspan=this.colspan
7456         }
7457         if (this.headers) {
7458             cfg.headers=this.headers
7459         }
7460         if (this.height) {
7461             cfg.height=this.height
7462         }
7463         if (this.nowrap) {
7464             cfg.nowrap=this.nowrap
7465         }
7466         if (this.rowspan) {
7467             cfg.rowspan=this.rowspan
7468         }
7469         if (this.scope) {
7470             cfg.scope=this.scope
7471         }
7472         if (this.valign) {
7473             cfg.valign=this.valign
7474         }
7475         if (this.width) {
7476             cfg.width=this.width
7477         }
7478         
7479         
7480         return cfg;
7481     }
7482    
7483 });
7484
7485  
7486
7487  /*
7488  * - LGPL
7489  *
7490  * table row
7491  * 
7492  */
7493
7494 /**
7495  * @class Roo.bootstrap.TableRow
7496  * @extends Roo.bootstrap.Component
7497  * Bootstrap TableRow class
7498  * @cfg {String} cls row class
7499  * @cfg {String} align Aligns the content in a table row
7500  * @cfg {String} bgcolor Specifies a background color for a table row
7501  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7502  * @cfg {String} valign Vertical aligns the content in a table row
7503  * 
7504  * @constructor
7505  * Create a new TableRow
7506  * @param {Object} config The config object
7507  */
7508
7509 Roo.bootstrap.TableRow = function(config){
7510     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7511 };
7512
7513 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7514     
7515     cls: false,
7516     align: false,
7517     bgcolor: false,
7518     charoff: false,
7519     valign: false,
7520     
7521     getAutoCreate : function(){
7522         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7523         
7524         cfg = {
7525             tag: 'tr'
7526         };
7527             
7528         if(this.cls){
7529             cfg.cls = this.cls;
7530         }
7531         if(this.align){
7532             cfg.align = this.align;
7533         }
7534         if(this.bgcolor){
7535             cfg.bgcolor = this.bgcolor;
7536         }
7537         if(this.charoff){
7538             cfg.charoff = this.charoff;
7539         }
7540         if(this.valign){
7541             cfg.valign = this.valign;
7542         }
7543         
7544         return cfg;
7545     }
7546    
7547 });
7548
7549  
7550
7551  /*
7552  * - LGPL
7553  *
7554  * table body
7555  * 
7556  */
7557
7558 /**
7559  * @class Roo.bootstrap.TableBody
7560  * @extends Roo.bootstrap.Component
7561  * Bootstrap TableBody class
7562  * @cfg {String} cls element class
7563  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7564  * @cfg {String} align Aligns the content inside the element
7565  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7566  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7567  * 
7568  * @constructor
7569  * Create a new TableBody
7570  * @param {Object} config The config object
7571  */
7572
7573 Roo.bootstrap.TableBody = function(config){
7574     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7575 };
7576
7577 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7578     
7579     cls: false,
7580     tag: false,
7581     align: false,
7582     charoff: false,
7583     valign: false,
7584     
7585     getAutoCreate : function(){
7586         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7587         
7588         cfg = {
7589             tag: 'tbody'
7590         };
7591             
7592         if (this.cls) {
7593             cfg.cls=this.cls
7594         }
7595         if(this.tag){
7596             cfg.tag = this.tag;
7597         }
7598         
7599         if(this.align){
7600             cfg.align = this.align;
7601         }
7602         if(this.charoff){
7603             cfg.charoff = this.charoff;
7604         }
7605         if(this.valign){
7606             cfg.valign = this.valign;
7607         }
7608         
7609         return cfg;
7610     }
7611     
7612     
7613 //    initEvents : function()
7614 //    {
7615 //        
7616 //        if(!this.store){
7617 //            return;
7618 //        }
7619 //        
7620 //        this.store = Roo.factory(this.store, Roo.data);
7621 //        this.store.on('load', this.onLoad, this);
7622 //        
7623 //        this.store.load();
7624 //        
7625 //    },
7626 //    
7627 //    onLoad: function () 
7628 //    {   
7629 //        this.fireEvent('load', this);
7630 //    }
7631 //    
7632 //   
7633 });
7634
7635  
7636
7637  /*
7638  * Based on:
7639  * Ext JS Library 1.1.1
7640  * Copyright(c) 2006-2007, Ext JS, LLC.
7641  *
7642  * Originally Released Under LGPL - original licence link has changed is not relivant.
7643  *
7644  * Fork - LGPL
7645  * <script type="text/javascript">
7646  */
7647
7648 // as we use this in bootstrap.
7649 Roo.namespace('Roo.form');
7650  /**
7651  * @class Roo.form.Action
7652  * Internal Class used to handle form actions
7653  * @constructor
7654  * @param {Roo.form.BasicForm} el The form element or its id
7655  * @param {Object} config Configuration options
7656  */
7657
7658  
7659  
7660 // define the action interface
7661 Roo.form.Action = function(form, options){
7662     this.form = form;
7663     this.options = options || {};
7664 };
7665 /**
7666  * Client Validation Failed
7667  * @const 
7668  */
7669 Roo.form.Action.CLIENT_INVALID = 'client';
7670 /**
7671  * Server Validation Failed
7672  * @const 
7673  */
7674 Roo.form.Action.SERVER_INVALID = 'server';
7675  /**
7676  * Connect to Server Failed
7677  * @const 
7678  */
7679 Roo.form.Action.CONNECT_FAILURE = 'connect';
7680 /**
7681  * Reading Data from Server Failed
7682  * @const 
7683  */
7684 Roo.form.Action.LOAD_FAILURE = 'load';
7685
7686 Roo.form.Action.prototype = {
7687     type : 'default',
7688     failureType : undefined,
7689     response : undefined,
7690     result : undefined,
7691
7692     // interface method
7693     run : function(options){
7694
7695     },
7696
7697     // interface method
7698     success : function(response){
7699
7700     },
7701
7702     // interface method
7703     handleResponse : function(response){
7704
7705     },
7706
7707     // default connection failure
7708     failure : function(response){
7709         
7710         this.response = response;
7711         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7712         this.form.afterAction(this, false);
7713     },
7714
7715     processResponse : function(response){
7716         this.response = response;
7717         if(!response.responseText){
7718             return true;
7719         }
7720         this.result = this.handleResponse(response);
7721         return this.result;
7722     },
7723
7724     // utility functions used internally
7725     getUrl : function(appendParams){
7726         var url = this.options.url || this.form.url || this.form.el.dom.action;
7727         if(appendParams){
7728             var p = this.getParams();
7729             if(p){
7730                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7731             }
7732         }
7733         return url;
7734     },
7735
7736     getMethod : function(){
7737         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7738     },
7739
7740     getParams : function(){
7741         var bp = this.form.baseParams;
7742         var p = this.options.params;
7743         if(p){
7744             if(typeof p == "object"){
7745                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7746             }else if(typeof p == 'string' && bp){
7747                 p += '&' + Roo.urlEncode(bp);
7748             }
7749         }else if(bp){
7750             p = Roo.urlEncode(bp);
7751         }
7752         return p;
7753     },
7754
7755     createCallback : function(){
7756         return {
7757             success: this.success,
7758             failure: this.failure,
7759             scope: this,
7760             timeout: (this.form.timeout*1000),
7761             upload: this.form.fileUpload ? this.success : undefined
7762         };
7763     }
7764 };
7765
7766 Roo.form.Action.Submit = function(form, options){
7767     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7768 };
7769
7770 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7771     type : 'submit',
7772
7773     haveProgress : false,
7774     uploadComplete : false,
7775     
7776     // uploadProgress indicator.
7777     uploadProgress : function()
7778     {
7779         if (!this.form.progressUrl) {
7780             return;
7781         }
7782         
7783         if (!this.haveProgress) {
7784             Roo.MessageBox.progress("Uploading", "Uploading");
7785         }
7786         if (this.uploadComplete) {
7787            Roo.MessageBox.hide();
7788            return;
7789         }
7790         
7791         this.haveProgress = true;
7792    
7793         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7794         
7795         var c = new Roo.data.Connection();
7796         c.request({
7797             url : this.form.progressUrl,
7798             params: {
7799                 id : uid
7800             },
7801             method: 'GET',
7802             success : function(req){
7803                //console.log(data);
7804                 var rdata = false;
7805                 var edata;
7806                 try  {
7807                    rdata = Roo.decode(req.responseText)
7808                 } catch (e) {
7809                     Roo.log("Invalid data from server..");
7810                     Roo.log(edata);
7811                     return;
7812                 }
7813                 if (!rdata || !rdata.success) {
7814                     Roo.log(rdata);
7815                     Roo.MessageBox.alert(Roo.encode(rdata));
7816                     return;
7817                 }
7818                 var data = rdata.data;
7819                 
7820                 if (this.uploadComplete) {
7821                    Roo.MessageBox.hide();
7822                    return;
7823                 }
7824                    
7825                 if (data){
7826                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7827                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7828                     );
7829                 }
7830                 this.uploadProgress.defer(2000,this);
7831             },
7832        
7833             failure: function(data) {
7834                 Roo.log('progress url failed ');
7835                 Roo.log(data);
7836             },
7837             scope : this
7838         });
7839            
7840     },
7841     
7842     
7843     run : function()
7844     {
7845         // run get Values on the form, so it syncs any secondary forms.
7846         this.form.getValues();
7847         
7848         var o = this.options;
7849         var method = this.getMethod();
7850         var isPost = method == 'POST';
7851         if(o.clientValidation === false || this.form.isValid()){
7852             
7853             if (this.form.progressUrl) {
7854                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7855                     (new Date() * 1) + '' + Math.random());
7856                     
7857             } 
7858             
7859             
7860             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7861                 form:this.form.el.dom,
7862                 url:this.getUrl(!isPost),
7863                 method: method,
7864                 params:isPost ? this.getParams() : null,
7865                 isUpload: this.form.fileUpload
7866             }));
7867             
7868             this.uploadProgress();
7869
7870         }else if (o.clientValidation !== false){ // client validation failed
7871             this.failureType = Roo.form.Action.CLIENT_INVALID;
7872             this.form.afterAction(this, false);
7873         }
7874     },
7875
7876     success : function(response)
7877     {
7878         this.uploadComplete= true;
7879         if (this.haveProgress) {
7880             Roo.MessageBox.hide();
7881         }
7882         
7883         
7884         var result = this.processResponse(response);
7885         if(result === true || result.success){
7886             this.form.afterAction(this, true);
7887             return;
7888         }
7889         if(result.errors){
7890             this.form.markInvalid(result.errors);
7891             this.failureType = Roo.form.Action.SERVER_INVALID;
7892         }
7893         this.form.afterAction(this, false);
7894     },
7895     failure : function(response)
7896     {
7897         this.uploadComplete= true;
7898         if (this.haveProgress) {
7899             Roo.MessageBox.hide();
7900         }
7901         
7902         this.response = response;
7903         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7904         this.form.afterAction(this, false);
7905     },
7906     
7907     handleResponse : function(response){
7908         if(this.form.errorReader){
7909             var rs = this.form.errorReader.read(response);
7910             var errors = [];
7911             if(rs.records){
7912                 for(var i = 0, len = rs.records.length; i < len; i++) {
7913                     var r = rs.records[i];
7914                     errors[i] = r.data;
7915                 }
7916             }
7917             if(errors.length < 1){
7918                 errors = null;
7919             }
7920             return {
7921                 success : rs.success,
7922                 errors : errors
7923             };
7924         }
7925         var ret = false;
7926         try {
7927             ret = Roo.decode(response.responseText);
7928         } catch (e) {
7929             ret = {
7930                 success: false,
7931                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7932                 errors : []
7933             };
7934         }
7935         return ret;
7936         
7937     }
7938 });
7939
7940
7941 Roo.form.Action.Load = function(form, options){
7942     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7943     this.reader = this.form.reader;
7944 };
7945
7946 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7947     type : 'load',
7948
7949     run : function(){
7950         
7951         Roo.Ajax.request(Roo.apply(
7952                 this.createCallback(), {
7953                     method:this.getMethod(),
7954                     url:this.getUrl(false),
7955                     params:this.getParams()
7956         }));
7957     },
7958
7959     success : function(response){
7960         
7961         var result = this.processResponse(response);
7962         if(result === true || !result.success || !result.data){
7963             this.failureType = Roo.form.Action.LOAD_FAILURE;
7964             this.form.afterAction(this, false);
7965             return;
7966         }
7967         this.form.clearInvalid();
7968         this.form.setValues(result.data);
7969         this.form.afterAction(this, true);
7970     },
7971
7972     handleResponse : function(response){
7973         if(this.form.reader){
7974             var rs = this.form.reader.read(response);
7975             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7976             return {
7977                 success : rs.success,
7978                 data : data
7979             };
7980         }
7981         return Roo.decode(response.responseText);
7982     }
7983 });
7984
7985 Roo.form.Action.ACTION_TYPES = {
7986     'load' : Roo.form.Action.Load,
7987     'submit' : Roo.form.Action.Submit
7988 };/*
7989  * - LGPL
7990  *
7991  * form
7992  *
7993  */
7994
7995 /**
7996  * @class Roo.bootstrap.Form
7997  * @extends Roo.bootstrap.Component
7998  * Bootstrap Form class
7999  * @cfg {String} method  GET | POST (default POST)
8000  * @cfg {String} labelAlign top | left (default top)
8001  * @cfg {String} align left  | right - for navbars
8002  * @cfg {Boolean} loadMask load mask when submit (default true)
8003
8004  *
8005  * @constructor
8006  * Create a new Form
8007  * @param {Object} config The config object
8008  */
8009
8010
8011 Roo.bootstrap.Form = function(config){
8012     
8013     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8014     
8015     Roo.bootstrap.Form.popover.apply();
8016     
8017     this.addEvents({
8018         /**
8019          * @event clientvalidation
8020          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8021          * @param {Form} this
8022          * @param {Boolean} valid true if the form has passed client-side validation
8023          */
8024         clientvalidation: true,
8025         /**
8026          * @event beforeaction
8027          * Fires before any action is performed. Return false to cancel the action.
8028          * @param {Form} this
8029          * @param {Action} action The action to be performed
8030          */
8031         beforeaction: true,
8032         /**
8033          * @event actionfailed
8034          * Fires when an action fails.
8035          * @param {Form} this
8036          * @param {Action} action The action that failed
8037          */
8038         actionfailed : true,
8039         /**
8040          * @event actioncomplete
8041          * Fires when an action is completed.
8042          * @param {Form} this
8043          * @param {Action} action The action that completed
8044          */
8045         actioncomplete : true
8046     });
8047 };
8048
8049 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8050
8051      /**
8052      * @cfg {String} method
8053      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8054      */
8055     method : 'POST',
8056     /**
8057      * @cfg {String} url
8058      * The URL to use for form actions if one isn't supplied in the action options.
8059      */
8060     /**
8061      * @cfg {Boolean} fileUpload
8062      * Set to true if this form is a file upload.
8063      */
8064
8065     /**
8066      * @cfg {Object} baseParams
8067      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8068      */
8069
8070     /**
8071      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8072      */
8073     timeout: 30,
8074     /**
8075      * @cfg {Sting} align (left|right) for navbar forms
8076      */
8077     align : 'left',
8078
8079     // private
8080     activeAction : null,
8081
8082     /**
8083      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8084      * element by passing it or its id or mask the form itself by passing in true.
8085      * @type Mixed
8086      */
8087     waitMsgTarget : false,
8088
8089     loadMask : true,
8090     
8091     /**
8092      * @cfg {Boolean} errorMask (true|false) default false
8093      */
8094     errorMask : false,
8095     
8096     /**
8097      * @cfg {Number} maskOffset Default 100
8098      */
8099     maskOffset : 100,
8100     
8101     /**
8102      * @cfg {Boolean} maskBody
8103      */
8104     maskBody : false,
8105
8106     getAutoCreate : function(){
8107
8108         var cfg = {
8109             tag: 'form',
8110             method : this.method || 'POST',
8111             id : this.id || Roo.id(),
8112             cls : ''
8113         };
8114         if (this.parent().xtype.match(/^Nav/)) {
8115             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8116
8117         }
8118
8119         if (this.labelAlign == 'left' ) {
8120             cfg.cls += ' form-horizontal';
8121         }
8122
8123
8124         return cfg;
8125     },
8126     initEvents : function()
8127     {
8128         this.el.on('submit', this.onSubmit, this);
8129         // this was added as random key presses on the form where triggering form submit.
8130         this.el.on('keypress', function(e) {
8131             if (e.getCharCode() != 13) {
8132                 return true;
8133             }
8134             // we might need to allow it for textareas.. and some other items.
8135             // check e.getTarget().
8136
8137             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8138                 return true;
8139             }
8140
8141             Roo.log("keypress blocked");
8142
8143             e.preventDefault();
8144             return false;
8145         });
8146         
8147     },
8148     // private
8149     onSubmit : function(e){
8150         e.stopEvent();
8151     },
8152
8153      /**
8154      * Returns true if client-side validation on the form is successful.
8155      * @return Boolean
8156      */
8157     isValid : function(){
8158         var items = this.getItems();
8159         var valid = true;
8160         var target = false;
8161         
8162         items.each(function(f){
8163             
8164             if(f.validate()){
8165                 return;
8166             }
8167             
8168             Roo.log('invalid field: ' + f.name);
8169             
8170             valid = false;
8171
8172             if(!target && f.el.isVisible(true)){
8173                 target = f;
8174             }
8175            
8176         });
8177         
8178         if(this.errorMask && !valid){
8179             Roo.bootstrap.Form.popover.mask(this, target);
8180         }
8181         
8182         return valid;
8183     },
8184     
8185     /**
8186      * Returns true if any fields in this form have changed since their original load.
8187      * @return Boolean
8188      */
8189     isDirty : function(){
8190         var dirty = false;
8191         var items = this.getItems();
8192         items.each(function(f){
8193            if(f.isDirty()){
8194                dirty = true;
8195                return false;
8196            }
8197            return true;
8198         });
8199         return dirty;
8200     },
8201      /**
8202      * Performs a predefined action (submit or load) or custom actions you define on this form.
8203      * @param {String} actionName The name of the action type
8204      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8205      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8206      * accept other config options):
8207      * <pre>
8208 Property          Type             Description
8209 ----------------  ---------------  ----------------------------------------------------------------------------------
8210 url               String           The url for the action (defaults to the form's url)
8211 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8212 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8213 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8214                                    validate the form on the client (defaults to false)
8215      * </pre>
8216      * @return {BasicForm} this
8217      */
8218     doAction : function(action, options){
8219         if(typeof action == 'string'){
8220             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8221         }
8222         if(this.fireEvent('beforeaction', this, action) !== false){
8223             this.beforeAction(action);
8224             action.run.defer(100, action);
8225         }
8226         return this;
8227     },
8228
8229     // private
8230     beforeAction : function(action){
8231         var o = action.options;
8232         
8233         if(this.loadMask){
8234             
8235             if(this.maskBody){
8236                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8237             } else {
8238                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8239             }
8240         }
8241         // not really supported yet.. ??
8242
8243         //if(this.waitMsgTarget === true){
8244         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8245         //}else if(this.waitMsgTarget){
8246         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8247         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8248         //}else {
8249         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8250        // }
8251
8252     },
8253
8254     // private
8255     afterAction : function(action, success){
8256         this.activeAction = null;
8257         var o = action.options;
8258
8259         if(this.loadMask){
8260             
8261             if(this.maskBody){
8262                 Roo.get(document.body).unmask();
8263             } else {
8264                 this.el.unmask();
8265             }
8266         }
8267         
8268         //if(this.waitMsgTarget === true){
8269 //            this.el.unmask();
8270         //}else if(this.waitMsgTarget){
8271         //    this.waitMsgTarget.unmask();
8272         //}else{
8273         //    Roo.MessageBox.updateProgress(1);
8274         //    Roo.MessageBox.hide();
8275        // }
8276         //
8277         if(success){
8278             if(o.reset){
8279                 this.reset();
8280             }
8281             Roo.callback(o.success, o.scope, [this, action]);
8282             this.fireEvent('actioncomplete', this, action);
8283
8284         }else{
8285
8286             // failure condition..
8287             // we have a scenario where updates need confirming.
8288             // eg. if a locking scenario exists..
8289             // we look for { errors : { needs_confirm : true }} in the response.
8290             if (
8291                 (typeof(action.result) != 'undefined')  &&
8292                 (typeof(action.result.errors) != 'undefined')  &&
8293                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8294            ){
8295                 var _t = this;
8296                 Roo.log("not supported yet");
8297                  /*
8298
8299                 Roo.MessageBox.confirm(
8300                     "Change requires confirmation",
8301                     action.result.errorMsg,
8302                     function(r) {
8303                         if (r != 'yes') {
8304                             return;
8305                         }
8306                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8307                     }
8308
8309                 );
8310                 */
8311
8312
8313                 return;
8314             }
8315
8316             Roo.callback(o.failure, o.scope, [this, action]);
8317             // show an error message if no failed handler is set..
8318             if (!this.hasListener('actionfailed')) {
8319                 Roo.log("need to add dialog support");
8320                 /*
8321                 Roo.MessageBox.alert("Error",
8322                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8323                         action.result.errorMsg :
8324                         "Saving Failed, please check your entries or try again"
8325                 );
8326                 */
8327             }
8328
8329             this.fireEvent('actionfailed', this, action);
8330         }
8331
8332     },
8333     /**
8334      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8335      * @param {String} id The value to search for
8336      * @return Field
8337      */
8338     findField : function(id){
8339         var items = this.getItems();
8340         var field = items.get(id);
8341         if(!field){
8342              items.each(function(f){
8343                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8344                     field = f;
8345                     return false;
8346                 }
8347                 return true;
8348             });
8349         }
8350         return field || null;
8351     },
8352      /**
8353      * Mark fields in this form invalid in bulk.
8354      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8355      * @return {BasicForm} this
8356      */
8357     markInvalid : function(errors){
8358         if(errors instanceof Array){
8359             for(var i = 0, len = errors.length; i < len; i++){
8360                 var fieldError = errors[i];
8361                 var f = this.findField(fieldError.id);
8362                 if(f){
8363                     f.markInvalid(fieldError.msg);
8364                 }
8365             }
8366         }else{
8367             var field, id;
8368             for(id in errors){
8369                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8370                     field.markInvalid(errors[id]);
8371                 }
8372             }
8373         }
8374         //Roo.each(this.childForms || [], function (f) {
8375         //    f.markInvalid(errors);
8376         //});
8377
8378         return this;
8379     },
8380
8381     /**
8382      * Set values for fields in this form in bulk.
8383      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8384      * @return {BasicForm} this
8385      */
8386     setValues : function(values){
8387         if(values instanceof Array){ // array of objects
8388             for(var i = 0, len = values.length; i < len; i++){
8389                 var v = values[i];
8390                 var f = this.findField(v.id);
8391                 if(f){
8392                     f.setValue(v.value);
8393                     if(this.trackResetOnLoad){
8394                         f.originalValue = f.getValue();
8395                     }
8396                 }
8397             }
8398         }else{ // object hash
8399             var field, id;
8400             for(id in values){
8401                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8402
8403                     if (field.setFromData &&
8404                         field.valueField &&
8405                         field.displayField &&
8406                         // combos' with local stores can
8407                         // be queried via setValue()
8408                         // to set their value..
8409                         (field.store && !field.store.isLocal)
8410                         ) {
8411                         // it's a combo
8412                         var sd = { };
8413                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8414                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8415                         field.setFromData(sd);
8416
8417                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8418                         
8419                         field.setFromData(values);
8420                         
8421                     } else {
8422                         field.setValue(values[id]);
8423                     }
8424
8425
8426                     if(this.trackResetOnLoad){
8427                         field.originalValue = field.getValue();
8428                     }
8429                 }
8430             }
8431         }
8432
8433         //Roo.each(this.childForms || [], function (f) {
8434         //    f.setValues(values);
8435         //});
8436
8437         return this;
8438     },
8439
8440     /**
8441      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8442      * they are returned as an array.
8443      * @param {Boolean} asString
8444      * @return {Object}
8445      */
8446     getValues : function(asString){
8447         //if (this.childForms) {
8448             // copy values from the child forms
8449         //    Roo.each(this.childForms, function (f) {
8450         //        this.setValues(f.getValues());
8451         //    }, this);
8452         //}
8453
8454
8455
8456         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8457         if(asString === true){
8458             return fs;
8459         }
8460         return Roo.urlDecode(fs);
8461     },
8462
8463     /**
8464      * Returns the fields in this form as an object with key/value pairs.
8465      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8466      * @return {Object}
8467      */
8468     getFieldValues : function(with_hidden)
8469     {
8470         var items = this.getItems();
8471         var ret = {};
8472         items.each(function(f){
8473             
8474             if (!f.getName()) {
8475                 return;
8476             }
8477             
8478             var v = f.getValue();
8479             
8480             if (f.inputType =='radio') {
8481                 if (typeof(ret[f.getName()]) == 'undefined') {
8482                     ret[f.getName()] = ''; // empty..
8483                 }
8484
8485                 if (!f.el.dom.checked) {
8486                     return;
8487
8488                 }
8489                 v = f.el.dom.value;
8490
8491             }
8492             
8493             if(f.xtype == 'MoneyField'){
8494                 ret[f.currencyName] = f.getCurrency();
8495             }
8496
8497             // not sure if this supported any more..
8498             if ((typeof(v) == 'object') && f.getRawValue) {
8499                 v = f.getRawValue() ; // dates..
8500             }
8501             // combo boxes where name != hiddenName...
8502             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8503                 ret[f.name] = f.getRawValue();
8504             }
8505             ret[f.getName()] = v;
8506         });
8507
8508         return ret;
8509     },
8510
8511     /**
8512      * Clears all invalid messages in this form.
8513      * @return {BasicForm} this
8514      */
8515     clearInvalid : function(){
8516         var items = this.getItems();
8517
8518         items.each(function(f){
8519            f.clearInvalid();
8520         });
8521
8522         return this;
8523     },
8524
8525     /**
8526      * Resets this form.
8527      * @return {BasicForm} this
8528      */
8529     reset : function(){
8530         var items = this.getItems();
8531         items.each(function(f){
8532             f.reset();
8533         });
8534
8535         Roo.each(this.childForms || [], function (f) {
8536             f.reset();
8537         });
8538
8539
8540         return this;
8541     },
8542     
8543     getItems : function()
8544     {
8545         var r=new Roo.util.MixedCollection(false, function(o){
8546             return o.id || (o.id = Roo.id());
8547         });
8548         var iter = function(el) {
8549             if (el.inputEl) {
8550                 r.add(el);
8551             }
8552             if (!el.items) {
8553                 return;
8554             }
8555             Roo.each(el.items,function(e) {
8556                 iter(e);
8557             });
8558         };
8559
8560         iter(this);
8561         return r;
8562     },
8563     
8564     hideFields : function(items)
8565     {
8566         Roo.each(items, function(i){
8567             
8568             var f = this.findField(i);
8569             
8570             if(!f){
8571                 return;
8572             }
8573             
8574             f.hide();
8575             
8576         }, this);
8577     },
8578     
8579     showFields : function(items)
8580     {
8581         Roo.each(items, function(i){
8582             
8583             var f = this.findField(i);
8584             
8585             if(!f){
8586                 return;
8587             }
8588             
8589             f.show();
8590             
8591         }, this);
8592     }
8593
8594 });
8595
8596 Roo.apply(Roo.bootstrap.Form, {
8597     
8598     popover : {
8599         
8600         padding : 5,
8601         
8602         isApplied : false,
8603         
8604         isMasked : false,
8605         
8606         form : false,
8607         
8608         target : false,
8609         
8610         toolTip : false,
8611         
8612         intervalID : false,
8613         
8614         maskEl : false,
8615         
8616         apply : function()
8617         {
8618             if(this.isApplied){
8619                 return;
8620             }
8621             
8622             this.maskEl = {
8623                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8624                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8625                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8626                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8627             };
8628             
8629             this.maskEl.top.enableDisplayMode("block");
8630             this.maskEl.left.enableDisplayMode("block");
8631             this.maskEl.bottom.enableDisplayMode("block");
8632             this.maskEl.right.enableDisplayMode("block");
8633             
8634             this.toolTip = new Roo.bootstrap.Tooltip({
8635                 cls : 'roo-form-error-popover',
8636                 alignment : {
8637                     'left' : ['r-l', [-2,0], 'right'],
8638                     'right' : ['l-r', [2,0], 'left'],
8639                     'bottom' : ['tl-bl', [0,2], 'top'],
8640                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8641                 }
8642             });
8643             
8644             this.toolTip.render(Roo.get(document.body));
8645
8646             this.toolTip.el.enableDisplayMode("block");
8647             
8648             Roo.get(document.body).on('click', function(){
8649                 this.unmask();
8650             }, this);
8651             
8652             Roo.get(document.body).on('touchstart', function(){
8653                 this.unmask();
8654             }, this);
8655             
8656             this.isApplied = true
8657         },
8658         
8659         mask : function(form, target)
8660         {
8661             this.form = form;
8662             
8663             this.target = target;
8664             
8665             if(!this.form.errorMask || !target.el){
8666                 return;
8667             }
8668             
8669             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8670             
8671             Roo.log(scrollable);
8672             
8673             var ot = this.target.el.calcOffsetsTo(scrollable);
8674             
8675             var scrollTo = ot[1] - this.form.maskOffset;
8676             
8677             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8678             
8679             scrollable.scrollTo('top', scrollTo);
8680             
8681             var box = this.target.el.getBox();
8682             Roo.log(box);
8683             var zIndex = Roo.bootstrap.Modal.zIndex++;
8684
8685             
8686             this.maskEl.top.setStyle('position', 'absolute');
8687             this.maskEl.top.setStyle('z-index', zIndex);
8688             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8689             this.maskEl.top.setLeft(0);
8690             this.maskEl.top.setTop(0);
8691             this.maskEl.top.show();
8692             
8693             this.maskEl.left.setStyle('position', 'absolute');
8694             this.maskEl.left.setStyle('z-index', zIndex);
8695             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8696             this.maskEl.left.setLeft(0);
8697             this.maskEl.left.setTop(box.y - this.padding);
8698             this.maskEl.left.show();
8699
8700             this.maskEl.bottom.setStyle('position', 'absolute');
8701             this.maskEl.bottom.setStyle('z-index', zIndex);
8702             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8703             this.maskEl.bottom.setLeft(0);
8704             this.maskEl.bottom.setTop(box.bottom + this.padding);
8705             this.maskEl.bottom.show();
8706
8707             this.maskEl.right.setStyle('position', 'absolute');
8708             this.maskEl.right.setStyle('z-index', zIndex);
8709             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8710             this.maskEl.right.setLeft(box.right + this.padding);
8711             this.maskEl.right.setTop(box.y - this.padding);
8712             this.maskEl.right.show();
8713
8714             this.toolTip.bindEl = this.target.el;
8715
8716             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8717
8718             var tip = this.target.blankText;
8719
8720             if(this.target.getValue() !== '' ) {
8721                 
8722                 if (this.target.invalidText.length) {
8723                     tip = this.target.invalidText;
8724                 } else if (this.target.regexText.length){
8725                     tip = this.target.regexText;
8726                 }
8727             }
8728
8729             this.toolTip.show(tip);
8730
8731             this.intervalID = window.setInterval(function() {
8732                 Roo.bootstrap.Form.popover.unmask();
8733             }, 10000);
8734
8735             window.onwheel = function(){ return false;};
8736             
8737             (function(){ this.isMasked = true; }).defer(500, this);
8738             
8739         },
8740         
8741         unmask : function()
8742         {
8743             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8744                 return;
8745             }
8746             
8747             this.maskEl.top.setStyle('position', 'absolute');
8748             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8749             this.maskEl.top.hide();
8750
8751             this.maskEl.left.setStyle('position', 'absolute');
8752             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8753             this.maskEl.left.hide();
8754
8755             this.maskEl.bottom.setStyle('position', 'absolute');
8756             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.bottom.hide();
8758
8759             this.maskEl.right.setStyle('position', 'absolute');
8760             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8761             this.maskEl.right.hide();
8762             
8763             this.toolTip.hide();
8764             
8765             this.toolTip.el.hide();
8766             
8767             window.onwheel = function(){ return true;};
8768             
8769             if(this.intervalID){
8770                 window.clearInterval(this.intervalID);
8771                 this.intervalID = false;
8772             }
8773             
8774             this.isMasked = false;
8775             
8776         }
8777         
8778     }
8779     
8780 });
8781
8782 /*
8783  * Based on:
8784  * Ext JS Library 1.1.1
8785  * Copyright(c) 2006-2007, Ext JS, LLC.
8786  *
8787  * Originally Released Under LGPL - original licence link has changed is not relivant.
8788  *
8789  * Fork - LGPL
8790  * <script type="text/javascript">
8791  */
8792 /**
8793  * @class Roo.form.VTypes
8794  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8795  * @singleton
8796  */
8797 Roo.form.VTypes = function(){
8798     // closure these in so they are only created once.
8799     var alpha = /^[a-zA-Z_]+$/;
8800     var alphanum = /^[a-zA-Z0-9_]+$/;
8801     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8802     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8803
8804     // All these messages and functions are configurable
8805     return {
8806         /**
8807          * The function used to validate email addresses
8808          * @param {String} value The email address
8809          */
8810         'email' : function(v){
8811             return email.test(v);
8812         },
8813         /**
8814          * The error text to display when the email validation function returns false
8815          * @type String
8816          */
8817         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8818         /**
8819          * The keystroke filter mask to be applied on email input
8820          * @type RegExp
8821          */
8822         'emailMask' : /[a-z0-9_\.\-@]/i,
8823
8824         /**
8825          * The function used to validate URLs
8826          * @param {String} value The URL
8827          */
8828         'url' : function(v){
8829             return url.test(v);
8830         },
8831         /**
8832          * The error text to display when the url validation function returns false
8833          * @type String
8834          */
8835         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8836         
8837         /**
8838          * The function used to validate alpha values
8839          * @param {String} value The value
8840          */
8841         'alpha' : function(v){
8842             return alpha.test(v);
8843         },
8844         /**
8845          * The error text to display when the alpha validation function returns false
8846          * @type String
8847          */
8848         'alphaText' : 'This field should only contain letters and _',
8849         /**
8850          * The keystroke filter mask to be applied on alpha input
8851          * @type RegExp
8852          */
8853         'alphaMask' : /[a-z_]/i,
8854
8855         /**
8856          * The function used to validate alphanumeric values
8857          * @param {String} value The value
8858          */
8859         'alphanum' : function(v){
8860             return alphanum.test(v);
8861         },
8862         /**
8863          * The error text to display when the alphanumeric validation function returns false
8864          * @type String
8865          */
8866         'alphanumText' : 'This field should only contain letters, numbers and _',
8867         /**
8868          * The keystroke filter mask to be applied on alphanumeric input
8869          * @type RegExp
8870          */
8871         'alphanumMask' : /[a-z0-9_]/i
8872     };
8873 }();/*
8874  * - LGPL
8875  *
8876  * Input
8877  * 
8878  */
8879
8880 /**
8881  * @class Roo.bootstrap.Input
8882  * @extends Roo.bootstrap.Component
8883  * Bootstrap Input class
8884  * @cfg {Boolean} disabled is it disabled
8885  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8886  * @cfg {String} name name of the input
8887  * @cfg {string} fieldLabel - the label associated
8888  * @cfg {string} placeholder - placeholder to put in text.
8889  * @cfg {string}  before - input group add on before
8890  * @cfg {string} after - input group add on after
8891  * @cfg {string} size - (lg|sm) or leave empty..
8892  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8893  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8894  * @cfg {Number} md colspan out of 12 for computer-sized screens
8895  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8896  * @cfg {string} value default value of the input
8897  * @cfg {Number} labelWidth set the width of label 
8898  * @cfg {Number} labellg set the width of label (1-12)
8899  * @cfg {Number} labelmd set the width of label (1-12)
8900  * @cfg {Number} labelsm set the width of label (1-12)
8901  * @cfg {Number} labelxs set the width of label (1-12)
8902  * @cfg {String} labelAlign (top|left)
8903  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8904  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8905  * @cfg {String} indicatorpos (left|right) default left
8906  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8907  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8908
8909  * @cfg {String} align (left|center|right) Default left
8910  * @cfg {Boolean} forceFeedback (true|false) Default false
8911  * 
8912  * @constructor
8913  * Create a new Input
8914  * @param {Object} config The config object
8915  */
8916
8917 Roo.bootstrap.Input = function(config){
8918     
8919     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8920     
8921     this.addEvents({
8922         /**
8923          * @event focus
8924          * Fires when this field receives input focus.
8925          * @param {Roo.form.Field} this
8926          */
8927         focus : true,
8928         /**
8929          * @event blur
8930          * Fires when this field loses input focus.
8931          * @param {Roo.form.Field} this
8932          */
8933         blur : true,
8934         /**
8935          * @event specialkey
8936          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8937          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8938          * @param {Roo.form.Field} this
8939          * @param {Roo.EventObject} e The event object
8940          */
8941         specialkey : true,
8942         /**
8943          * @event change
8944          * Fires just before the field blurs if the field value has changed.
8945          * @param {Roo.form.Field} this
8946          * @param {Mixed} newValue The new value
8947          * @param {Mixed} oldValue The original value
8948          */
8949         change : true,
8950         /**
8951          * @event invalid
8952          * Fires after the field has been marked as invalid.
8953          * @param {Roo.form.Field} this
8954          * @param {String} msg The validation message
8955          */
8956         invalid : true,
8957         /**
8958          * @event valid
8959          * Fires after the field has been validated with no errors.
8960          * @param {Roo.form.Field} this
8961          */
8962         valid : true,
8963          /**
8964          * @event keyup
8965          * Fires after the key up
8966          * @param {Roo.form.Field} this
8967          * @param {Roo.EventObject}  e The event Object
8968          */
8969         keyup : true
8970     });
8971 };
8972
8973 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8974      /**
8975      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8976       automatic validation (defaults to "keyup").
8977      */
8978     validationEvent : "keyup",
8979      /**
8980      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8981      */
8982     validateOnBlur : true,
8983     /**
8984      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8985      */
8986     validationDelay : 250,
8987      /**
8988      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8989      */
8990     focusClass : "x-form-focus",  // not needed???
8991     
8992        
8993     /**
8994      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
8995      */
8996     invalidClass : "has-warning",
8997     
8998     /**
8999      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9000      */
9001     validClass : "has-success",
9002     
9003     /**
9004      * @cfg {Boolean} hasFeedback (true|false) default true
9005      */
9006     hasFeedback : true,
9007     
9008     /**
9009      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9010      */
9011     invalidFeedbackClass : "glyphicon-warning-sign",
9012     
9013     /**
9014      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9015      */
9016     validFeedbackClass : "glyphicon-ok",
9017     
9018     /**
9019      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9020      */
9021     selectOnFocus : false,
9022     
9023      /**
9024      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9025      */
9026     maskRe : null,
9027        /**
9028      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9029      */
9030     vtype : null,
9031     
9032       /**
9033      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9034      */
9035     disableKeyFilter : false,
9036     
9037        /**
9038      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9039      */
9040     disabled : false,
9041      /**
9042      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9043      */
9044     allowBlank : true,
9045     /**
9046      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9047      */
9048     blankText : "Please complete this mandatory field",
9049     
9050      /**
9051      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9052      */
9053     minLength : 0,
9054     /**
9055      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9056      */
9057     maxLength : Number.MAX_VALUE,
9058     /**
9059      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9060      */
9061     minLengthText : "The minimum length for this field is {0}",
9062     /**
9063      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9064      */
9065     maxLengthText : "The maximum length for this field is {0}",
9066   
9067     
9068     /**
9069      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9070      * If available, this function will be called only after the basic validators all return true, and will be passed the
9071      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9072      */
9073     validator : null,
9074     /**
9075      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9076      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9077      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9078      */
9079     regex : null,
9080     /**
9081      * @cfg {String} regexText -- Depricated - use Invalid Text
9082      */
9083     regexText : "",
9084     
9085     /**
9086      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9087      */
9088     invalidText : "",
9089     
9090     
9091     
9092     autocomplete: false,
9093     
9094     
9095     fieldLabel : '',
9096     inputType : 'text',
9097     
9098     name : false,
9099     placeholder: false,
9100     before : false,
9101     after : false,
9102     size : false,
9103     hasFocus : false,
9104     preventMark: false,
9105     isFormField : true,
9106     value : '',
9107     labelWidth : 2,
9108     labelAlign : false,
9109     readOnly : false,
9110     align : false,
9111     formatedValue : false,
9112     forceFeedback : false,
9113     
9114     indicatorpos : 'left',
9115     
9116     labellg : 0,
9117     labelmd : 0,
9118     labelsm : 0,
9119     labelxs : 0,
9120     
9121     capture : '',
9122     accept : '',
9123     
9124     parentLabelAlign : function()
9125     {
9126         var parent = this;
9127         while (parent.parent()) {
9128             parent = parent.parent();
9129             if (typeof(parent.labelAlign) !='undefined') {
9130                 return parent.labelAlign;
9131             }
9132         }
9133         return 'left';
9134         
9135     },
9136     
9137     getAutoCreate : function()
9138     {
9139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9140         
9141         var id = Roo.id();
9142         
9143         var cfg = {};
9144         
9145         if(this.inputType != 'hidden'){
9146             cfg.cls = 'form-group' //input-group
9147         }
9148         
9149         var input =  {
9150             tag: 'input',
9151             id : id,
9152             type : this.inputType,
9153             value : this.value,
9154             cls : 'form-control',
9155             placeholder : this.placeholder || '',
9156             autocomplete : this.autocomplete || 'new-password'
9157         };
9158         
9159         if(this.capture.length){
9160             input.capture = this.capture;
9161         }
9162         
9163         if(this.accept.length){
9164             input.accept = this.accept + "/*";
9165         }
9166         
9167         if(this.align){
9168             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9169         }
9170         
9171         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9172             input.maxLength = this.maxLength;
9173         }
9174         
9175         if (this.disabled) {
9176             input.disabled=true;
9177         }
9178         
9179         if (this.readOnly) {
9180             input.readonly=true;
9181         }
9182         
9183         if (this.name) {
9184             input.name = this.name;
9185         }
9186         
9187         if (this.size) {
9188             input.cls += ' input-' + this.size;
9189         }
9190         
9191         var settings=this;
9192         ['xs','sm','md','lg'].map(function(size){
9193             if (settings[size]) {
9194                 cfg.cls += ' col-' + size + '-' + settings[size];
9195             }
9196         });
9197         
9198         var inputblock = input;
9199         
9200         var feedback = {
9201             tag: 'span',
9202             cls: 'glyphicon form-control-feedback'
9203         };
9204             
9205         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9206             
9207             inputblock = {
9208                 cls : 'has-feedback',
9209                 cn :  [
9210                     input,
9211                     feedback
9212                 ] 
9213             };  
9214         }
9215         
9216         if (this.before || this.after) {
9217             
9218             inputblock = {
9219                 cls : 'input-group',
9220                 cn :  [] 
9221             };
9222             
9223             if (this.before && typeof(this.before) == 'string') {
9224                 
9225                 inputblock.cn.push({
9226                     tag :'span',
9227                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9228                     html : this.before
9229                 });
9230             }
9231             if (this.before && typeof(this.before) == 'object') {
9232                 this.before = Roo.factory(this.before);
9233                 
9234                 inputblock.cn.push({
9235                     tag :'span',
9236                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9237                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9238                 });
9239             }
9240             
9241             inputblock.cn.push(input);
9242             
9243             if (this.after && typeof(this.after) == 'string') {
9244                 inputblock.cn.push({
9245                     tag :'span',
9246                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9247                     html : this.after
9248                 });
9249             }
9250             if (this.after && typeof(this.after) == 'object') {
9251                 this.after = Roo.factory(this.after);
9252                 
9253                 inputblock.cn.push({
9254                     tag :'span',
9255                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9256                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9257                 });
9258             }
9259             
9260             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9261                 inputblock.cls += ' has-feedback';
9262                 inputblock.cn.push(feedback);
9263             }
9264         };
9265         var indicator = {
9266             tag : 'i',
9267             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9268             tooltip : 'This field is required'
9269         };
9270         if (Roo.bootstrap.version == 4) {
9271             indicator = {
9272                 tag : 'i',
9273                 style : 'display-none'
9274             };
9275         }
9276         if (align ==='left' && this.fieldLabel.length) {
9277             
9278             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9279             
9280             cfg.cn = [
9281                 indicator,
9282                 {
9283                     tag: 'label',
9284                     'for' :  id,
9285                     cls : 'control-label col-form-label',
9286                     html : this.fieldLabel
9287
9288                 },
9289                 {
9290                     cls : "", 
9291                     cn: [
9292                         inputblock
9293                     ]
9294                 }
9295             ];
9296             
9297             var labelCfg = cfg.cn[1];
9298             var contentCfg = cfg.cn[2];
9299             
9300             if(this.indicatorpos == 'right'){
9301                 cfg.cn = [
9302                     {
9303                         tag: 'label',
9304                         'for' :  id,
9305                         cls : 'control-label col-form-label',
9306                         cn : [
9307                             {
9308                                 tag : 'span',
9309                                 html : this.fieldLabel
9310                             },
9311                             indicator
9312                         ]
9313                     },
9314                     {
9315                         cls : "",
9316                         cn: [
9317                             inputblock
9318                         ]
9319                     }
9320
9321                 ];
9322                 
9323                 labelCfg = cfg.cn[0];
9324                 contentCfg = cfg.cn[1];
9325             
9326             }
9327             
9328             if(this.labelWidth > 12){
9329                 labelCfg.style = "width: " + this.labelWidth + 'px';
9330             }
9331             
9332             if(this.labelWidth < 13 && this.labelmd == 0){
9333                 this.labelmd = this.labelWidth;
9334             }
9335             
9336             if(this.labellg > 0){
9337                 labelCfg.cls += ' col-lg-' + this.labellg;
9338                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9339             }
9340             
9341             if(this.labelmd > 0){
9342                 labelCfg.cls += ' col-md-' + this.labelmd;
9343                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9344             }
9345             
9346             if(this.labelsm > 0){
9347                 labelCfg.cls += ' col-sm-' + this.labelsm;
9348                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9349             }
9350             
9351             if(this.labelxs > 0){
9352                 labelCfg.cls += ' col-xs-' + this.labelxs;
9353                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9354             }
9355             
9356             
9357         } else if ( this.fieldLabel.length) {
9358                 
9359             cfg.cn = [
9360                 {
9361                     tag : 'i',
9362                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9363                     tooltip : 'This field is required'
9364                 },
9365                 {
9366                     tag: 'label',
9367                    //cls : 'input-group-addon',
9368                     html : this.fieldLabel
9369
9370                 },
9371
9372                inputblock
9373
9374            ];
9375            
9376            if(this.indicatorpos == 'right'){
9377                 
9378                 cfg.cn = [
9379                     {
9380                         tag: 'label',
9381                        //cls : 'input-group-addon',
9382                         html : this.fieldLabel
9383
9384                     },
9385                     {
9386                         tag : 'i',
9387                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9388                         tooltip : 'This field is required'
9389                     },
9390
9391                    inputblock
9392
9393                ];
9394
9395             }
9396
9397         } else {
9398             
9399             cfg.cn = [
9400
9401                     inputblock
9402
9403             ];
9404                 
9405                 
9406         };
9407         
9408         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9409            cfg.cls += ' navbar-form';
9410         }
9411         
9412         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9413             // on BS4 we do this only if not form 
9414             cfg.cls += ' navbar-form';
9415             cfg.tag = 'li';
9416         }
9417         
9418         return cfg;
9419         
9420     },
9421     /**
9422      * return the real input element.
9423      */
9424     inputEl: function ()
9425     {
9426         return this.el.select('input.form-control',true).first();
9427     },
9428     
9429     tooltipEl : function()
9430     {
9431         return this.inputEl();
9432     },
9433     
9434     indicatorEl : function()
9435     {
9436         if (Roo.bootstrap.version == 4) {
9437             return false; // not enabled in v4 yet.
9438         }
9439         
9440         var indicator = this.el.select('i.roo-required-indicator',true).first();
9441         
9442         if(!indicator){
9443             return false;
9444         }
9445         
9446         return indicator;
9447         
9448     },
9449     
9450     setDisabled : function(v)
9451     {
9452         var i  = this.inputEl().dom;
9453         if (!v) {
9454             i.removeAttribute('disabled');
9455             return;
9456             
9457         }
9458         i.setAttribute('disabled','true');
9459     },
9460     initEvents : function()
9461     {
9462           
9463         this.inputEl().on("keydown" , this.fireKey,  this);
9464         this.inputEl().on("focus", this.onFocus,  this);
9465         this.inputEl().on("blur", this.onBlur,  this);
9466         
9467         this.inputEl().relayEvent('keyup', this);
9468         
9469         this.indicator = this.indicatorEl();
9470         
9471         if(this.indicator){
9472             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9473         }
9474  
9475         // reference to original value for reset
9476         this.originalValue = this.getValue();
9477         //Roo.form.TextField.superclass.initEvents.call(this);
9478         if(this.validationEvent == 'keyup'){
9479             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9480             this.inputEl().on('keyup', this.filterValidation, this);
9481         }
9482         else if(this.validationEvent !== false){
9483             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9484         }
9485         
9486         if(this.selectOnFocus){
9487             this.on("focus", this.preFocus, this);
9488             
9489         }
9490         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9491             this.inputEl().on("keypress", this.filterKeys, this);
9492         } else {
9493             this.inputEl().relayEvent('keypress', this);
9494         }
9495        /* if(this.grow){
9496             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9497             this.el.on("click", this.autoSize,  this);
9498         }
9499         */
9500         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9501             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9502         }
9503         
9504         if (typeof(this.before) == 'object') {
9505             this.before.render(this.el.select('.roo-input-before',true).first());
9506         }
9507         if (typeof(this.after) == 'object') {
9508             this.after.render(this.el.select('.roo-input-after',true).first());
9509         }
9510         
9511         this.inputEl().on('change', this.onChange, this);
9512         
9513     },
9514     filterValidation : function(e){
9515         if(!e.isNavKeyPress()){
9516             this.validationTask.delay(this.validationDelay);
9517         }
9518     },
9519      /**
9520      * Validates the field value
9521      * @return {Boolean} True if the value is valid, else false
9522      */
9523     validate : function(){
9524         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9525         if(this.disabled || this.validateValue(this.getRawValue())){
9526             this.markValid();
9527             return true;
9528         }
9529         
9530         this.markInvalid();
9531         return false;
9532     },
9533     
9534     
9535     /**
9536      * Validates a value according to the field's validation rules and marks the field as invalid
9537      * if the validation fails
9538      * @param {Mixed} value The value to validate
9539      * @return {Boolean} True if the value is valid, else false
9540      */
9541     validateValue : function(value)
9542     {
9543         if(this.getVisibilityEl().hasClass('hidden')){
9544             return true;
9545         }
9546         
9547         if(value.length < 1)  { // if it's blank
9548             if(this.allowBlank){
9549                 return true;
9550             }
9551             return false;
9552         }
9553         
9554         if(value.length < this.minLength){
9555             return false;
9556         }
9557         if(value.length > this.maxLength){
9558             return false;
9559         }
9560         if(this.vtype){
9561             var vt = Roo.form.VTypes;
9562             if(!vt[this.vtype](value, this)){
9563                 return false;
9564             }
9565         }
9566         if(typeof this.validator == "function"){
9567             var msg = this.validator(value);
9568             if(msg !== true){
9569                 return false;
9570             }
9571             if (typeof(msg) == 'string') {
9572                 this.invalidText = msg;
9573             }
9574         }
9575         
9576         if(this.regex && !this.regex.test(value)){
9577             return false;
9578         }
9579         
9580         return true;
9581     },
9582     
9583      // private
9584     fireKey : function(e){
9585         //Roo.log('field ' + e.getKey());
9586         if(e.isNavKeyPress()){
9587             this.fireEvent("specialkey", this, e);
9588         }
9589     },
9590     focus : function (selectText){
9591         if(this.rendered){
9592             this.inputEl().focus();
9593             if(selectText === true){
9594                 this.inputEl().dom.select();
9595             }
9596         }
9597         return this;
9598     } ,
9599     
9600     onFocus : function(){
9601         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9602            // this.el.addClass(this.focusClass);
9603         }
9604         if(!this.hasFocus){
9605             this.hasFocus = true;
9606             this.startValue = this.getValue();
9607             this.fireEvent("focus", this);
9608         }
9609     },
9610     
9611     beforeBlur : Roo.emptyFn,
9612
9613     
9614     // private
9615     onBlur : function(){
9616         this.beforeBlur();
9617         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9618             //this.el.removeClass(this.focusClass);
9619         }
9620         this.hasFocus = false;
9621         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9622             this.validate();
9623         }
9624         var v = this.getValue();
9625         if(String(v) !== String(this.startValue)){
9626             this.fireEvent('change', this, v, this.startValue);
9627         }
9628         this.fireEvent("blur", this);
9629     },
9630     
9631     onChange : function(e)
9632     {
9633         var v = this.getValue();
9634         if(String(v) !== String(this.startValue)){
9635             this.fireEvent('change', this, v, this.startValue);
9636         }
9637         
9638     },
9639     
9640     /**
9641      * Resets the current field value to the originally loaded value and clears any validation messages
9642      */
9643     reset : function(){
9644         this.setValue(this.originalValue);
9645         this.validate();
9646     },
9647      /**
9648      * Returns the name of the field
9649      * @return {Mixed} name The name field
9650      */
9651     getName: function(){
9652         return this.name;
9653     },
9654      /**
9655      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9656      * @return {Mixed} value The field value
9657      */
9658     getValue : function(){
9659         
9660         var v = this.inputEl().getValue();
9661         
9662         return v;
9663     },
9664     /**
9665      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9666      * @return {Mixed} value The field value
9667      */
9668     getRawValue : function(){
9669         var v = this.inputEl().getValue();
9670         
9671         return v;
9672     },
9673     
9674     /**
9675      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9676      * @param {Mixed} value The value to set
9677      */
9678     setRawValue : function(v){
9679         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9680     },
9681     
9682     selectText : function(start, end){
9683         var v = this.getRawValue();
9684         if(v.length > 0){
9685             start = start === undefined ? 0 : start;
9686             end = end === undefined ? v.length : end;
9687             var d = this.inputEl().dom;
9688             if(d.setSelectionRange){
9689                 d.setSelectionRange(start, end);
9690             }else if(d.createTextRange){
9691                 var range = d.createTextRange();
9692                 range.moveStart("character", start);
9693                 range.moveEnd("character", v.length-end);
9694                 range.select();
9695             }
9696         }
9697     },
9698     
9699     /**
9700      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9701      * @param {Mixed} value The value to set
9702      */
9703     setValue : function(v){
9704         this.value = v;
9705         if(this.rendered){
9706             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9707             this.validate();
9708         }
9709     },
9710     
9711     /*
9712     processValue : function(value){
9713         if(this.stripCharsRe){
9714             var newValue = value.replace(this.stripCharsRe, '');
9715             if(newValue !== value){
9716                 this.setRawValue(newValue);
9717                 return newValue;
9718             }
9719         }
9720         return value;
9721     },
9722   */
9723     preFocus : function(){
9724         
9725         if(this.selectOnFocus){
9726             this.inputEl().dom.select();
9727         }
9728     },
9729     filterKeys : function(e){
9730         var k = e.getKey();
9731         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9732             return;
9733         }
9734         var c = e.getCharCode(), cc = String.fromCharCode(c);
9735         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9736             return;
9737         }
9738         if(!this.maskRe.test(cc)){
9739             e.stopEvent();
9740         }
9741     },
9742      /**
9743      * Clear any invalid styles/messages for this field
9744      */
9745     clearInvalid : function(){
9746         
9747         if(!this.el || this.preventMark){ // not rendered
9748             return;
9749         }
9750         
9751         
9752         this.el.removeClass([this.invalidClass, 'is-invalid']);
9753         
9754         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9755             
9756             var feedback = this.el.select('.form-control-feedback', true).first();
9757             
9758             if(feedback){
9759                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9760             }
9761             
9762         }
9763         
9764         if(this.indicator){
9765             this.indicator.removeClass('visible');
9766             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9767         }
9768         
9769         this.fireEvent('valid', this);
9770     },
9771     
9772      /**
9773      * Mark this field as valid
9774      */
9775     markValid : function()
9776     {
9777         if(!this.el  || this.preventMark){ // not rendered...
9778             return;
9779         }
9780         
9781         this.el.removeClass([this.invalidClass, this.validClass]);
9782         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9783
9784         var feedback = this.el.select('.form-control-feedback', true).first();
9785             
9786         if(feedback){
9787             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9788         }
9789         
9790         if(this.indicator){
9791             this.indicator.removeClass('visible');
9792             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9793         }
9794         
9795         if(this.disabled){
9796             return;
9797         }
9798         
9799         if(this.allowBlank && !this.getRawValue().length){
9800             return;
9801         }
9802         if (Roo.bootstrap.version == 3) {
9803             this.el.addClass(this.validClass);
9804         } else {
9805             this.inputEl().addClass('is-valid');
9806         }
9807
9808         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9815             }
9816             
9817         }
9818         
9819         this.fireEvent('valid', this);
9820     },
9821     
9822      /**
9823      * Mark this field as invalid
9824      * @param {String} msg The validation message
9825      */
9826     markInvalid : function(msg)
9827     {
9828         if(!this.el  || this.preventMark){ // not rendered
9829             return;
9830         }
9831         
9832         this.el.removeClass([this.invalidClass, this.validClass]);
9833         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9834         
9835         var feedback = this.el.select('.form-control-feedback', true).first();
9836             
9837         if(feedback){
9838             this.el.select('.form-control-feedback', true).first().removeClass(
9839                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9840         }
9841
9842         if(this.disabled){
9843             return;
9844         }
9845         
9846         if(this.allowBlank && !this.getRawValue().length){
9847             return;
9848         }
9849         
9850         if(this.indicator){
9851             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9852             this.indicator.addClass('visible');
9853         }
9854         if (Roo.bootstrap.version == 3) {
9855             this.el.addClass(this.invalidClass);
9856         } else {
9857             this.inputEl().addClass('is-invalid');
9858         }
9859         
9860         
9861         
9862         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9863             
9864             var feedback = this.el.select('.form-control-feedback', true).first();
9865             
9866             if(feedback){
9867                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9868                 
9869                 if(this.getValue().length || this.forceFeedback){
9870                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9871                 }
9872                 
9873             }
9874             
9875         }
9876         
9877         this.fireEvent('invalid', this, msg);
9878     },
9879     // private
9880     SafariOnKeyDown : function(event)
9881     {
9882         // this is a workaround for a password hang bug on chrome/ webkit.
9883         if (this.inputEl().dom.type != 'password') {
9884             return;
9885         }
9886         
9887         var isSelectAll = false;
9888         
9889         if(this.inputEl().dom.selectionEnd > 0){
9890             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9891         }
9892         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9893             event.preventDefault();
9894             this.setValue('');
9895             return;
9896         }
9897         
9898         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9899             
9900             event.preventDefault();
9901             // this is very hacky as keydown always get's upper case.
9902             //
9903             var cc = String.fromCharCode(event.getCharCode());
9904             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9905             
9906         }
9907     },
9908     adjustWidth : function(tag, w){
9909         tag = tag.toLowerCase();
9910         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9911             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9912                 if(tag == 'input'){
9913                     return w + 2;
9914                 }
9915                 if(tag == 'textarea'){
9916                     return w-2;
9917                 }
9918             }else if(Roo.isOpera){
9919                 if(tag == 'input'){
9920                     return w + 2;
9921                 }
9922                 if(tag == 'textarea'){
9923                     return w-2;
9924                 }
9925             }
9926         }
9927         return w;
9928     },
9929     
9930     setFieldLabel : function(v)
9931     {
9932         if(!this.rendered){
9933             return;
9934         }
9935         
9936         if(this.indicatorEl()){
9937             var ar = this.el.select('label > span',true);
9938             
9939             if (ar.elements.length) {
9940                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9941                 this.fieldLabel = v;
9942                 return;
9943             }
9944             
9945             var br = this.el.select('label',true);
9946             
9947             if(br.elements.length) {
9948                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9949                 this.fieldLabel = v;
9950                 return;
9951             }
9952             
9953             Roo.log('Cannot Found any of label > span || label in input');
9954             return;
9955         }
9956         
9957         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9958         this.fieldLabel = v;
9959         
9960         
9961     }
9962 });
9963
9964  
9965 /*
9966  * - LGPL
9967  *
9968  * Input
9969  * 
9970  */
9971
9972 /**
9973  * @class Roo.bootstrap.TextArea
9974  * @extends Roo.bootstrap.Input
9975  * Bootstrap TextArea class
9976  * @cfg {Number} cols Specifies the visible width of a text area
9977  * @cfg {Number} rows Specifies the visible number of lines in a text area
9978  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9979  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9980  * @cfg {string} html text
9981  * 
9982  * @constructor
9983  * Create a new TextArea
9984  * @param {Object} config The config object
9985  */
9986
9987 Roo.bootstrap.TextArea = function(config){
9988     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9989    
9990 };
9991
9992 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9993      
9994     cols : false,
9995     rows : 5,
9996     readOnly : false,
9997     warp : 'soft',
9998     resize : false,
9999     value: false,
10000     html: false,
10001     
10002     getAutoCreate : function(){
10003         
10004         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10005         
10006         var id = Roo.id();
10007         
10008         var cfg = {};
10009         
10010         if(this.inputType != 'hidden'){
10011             cfg.cls = 'form-group' //input-group
10012         }
10013         
10014         var input =  {
10015             tag: 'textarea',
10016             id : id,
10017             warp : this.warp,
10018             rows : this.rows,
10019             value : this.value || '',
10020             html: this.html || '',
10021             cls : 'form-control',
10022             placeholder : this.placeholder || '' 
10023             
10024         };
10025         
10026         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10027             input.maxLength = this.maxLength;
10028         }
10029         
10030         if(this.resize){
10031             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10032         }
10033         
10034         if(this.cols){
10035             input.cols = this.cols;
10036         }
10037         
10038         if (this.readOnly) {
10039             input.readonly = true;
10040         }
10041         
10042         if (this.name) {
10043             input.name = this.name;
10044         }
10045         
10046         if (this.size) {
10047             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10048         }
10049         
10050         var settings=this;
10051         ['xs','sm','md','lg'].map(function(size){
10052             if (settings[size]) {
10053                 cfg.cls += ' col-' + size + '-' + settings[size];
10054             }
10055         });
10056         
10057         var inputblock = input;
10058         
10059         if(this.hasFeedback && !this.allowBlank){
10060             
10061             var feedback = {
10062                 tag: 'span',
10063                 cls: 'glyphicon form-control-feedback'
10064             };
10065
10066             inputblock = {
10067                 cls : 'has-feedback',
10068                 cn :  [
10069                     input,
10070                     feedback
10071                 ] 
10072             };  
10073         }
10074         
10075         
10076         if (this.before || this.after) {
10077             
10078             inputblock = {
10079                 cls : 'input-group',
10080                 cn :  [] 
10081             };
10082             if (this.before) {
10083                 inputblock.cn.push({
10084                     tag :'span',
10085                     cls : 'input-group-addon',
10086                     html : this.before
10087                 });
10088             }
10089             
10090             inputblock.cn.push(input);
10091             
10092             if(this.hasFeedback && !this.allowBlank){
10093                 inputblock.cls += ' has-feedback';
10094                 inputblock.cn.push(feedback);
10095             }
10096             
10097             if (this.after) {
10098                 inputblock.cn.push({
10099                     tag :'span',
10100                     cls : 'input-group-addon',
10101                     html : this.after
10102                 });
10103             }
10104             
10105         }
10106         
10107         if (align ==='left' && this.fieldLabel.length) {
10108             cfg.cn = [
10109                 {
10110                     tag: 'label',
10111                     'for' :  id,
10112                     cls : 'control-label',
10113                     html : this.fieldLabel
10114                 },
10115                 {
10116                     cls : "",
10117                     cn: [
10118                         inputblock
10119                     ]
10120                 }
10121
10122             ];
10123             
10124             if(this.labelWidth > 12){
10125                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10126             }
10127
10128             if(this.labelWidth < 13 && this.labelmd == 0){
10129                 this.labelmd = this.labelWidth;
10130             }
10131
10132             if(this.labellg > 0){
10133                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10134                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10135             }
10136
10137             if(this.labelmd > 0){
10138                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10139                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10140             }
10141
10142             if(this.labelsm > 0){
10143                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10144                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10145             }
10146
10147             if(this.labelxs > 0){
10148                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10149                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10150             }
10151             
10152         } else if ( this.fieldLabel.length) {
10153             cfg.cn = [
10154
10155                {
10156                    tag: 'label',
10157                    //cls : 'input-group-addon',
10158                    html : this.fieldLabel
10159
10160                },
10161
10162                inputblock
10163
10164            ];
10165
10166         } else {
10167
10168             cfg.cn = [
10169
10170                 inputblock
10171
10172             ];
10173                 
10174         }
10175         
10176         if (this.disabled) {
10177             input.disabled=true;
10178         }
10179         
10180         return cfg;
10181         
10182     },
10183     /**
10184      * return the real textarea element.
10185      */
10186     inputEl: function ()
10187     {
10188         return this.el.select('textarea.form-control',true).first();
10189     },
10190     
10191     /**
10192      * Clear any invalid styles/messages for this field
10193      */
10194     clearInvalid : function()
10195     {
10196         
10197         if(!this.el || this.preventMark){ // not rendered
10198             return;
10199         }
10200         
10201         var label = this.el.select('label', true).first();
10202         var icon = this.el.select('i.fa-star', true).first();
10203         
10204         if(label && icon){
10205             icon.remove();
10206         }
10207         
10208         this.el.removeClass([ this.invalidClass, 'is-invalid']);
10209         
10210         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10211             
10212             var feedback = this.el.select('.form-control-feedback', true).first();
10213             
10214             if(feedback){
10215                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10216             }
10217             
10218         }
10219         
10220         this.fireEvent('valid', this);
10221     },
10222     
10223      /**
10224      * Mark this field as valid
10225      */
10226     markValid : function()
10227     {
10228         if(!this.el  || this.preventMark){ // not rendered
10229             return;
10230         }
10231         
10232         this.el.removeClass([this.invalidClass, this.validClass, 'is-valid', 'is-invalid']);
10233         
10234         
10235         var feedback = this.el.select('.form-control-feedback', true).first();
10236             
10237         if(feedback){
10238             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10239         }
10240
10241         if(this.disabled || this.allowBlank){
10242             return;
10243         }
10244         
10245         var label = this.el.select('label', true).first();
10246         var icon = this.el.select('i.fa-star', true).first();
10247         
10248         if(label && icon){
10249             icon.remove();
10250         }
10251         if (Roo.bootstrap.version == 3) {
10252             this.el.addClass(this.validClass);
10253         } else {
10254             this.inputEl().addClass('is-valid');
10255         }
10256         
10257         
10258         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10259             
10260             var feedback = this.el.select('.form-control-feedback', true).first();
10261             
10262             if(feedback){
10263                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10264                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10265             }
10266             
10267         }
10268         
10269         this.fireEvent('valid', this);
10270     },
10271     
10272      /**
10273      * Mark this field as invalid
10274      * @param {String} msg The validation message
10275      */
10276     markInvalid : function(msg)
10277     {
10278         if(!this.el  || this.preventMark){ // not rendered
10279             return;
10280         }
10281         
10282         this.el.removeClass([this.invalidClass, this.validClass, 'is-valid', 'is-invalid']);
10283         
10284         var feedback = this.el.select('.form-control-feedback', true).first();
10285             
10286         if(feedback){
10287             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10288         }
10289
10290         if(this.disabled || this.allowBlank){
10291             return;
10292         }
10293         
10294         var label = this.el.select('label', true).first();
10295         var icon = this.el.select('i.fa-star', true).first();
10296         
10297         if(!this.getValue().length && label && !icon){
10298             this.el.createChild({
10299                 tag : 'i',
10300                 cls : 'text-danger fa fa-lg fa-star',
10301                 tooltip : 'This field is required',
10302                 style : 'margin-right:5px;'
10303             }, label, true);
10304         }
10305         
10306         if (Roo.bootstrap.version == 3) {
10307             this.el.addClass(this.invalidClass);
10308         } else {
10309             this.inputEl().addClass('is-invalid');
10310         }
10311         
10312         // fixme ... this may be depricated need to test..
10313         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10314             
10315             var feedback = this.el.select('.form-control-feedback', true).first();
10316             
10317             if(feedback){
10318                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10319                 
10320                 if(this.getValue().length || this.forceFeedback){
10321                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10322                 }
10323                 
10324             }
10325             
10326         }
10327         
10328         this.fireEvent('invalid', this, msg);
10329     }
10330 });
10331
10332  
10333 /*
10334  * - LGPL
10335  *
10336  * trigger field - base class for combo..
10337  * 
10338  */
10339  
10340 /**
10341  * @class Roo.bootstrap.TriggerField
10342  * @extends Roo.bootstrap.Input
10343  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10344  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10345  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10346  * for which you can provide a custom implementation.  For example:
10347  * <pre><code>
10348 var trigger = new Roo.bootstrap.TriggerField();
10349 trigger.onTriggerClick = myTriggerFn;
10350 trigger.applyTo('my-field');
10351 </code></pre>
10352  *
10353  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10354  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10355  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10356  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10357  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10358
10359  * @constructor
10360  * Create a new TriggerField.
10361  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10362  * to the base TextField)
10363  */
10364 Roo.bootstrap.TriggerField = function(config){
10365     this.mimicing = false;
10366     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10367 };
10368
10369 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10370     /**
10371      * @cfg {String} triggerClass A CSS class to apply to the trigger
10372      */
10373      /**
10374      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10375      */
10376     hideTrigger:false,
10377
10378     /**
10379      * @cfg {Boolean} removable (true|false) special filter default false
10380      */
10381     removable : false,
10382     
10383     /** @cfg {Boolean} grow @hide */
10384     /** @cfg {Number} growMin @hide */
10385     /** @cfg {Number} growMax @hide */
10386
10387     /**
10388      * @hide 
10389      * @method
10390      */
10391     autoSize: Roo.emptyFn,
10392     // private
10393     monitorTab : true,
10394     // private
10395     deferHeight : true,
10396
10397     
10398     actionMode : 'wrap',
10399     
10400     caret : false,
10401     
10402     
10403     getAutoCreate : function(){
10404        
10405         var align = this.labelAlign || this.parentLabelAlign();
10406         
10407         var id = Roo.id();
10408         
10409         var cfg = {
10410             cls: 'form-group' //input-group
10411         };
10412         
10413         
10414         var input =  {
10415             tag: 'input',
10416             id : id,
10417             type : this.inputType,
10418             cls : 'form-control',
10419             autocomplete: 'new-password',
10420             placeholder : this.placeholder || '' 
10421             
10422         };
10423         if (this.name) {
10424             input.name = this.name;
10425         }
10426         if (this.size) {
10427             input.cls += ' input-' + this.size;
10428         }
10429         
10430         if (this.disabled) {
10431             input.disabled=true;
10432         }
10433         
10434         var inputblock = input;
10435         
10436         if(this.hasFeedback && !this.allowBlank){
10437             
10438             var feedback = {
10439                 tag: 'span',
10440                 cls: 'glyphicon form-control-feedback'
10441             };
10442             
10443             if(this.removable && !this.editable && !this.tickable){
10444                 inputblock = {
10445                     cls : 'has-feedback',
10446                     cn :  [
10447                         inputblock,
10448                         {
10449                             tag: 'button',
10450                             html : 'x',
10451                             cls : 'roo-combo-removable-btn close'
10452                         },
10453                         feedback
10454                     ] 
10455                 };
10456             } else {
10457                 inputblock = {
10458                     cls : 'has-feedback',
10459                     cn :  [
10460                         inputblock,
10461                         feedback
10462                     ] 
10463                 };
10464             }
10465
10466         } else {
10467             if(this.removable && !this.editable && !this.tickable){
10468                 inputblock = {
10469                     cls : 'roo-removable',
10470                     cn :  [
10471                         inputblock,
10472                         {
10473                             tag: 'button',
10474                             html : 'x',
10475                             cls : 'roo-combo-removable-btn close'
10476                         }
10477                     ] 
10478                 };
10479             }
10480         }
10481         
10482         if (this.before || this.after) {
10483             
10484             inputblock = {
10485                 cls : 'input-group',
10486                 cn :  [] 
10487             };
10488             if (this.before) {
10489                 inputblock.cn.push({
10490                     tag :'span',
10491                     cls : 'input-group-addon input-group-prepend input-group-text',
10492                     html : this.before
10493                 });
10494             }
10495             
10496             inputblock.cn.push(input);
10497             
10498             if(this.hasFeedback && !this.allowBlank){
10499                 inputblock.cls += ' has-feedback';
10500                 inputblock.cn.push(feedback);
10501             }
10502             
10503             if (this.after) {
10504                 inputblock.cn.push({
10505                     tag :'span',
10506                     cls : 'input-group-addon input-group-append input-group-text',
10507                     html : this.after
10508                 });
10509             }
10510             
10511         };
10512         
10513       
10514         
10515         var ibwrap = inputblock;
10516         
10517         if(this.multiple){
10518             ibwrap = {
10519                 tag: 'ul',
10520                 cls: 'roo-select2-choices',
10521                 cn:[
10522                     {
10523                         tag: 'li',
10524                         cls: 'roo-select2-search-field',
10525                         cn: [
10526
10527                             inputblock
10528                         ]
10529                     }
10530                 ]
10531             };
10532                 
10533         }
10534         
10535         var combobox = {
10536             cls: 'roo-select2-container input-group',
10537             cn: [
10538                  {
10539                     tag: 'input',
10540                     type : 'hidden',
10541                     cls: 'form-hidden-field'
10542                 },
10543                 ibwrap
10544             ]
10545         };
10546         
10547         if(!this.multiple && this.showToggleBtn){
10548             
10549             var caret = {
10550                         tag: 'span',
10551                         cls: 'caret'
10552              };
10553             if (this.caret != false) {
10554                 caret = {
10555                      tag: 'i',
10556                      cls: 'fa fa-' + this.caret
10557                 };
10558                 
10559             }
10560             
10561             combobox.cn.push({
10562                 tag :'span',
10563                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10564                 cn : [
10565                     caret,
10566                     {
10567                         tag: 'span',
10568                         cls: 'combobox-clear',
10569                         cn  : [
10570                             {
10571                                 tag : 'i',
10572                                 cls: 'icon-remove'
10573                             }
10574                         ]
10575                     }
10576                 ]
10577
10578             })
10579         }
10580         
10581         if(this.multiple){
10582             combobox.cls += ' roo-select2-container-multi';
10583         }
10584          var indicator = {
10585             tag : 'i',
10586             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10587             tooltip : 'This field is required'
10588         };
10589         if (Roo.bootstrap.version == 4) {
10590             indicator = {
10591                 tag : 'i',
10592                 style : 'display:none'
10593             };
10594         }
10595         
10596         
10597         if (align ==='left' && this.fieldLabel.length) {
10598             
10599             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10600
10601             cfg.cn = [
10602                 indicator,
10603                 {
10604                     tag: 'label',
10605                     'for' :  id,
10606                     cls : 'control-label',
10607                     html : this.fieldLabel
10608
10609                 },
10610                 {
10611                     cls : "", 
10612                     cn: [
10613                         combobox
10614                     ]
10615                 }
10616
10617             ];
10618             
10619             var labelCfg = cfg.cn[1];
10620             var contentCfg = cfg.cn[2];
10621             
10622             if(this.indicatorpos == 'right'){
10623                 cfg.cn = [
10624                     {
10625                         tag: 'label',
10626                         'for' :  id,
10627                         cls : 'control-label',
10628                         cn : [
10629                             {
10630                                 tag : 'span',
10631                                 html : this.fieldLabel
10632                             },
10633                             indicator
10634                         ]
10635                     },
10636                     {
10637                         cls : "", 
10638                         cn: [
10639                             combobox
10640                         ]
10641                     }
10642
10643                 ];
10644                 
10645                 labelCfg = cfg.cn[0];
10646                 contentCfg = cfg.cn[1];
10647             }
10648             
10649             if(this.labelWidth > 12){
10650                 labelCfg.style = "width: " + this.labelWidth + 'px';
10651             }
10652             
10653             if(this.labelWidth < 13 && this.labelmd == 0){
10654                 this.labelmd = this.labelWidth;
10655             }
10656             
10657             if(this.labellg > 0){
10658                 labelCfg.cls += ' col-lg-' + this.labellg;
10659                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10660             }
10661             
10662             if(this.labelmd > 0){
10663                 labelCfg.cls += ' col-md-' + this.labelmd;
10664                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10665             }
10666             
10667             if(this.labelsm > 0){
10668                 labelCfg.cls += ' col-sm-' + this.labelsm;
10669                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10670             }
10671             
10672             if(this.labelxs > 0){
10673                 labelCfg.cls += ' col-xs-' + this.labelxs;
10674                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10675             }
10676             
10677         } else if ( this.fieldLabel.length) {
10678 //                Roo.log(" label");
10679             cfg.cn = [
10680                 indicator,
10681                {
10682                    tag: 'label',
10683                    //cls : 'input-group-addon',
10684                    html : this.fieldLabel
10685
10686                },
10687
10688                combobox
10689
10690             ];
10691             
10692             if(this.indicatorpos == 'right'){
10693                 
10694                 cfg.cn = [
10695                     {
10696                        tag: 'label',
10697                        cn : [
10698                            {
10699                                tag : 'span',
10700                                html : this.fieldLabel
10701                            },
10702                            indicator
10703                        ]
10704
10705                     },
10706                     combobox
10707
10708                 ];
10709
10710             }
10711
10712         } else {
10713             
10714 //                Roo.log(" no label && no align");
10715                 cfg = combobox
10716                      
10717                 
10718         }
10719         
10720         var settings=this;
10721         ['xs','sm','md','lg'].map(function(size){
10722             if (settings[size]) {
10723                 cfg.cls += ' col-' + size + '-' + settings[size];
10724             }
10725         });
10726         
10727         return cfg;
10728         
10729     },
10730     
10731     
10732     
10733     // private
10734     onResize : function(w, h){
10735 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10736 //        if(typeof w == 'number'){
10737 //            var x = w - this.trigger.getWidth();
10738 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10739 //            this.trigger.setStyle('left', x+'px');
10740 //        }
10741     },
10742
10743     // private
10744     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10745
10746     // private
10747     getResizeEl : function(){
10748         return this.inputEl();
10749     },
10750
10751     // private
10752     getPositionEl : function(){
10753         return this.inputEl();
10754     },
10755
10756     // private
10757     alignErrorIcon : function(){
10758         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10759     },
10760
10761     // private
10762     initEvents : function(){
10763         
10764         this.createList();
10765         
10766         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10767         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10768         if(!this.multiple && this.showToggleBtn){
10769             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10770             if(this.hideTrigger){
10771                 this.trigger.setDisplayed(false);
10772             }
10773             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10774         }
10775         
10776         if(this.multiple){
10777             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10778         }
10779         
10780         if(this.removable && !this.editable && !this.tickable){
10781             var close = this.closeTriggerEl();
10782             
10783             if(close){
10784                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10785                 close.on('click', this.removeBtnClick, this, close);
10786             }
10787         }
10788         
10789         //this.trigger.addClassOnOver('x-form-trigger-over');
10790         //this.trigger.addClassOnClick('x-form-trigger-click');
10791         
10792         //if(!this.width){
10793         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10794         //}
10795     },
10796     
10797     closeTriggerEl : function()
10798     {
10799         var close = this.el.select('.roo-combo-removable-btn', true).first();
10800         return close ? close : false;
10801     },
10802     
10803     removeBtnClick : function(e, h, el)
10804     {
10805         e.preventDefault();
10806         
10807         if(this.fireEvent("remove", this) !== false){
10808             this.reset();
10809             this.fireEvent("afterremove", this)
10810         }
10811     },
10812     
10813     createList : function()
10814     {
10815         this.list = Roo.get(document.body).createChild({
10816             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10817             cls: 'typeahead typeahead-long dropdown-menu',
10818             style: 'display:none'
10819         });
10820         
10821         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10822         
10823     },
10824
10825     // private
10826     initTrigger : function(){
10827        
10828     },
10829
10830     // private
10831     onDestroy : function(){
10832         if(this.trigger){
10833             this.trigger.removeAllListeners();
10834           //  this.trigger.remove();
10835         }
10836         //if(this.wrap){
10837         //    this.wrap.remove();
10838         //}
10839         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10840     },
10841
10842     // private
10843     onFocus : function(){
10844         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10845         /*
10846         if(!this.mimicing){
10847             this.wrap.addClass('x-trigger-wrap-focus');
10848             this.mimicing = true;
10849             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10850             if(this.monitorTab){
10851                 this.el.on("keydown", this.checkTab, this);
10852             }
10853         }
10854         */
10855     },
10856
10857     // private
10858     checkTab : function(e){
10859         if(e.getKey() == e.TAB){
10860             this.triggerBlur();
10861         }
10862     },
10863
10864     // private
10865     onBlur : function(){
10866         // do nothing
10867     },
10868
10869     // private
10870     mimicBlur : function(e, t){
10871         /*
10872         if(!this.wrap.contains(t) && this.validateBlur()){
10873             this.triggerBlur();
10874         }
10875         */
10876     },
10877
10878     // private
10879     triggerBlur : function(){
10880         this.mimicing = false;
10881         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10882         if(this.monitorTab){
10883             this.el.un("keydown", this.checkTab, this);
10884         }
10885         //this.wrap.removeClass('x-trigger-wrap-focus');
10886         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10887     },
10888
10889     // private
10890     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10891     validateBlur : function(e, t){
10892         return true;
10893     },
10894
10895     // private
10896     onDisable : function(){
10897         this.inputEl().dom.disabled = true;
10898         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10899         //if(this.wrap){
10900         //    this.wrap.addClass('x-item-disabled');
10901         //}
10902     },
10903
10904     // private
10905     onEnable : function(){
10906         this.inputEl().dom.disabled = false;
10907         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10908         //if(this.wrap){
10909         //    this.el.removeClass('x-item-disabled');
10910         //}
10911     },
10912
10913     // private
10914     onShow : function(){
10915         var ae = this.getActionEl();
10916         
10917         if(ae){
10918             ae.dom.style.display = '';
10919             ae.dom.style.visibility = 'visible';
10920         }
10921     },
10922
10923     // private
10924     
10925     onHide : function(){
10926         var ae = this.getActionEl();
10927         ae.dom.style.display = 'none';
10928     },
10929
10930     /**
10931      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10932      * by an implementing function.
10933      * @method
10934      * @param {EventObject} e
10935      */
10936     onTriggerClick : Roo.emptyFn
10937 });
10938  /*
10939  * Based on:
10940  * Ext JS Library 1.1.1
10941  * Copyright(c) 2006-2007, Ext JS, LLC.
10942  *
10943  * Originally Released Under LGPL - original licence link has changed is not relivant.
10944  *
10945  * Fork - LGPL
10946  * <script type="text/javascript">
10947  */
10948
10949
10950 /**
10951  * @class Roo.data.SortTypes
10952  * @singleton
10953  * Defines the default sorting (casting?) comparison functions used when sorting data.
10954  */
10955 Roo.data.SortTypes = {
10956     /**
10957      * Default sort that does nothing
10958      * @param {Mixed} s The value being converted
10959      * @return {Mixed} The comparison value
10960      */
10961     none : function(s){
10962         return s;
10963     },
10964     
10965     /**
10966      * The regular expression used to strip tags
10967      * @type {RegExp}
10968      * @property
10969      */
10970     stripTagsRE : /<\/?[^>]+>/gi,
10971     
10972     /**
10973      * Strips all HTML tags to sort on text only
10974      * @param {Mixed} s The value being converted
10975      * @return {String} The comparison value
10976      */
10977     asText : function(s){
10978         return String(s).replace(this.stripTagsRE, "");
10979     },
10980     
10981     /**
10982      * Strips all HTML tags to sort on text only - Case insensitive
10983      * @param {Mixed} s The value being converted
10984      * @return {String} The comparison value
10985      */
10986     asUCText : function(s){
10987         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10988     },
10989     
10990     /**
10991      * Case insensitive string
10992      * @param {Mixed} s The value being converted
10993      * @return {String} The comparison value
10994      */
10995     asUCString : function(s) {
10996         return String(s).toUpperCase();
10997     },
10998     
10999     /**
11000      * Date sorting
11001      * @param {Mixed} s The value being converted
11002      * @return {Number} The comparison value
11003      */
11004     asDate : function(s) {
11005         if(!s){
11006             return 0;
11007         }
11008         if(s instanceof Date){
11009             return s.getTime();
11010         }
11011         return Date.parse(String(s));
11012     },
11013     
11014     /**
11015      * Float sorting
11016      * @param {Mixed} s The value being converted
11017      * @return {Float} The comparison value
11018      */
11019     asFloat : function(s) {
11020         var val = parseFloat(String(s).replace(/,/g, ""));
11021         if(isNaN(val)) {
11022             val = 0;
11023         }
11024         return val;
11025     },
11026     
11027     /**
11028      * Integer sorting
11029      * @param {Mixed} s The value being converted
11030      * @return {Number} The comparison value
11031      */
11032     asInt : function(s) {
11033         var val = parseInt(String(s).replace(/,/g, ""));
11034         if(isNaN(val)) {
11035             val = 0;
11036         }
11037         return val;
11038     }
11039 };/*
11040  * Based on:
11041  * Ext JS Library 1.1.1
11042  * Copyright(c) 2006-2007, Ext JS, LLC.
11043  *
11044  * Originally Released Under LGPL - original licence link has changed is not relivant.
11045  *
11046  * Fork - LGPL
11047  * <script type="text/javascript">
11048  */
11049
11050 /**
11051 * @class Roo.data.Record
11052  * Instances of this class encapsulate both record <em>definition</em> information, and record
11053  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11054  * to access Records cached in an {@link Roo.data.Store} object.<br>
11055  * <p>
11056  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11057  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11058  * objects.<br>
11059  * <p>
11060  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11061  * @constructor
11062  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11063  * {@link #create}. The parameters are the same.
11064  * @param {Array} data An associative Array of data values keyed by the field name.
11065  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11066  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11067  * not specified an integer id is generated.
11068  */
11069 Roo.data.Record = function(data, id){
11070     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11071     this.data = data;
11072 };
11073
11074 /**
11075  * Generate a constructor for a specific record layout.
11076  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11077  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11078  * Each field definition object may contain the following properties: <ul>
11079  * <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,
11080  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11081  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11082  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11083  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11084  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11085  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11086  * this may be omitted.</p></li>
11087  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11088  * <ul><li>auto (Default, implies no conversion)</li>
11089  * <li>string</li>
11090  * <li>int</li>
11091  * <li>float</li>
11092  * <li>boolean</li>
11093  * <li>date</li></ul></p></li>
11094  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11095  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11096  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11097  * by the Reader into an object that will be stored in the Record. It is passed the
11098  * following parameters:<ul>
11099  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11100  * </ul></p></li>
11101  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11102  * </ul>
11103  * <br>usage:<br><pre><code>
11104 var TopicRecord = Roo.data.Record.create(
11105     {name: 'title', mapping: 'topic_title'},
11106     {name: 'author', mapping: 'username'},
11107     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11108     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11109     {name: 'lastPoster', mapping: 'user2'},
11110     {name: 'excerpt', mapping: 'post_text'}
11111 );
11112
11113 var myNewRecord = new TopicRecord({
11114     title: 'Do my job please',
11115     author: 'noobie',
11116     totalPosts: 1,
11117     lastPost: new Date(),
11118     lastPoster: 'Animal',
11119     excerpt: 'No way dude!'
11120 });
11121 myStore.add(myNewRecord);
11122 </code></pre>
11123  * @method create
11124  * @static
11125  */
11126 Roo.data.Record.create = function(o){
11127     var f = function(){
11128         f.superclass.constructor.apply(this, arguments);
11129     };
11130     Roo.extend(f, Roo.data.Record);
11131     var p = f.prototype;
11132     p.fields = new Roo.util.MixedCollection(false, function(field){
11133         return field.name;
11134     });
11135     for(var i = 0, len = o.length; i < len; i++){
11136         p.fields.add(new Roo.data.Field(o[i]));
11137     }
11138     f.getField = function(name){
11139         return p.fields.get(name);  
11140     };
11141     return f;
11142 };
11143
11144 Roo.data.Record.AUTO_ID = 1000;
11145 Roo.data.Record.EDIT = 'edit';
11146 Roo.data.Record.REJECT = 'reject';
11147 Roo.data.Record.COMMIT = 'commit';
11148
11149 Roo.data.Record.prototype = {
11150     /**
11151      * Readonly flag - true if this record has been modified.
11152      * @type Boolean
11153      */
11154     dirty : false,
11155     editing : false,
11156     error: null,
11157     modified: null,
11158
11159     // private
11160     join : function(store){
11161         this.store = store;
11162     },
11163
11164     /**
11165      * Set the named field to the specified value.
11166      * @param {String} name The name of the field to set.
11167      * @param {Object} value The value to set the field to.
11168      */
11169     set : function(name, value){
11170         if(this.data[name] == value){
11171             return;
11172         }
11173         this.dirty = true;
11174         if(!this.modified){
11175             this.modified = {};
11176         }
11177         if(typeof this.modified[name] == 'undefined'){
11178             this.modified[name] = this.data[name];
11179         }
11180         this.data[name] = value;
11181         if(!this.editing && this.store){
11182             this.store.afterEdit(this);
11183         }       
11184     },
11185
11186     /**
11187      * Get the value of the named field.
11188      * @param {String} name The name of the field to get the value of.
11189      * @return {Object} The value of the field.
11190      */
11191     get : function(name){
11192         return this.data[name]; 
11193     },
11194
11195     // private
11196     beginEdit : function(){
11197         this.editing = true;
11198         this.modified = {}; 
11199     },
11200
11201     // private
11202     cancelEdit : function(){
11203         this.editing = false;
11204         delete this.modified;
11205     },
11206
11207     // private
11208     endEdit : function(){
11209         this.editing = false;
11210         if(this.dirty && this.store){
11211             this.store.afterEdit(this);
11212         }
11213     },
11214
11215     /**
11216      * Usually called by the {@link Roo.data.Store} which owns the Record.
11217      * Rejects all changes made to the Record since either creation, or the last commit operation.
11218      * Modified fields are reverted to their original values.
11219      * <p>
11220      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11221      * of reject operations.
11222      */
11223     reject : function(){
11224         var m = this.modified;
11225         for(var n in m){
11226             if(typeof m[n] != "function"){
11227                 this.data[n] = m[n];
11228             }
11229         }
11230         this.dirty = false;
11231         delete this.modified;
11232         this.editing = false;
11233         if(this.store){
11234             this.store.afterReject(this);
11235         }
11236     },
11237
11238     /**
11239      * Usually called by the {@link Roo.data.Store} which owns the Record.
11240      * Commits all changes made to the Record since either creation, or the last commit operation.
11241      * <p>
11242      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11243      * of commit operations.
11244      */
11245     commit : function(){
11246         this.dirty = false;
11247         delete this.modified;
11248         this.editing = false;
11249         if(this.store){
11250             this.store.afterCommit(this);
11251         }
11252     },
11253
11254     // private
11255     hasError : function(){
11256         return this.error != null;
11257     },
11258
11259     // private
11260     clearError : function(){
11261         this.error = null;
11262     },
11263
11264     /**
11265      * Creates a copy of this record.
11266      * @param {String} id (optional) A new record id if you don't want to use this record's id
11267      * @return {Record}
11268      */
11269     copy : function(newId) {
11270         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11271     }
11272 };/*
11273  * Based on:
11274  * Ext JS Library 1.1.1
11275  * Copyright(c) 2006-2007, Ext JS, LLC.
11276  *
11277  * Originally Released Under LGPL - original licence link has changed is not relivant.
11278  *
11279  * Fork - LGPL
11280  * <script type="text/javascript">
11281  */
11282
11283
11284
11285 /**
11286  * @class Roo.data.Store
11287  * @extends Roo.util.Observable
11288  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11289  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11290  * <p>
11291  * 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
11292  * has no knowledge of the format of the data returned by the Proxy.<br>
11293  * <p>
11294  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11295  * instances from the data object. These records are cached and made available through accessor functions.
11296  * @constructor
11297  * Creates a new Store.
11298  * @param {Object} config A config object containing the objects needed for the Store to access data,
11299  * and read the data into Records.
11300  */
11301 Roo.data.Store = function(config){
11302     this.data = new Roo.util.MixedCollection(false);
11303     this.data.getKey = function(o){
11304         return o.id;
11305     };
11306     this.baseParams = {};
11307     // private
11308     this.paramNames = {
11309         "start" : "start",
11310         "limit" : "limit",
11311         "sort" : "sort",
11312         "dir" : "dir",
11313         "multisort" : "_multisort"
11314     };
11315
11316     if(config && config.data){
11317         this.inlineData = config.data;
11318         delete config.data;
11319     }
11320
11321     Roo.apply(this, config);
11322     
11323     if(this.reader){ // reader passed
11324         this.reader = Roo.factory(this.reader, Roo.data);
11325         this.reader.xmodule = this.xmodule || false;
11326         if(!this.recordType){
11327             this.recordType = this.reader.recordType;
11328         }
11329         if(this.reader.onMetaChange){
11330             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11331         }
11332     }
11333
11334     if(this.recordType){
11335         this.fields = this.recordType.prototype.fields;
11336     }
11337     this.modified = [];
11338
11339     this.addEvents({
11340         /**
11341          * @event datachanged
11342          * Fires when the data cache has changed, and a widget which is using this Store
11343          * as a Record cache should refresh its view.
11344          * @param {Store} this
11345          */
11346         datachanged : true,
11347         /**
11348          * @event metachange
11349          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11350          * @param {Store} this
11351          * @param {Object} meta The JSON metadata
11352          */
11353         metachange : true,
11354         /**
11355          * @event add
11356          * Fires when Records have been added to the Store
11357          * @param {Store} this
11358          * @param {Roo.data.Record[]} records The array of Records added
11359          * @param {Number} index The index at which the record(s) were added
11360          */
11361         add : true,
11362         /**
11363          * @event remove
11364          * Fires when a Record has been removed from the Store
11365          * @param {Store} this
11366          * @param {Roo.data.Record} record The Record that was removed
11367          * @param {Number} index The index at which the record was removed
11368          */
11369         remove : true,
11370         /**
11371          * @event update
11372          * Fires when a Record has been updated
11373          * @param {Store} this
11374          * @param {Roo.data.Record} record The Record that was updated
11375          * @param {String} operation The update operation being performed.  Value may be one of:
11376          * <pre><code>
11377  Roo.data.Record.EDIT
11378  Roo.data.Record.REJECT
11379  Roo.data.Record.COMMIT
11380          * </code></pre>
11381          */
11382         update : true,
11383         /**
11384          * @event clear
11385          * Fires when the data cache has been cleared.
11386          * @param {Store} this
11387          */
11388         clear : true,
11389         /**
11390          * @event beforeload
11391          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11392          * the load action will be canceled.
11393          * @param {Store} this
11394          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11395          */
11396         beforeload : true,
11397         /**
11398          * @event beforeloadadd
11399          * Fires after a new set of Records has been loaded.
11400          * @param {Store} this
11401          * @param {Roo.data.Record[]} records The Records that were loaded
11402          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11403          */
11404         beforeloadadd : true,
11405         /**
11406          * @event load
11407          * Fires after a new set of Records has been loaded, before they are added to the store.
11408          * @param {Store} this
11409          * @param {Roo.data.Record[]} records The Records that were loaded
11410          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11411          * @params {Object} return from reader
11412          */
11413         load : true,
11414         /**
11415          * @event loadexception
11416          * Fires if an exception occurs in the Proxy during loading.
11417          * Called with the signature of the Proxy's "loadexception" event.
11418          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11419          * 
11420          * @param {Proxy} 
11421          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11422          * @param {Object} load options 
11423          * @param {Object} jsonData from your request (normally this contains the Exception)
11424          */
11425         loadexception : true
11426     });
11427     
11428     if(this.proxy){
11429         this.proxy = Roo.factory(this.proxy, Roo.data);
11430         this.proxy.xmodule = this.xmodule || false;
11431         this.relayEvents(this.proxy,  ["loadexception"]);
11432     }
11433     this.sortToggle = {};
11434     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11435
11436     Roo.data.Store.superclass.constructor.call(this);
11437
11438     if(this.inlineData){
11439         this.loadData(this.inlineData);
11440         delete this.inlineData;
11441     }
11442 };
11443
11444 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11445      /**
11446     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11447     * without a remote query - used by combo/forms at present.
11448     */
11449     
11450     /**
11451     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11452     */
11453     /**
11454     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11455     */
11456     /**
11457     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11458     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11459     */
11460     /**
11461     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11462     * on any HTTP request
11463     */
11464     /**
11465     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11466     */
11467     /**
11468     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11469     */
11470     multiSort: false,
11471     /**
11472     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11473     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11474     */
11475     remoteSort : false,
11476
11477     /**
11478     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11479      * loaded or when a record is removed. (defaults to false).
11480     */
11481     pruneModifiedRecords : false,
11482
11483     // private
11484     lastOptions : null,
11485
11486     /**
11487      * Add Records to the Store and fires the add event.
11488      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11489      */
11490     add : function(records){
11491         records = [].concat(records);
11492         for(var i = 0, len = records.length; i < len; i++){
11493             records[i].join(this);
11494         }
11495         var index = this.data.length;
11496         this.data.addAll(records);
11497         this.fireEvent("add", this, records, index);
11498     },
11499
11500     /**
11501      * Remove a Record from the Store and fires the remove event.
11502      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11503      */
11504     remove : function(record){
11505         var index = this.data.indexOf(record);
11506         this.data.removeAt(index);
11507  
11508         if(this.pruneModifiedRecords){
11509             this.modified.remove(record);
11510         }
11511         this.fireEvent("remove", this, record, index);
11512     },
11513
11514     /**
11515      * Remove all Records from the Store and fires the clear event.
11516      */
11517     removeAll : function(){
11518         this.data.clear();
11519         if(this.pruneModifiedRecords){
11520             this.modified = [];
11521         }
11522         this.fireEvent("clear", this);
11523     },
11524
11525     /**
11526      * Inserts Records to the Store at the given index and fires the add event.
11527      * @param {Number} index The start index at which to insert the passed Records.
11528      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11529      */
11530     insert : function(index, records){
11531         records = [].concat(records);
11532         for(var i = 0, len = records.length; i < len; i++){
11533             this.data.insert(index, records[i]);
11534             records[i].join(this);
11535         }
11536         this.fireEvent("add", this, records, index);
11537     },
11538
11539     /**
11540      * Get the index within the cache of the passed Record.
11541      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11542      * @return {Number} The index of the passed Record. Returns -1 if not found.
11543      */
11544     indexOf : function(record){
11545         return this.data.indexOf(record);
11546     },
11547
11548     /**
11549      * Get the index within the cache of the Record with the passed id.
11550      * @param {String} id The id of the Record to find.
11551      * @return {Number} The index of the Record. Returns -1 if not found.
11552      */
11553     indexOfId : function(id){
11554         return this.data.indexOfKey(id);
11555     },
11556
11557     /**
11558      * Get the Record with the specified id.
11559      * @param {String} id The id of the Record to find.
11560      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11561      */
11562     getById : function(id){
11563         return this.data.key(id);
11564     },
11565
11566     /**
11567      * Get the Record at the specified index.
11568      * @param {Number} index The index of the Record to find.
11569      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11570      */
11571     getAt : function(index){
11572         return this.data.itemAt(index);
11573     },
11574
11575     /**
11576      * Returns a range of Records between specified indices.
11577      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11578      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11579      * @return {Roo.data.Record[]} An array of Records
11580      */
11581     getRange : function(start, end){
11582         return this.data.getRange(start, end);
11583     },
11584
11585     // private
11586     storeOptions : function(o){
11587         o = Roo.apply({}, o);
11588         delete o.callback;
11589         delete o.scope;
11590         this.lastOptions = o;
11591     },
11592
11593     /**
11594      * Loads the Record cache from the configured Proxy using the configured Reader.
11595      * <p>
11596      * If using remote paging, then the first load call must specify the <em>start</em>
11597      * and <em>limit</em> properties in the options.params property to establish the initial
11598      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11599      * <p>
11600      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11601      * and this call will return before the new data has been loaded. Perform any post-processing
11602      * in a callback function, or in a "load" event handler.</strong>
11603      * <p>
11604      * @param {Object} options An object containing properties which control loading options:<ul>
11605      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11606      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11607      * passed the following arguments:<ul>
11608      * <li>r : Roo.data.Record[]</li>
11609      * <li>options: Options object from the load call</li>
11610      * <li>success: Boolean success indicator</li></ul></li>
11611      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11612      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11613      * </ul>
11614      */
11615     load : function(options){
11616         options = options || {};
11617         if(this.fireEvent("beforeload", this, options) !== false){
11618             this.storeOptions(options);
11619             var p = Roo.apply(options.params || {}, this.baseParams);
11620             // if meta was not loaded from remote source.. try requesting it.
11621             if (!this.reader.metaFromRemote) {
11622                 p._requestMeta = 1;
11623             }
11624             if(this.sortInfo && this.remoteSort){
11625                 var pn = this.paramNames;
11626                 p[pn["sort"]] = this.sortInfo.field;
11627                 p[pn["dir"]] = this.sortInfo.direction;
11628             }
11629             if (this.multiSort) {
11630                 var pn = this.paramNames;
11631                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11632             }
11633             
11634             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11635         }
11636     },
11637
11638     /**
11639      * Reloads the Record cache from the configured Proxy using the configured Reader and
11640      * the options from the last load operation performed.
11641      * @param {Object} options (optional) An object containing properties which may override the options
11642      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11643      * the most recently used options are reused).
11644      */
11645     reload : function(options){
11646         this.load(Roo.applyIf(options||{}, this.lastOptions));
11647     },
11648
11649     // private
11650     // Called as a callback by the Reader during a load operation.
11651     loadRecords : function(o, options, success){
11652         if(!o || success === false){
11653             if(success !== false){
11654                 this.fireEvent("load", this, [], options, o);
11655             }
11656             if(options.callback){
11657                 options.callback.call(options.scope || this, [], options, false);
11658             }
11659             return;
11660         }
11661         // if data returned failure - throw an exception.
11662         if (o.success === false) {
11663             // show a message if no listener is registered.
11664             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11665                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11666             }
11667             // loadmask wil be hooked into this..
11668             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11669             return;
11670         }
11671         var r = o.records, t = o.totalRecords || r.length;
11672         
11673         this.fireEvent("beforeloadadd", this, r, options, o);
11674         
11675         if(!options || options.add !== true){
11676             if(this.pruneModifiedRecords){
11677                 this.modified = [];
11678             }
11679             for(var i = 0, len = r.length; i < len; i++){
11680                 r[i].join(this);
11681             }
11682             if(this.snapshot){
11683                 this.data = this.snapshot;
11684                 delete this.snapshot;
11685             }
11686             this.data.clear();
11687             this.data.addAll(r);
11688             this.totalLength = t;
11689             this.applySort();
11690             this.fireEvent("datachanged", this);
11691         }else{
11692             this.totalLength = Math.max(t, this.data.length+r.length);
11693             this.add(r);
11694         }
11695         
11696         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11697                 
11698             var e = new Roo.data.Record({});
11699
11700             e.set(this.parent.displayField, this.parent.emptyTitle);
11701             e.set(this.parent.valueField, '');
11702
11703             this.insert(0, e);
11704         }
11705             
11706         this.fireEvent("load", this, r, options, o);
11707         if(options.callback){
11708             options.callback.call(options.scope || this, r, options, true);
11709         }
11710     },
11711
11712
11713     /**
11714      * Loads data from a passed data block. A Reader which understands the format of the data
11715      * must have been configured in the constructor.
11716      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11717      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11718      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11719      */
11720     loadData : function(o, append){
11721         var r = this.reader.readRecords(o);
11722         this.loadRecords(r, {add: append}, true);
11723     },
11724
11725     /**
11726      * Gets the number of cached records.
11727      * <p>
11728      * <em>If using paging, this may not be the total size of the dataset. If the data object
11729      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11730      * the data set size</em>
11731      */
11732     getCount : function(){
11733         return this.data.length || 0;
11734     },
11735
11736     /**
11737      * Gets the total number of records in the dataset as returned by the server.
11738      * <p>
11739      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11740      * the dataset size</em>
11741      */
11742     getTotalCount : function(){
11743         return this.totalLength || 0;
11744     },
11745
11746     /**
11747      * Returns the sort state of the Store as an object with two properties:
11748      * <pre><code>
11749  field {String} The name of the field by which the Records are sorted
11750  direction {String} The sort order, "ASC" or "DESC"
11751      * </code></pre>
11752      */
11753     getSortState : function(){
11754         return this.sortInfo;
11755     },
11756
11757     // private
11758     applySort : function(){
11759         if(this.sortInfo && !this.remoteSort){
11760             var s = this.sortInfo, f = s.field;
11761             var st = this.fields.get(f).sortType;
11762             var fn = function(r1, r2){
11763                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11764                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11765             };
11766             this.data.sort(s.direction, fn);
11767             if(this.snapshot && this.snapshot != this.data){
11768                 this.snapshot.sort(s.direction, fn);
11769             }
11770         }
11771     },
11772
11773     /**
11774      * Sets the default sort column and order to be used by the next load operation.
11775      * @param {String} fieldName The name of the field to sort by.
11776      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11777      */
11778     setDefaultSort : function(field, dir){
11779         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11780     },
11781
11782     /**
11783      * Sort the Records.
11784      * If remote sorting is used, the sort is performed on the server, and the cache is
11785      * reloaded. If local sorting is used, the cache is sorted internally.
11786      * @param {String} fieldName The name of the field to sort by.
11787      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11788      */
11789     sort : function(fieldName, dir){
11790         var f = this.fields.get(fieldName);
11791         if(!dir){
11792             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11793             
11794             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11795                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11796             }else{
11797                 dir = f.sortDir;
11798             }
11799         }
11800         this.sortToggle[f.name] = dir;
11801         this.sortInfo = {field: f.name, direction: dir};
11802         if(!this.remoteSort){
11803             this.applySort();
11804             this.fireEvent("datachanged", this);
11805         }else{
11806             this.load(this.lastOptions);
11807         }
11808     },
11809
11810     /**
11811      * Calls the specified function for each of the Records in the cache.
11812      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11813      * Returning <em>false</em> aborts and exits the iteration.
11814      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11815      */
11816     each : function(fn, scope){
11817         this.data.each(fn, scope);
11818     },
11819
11820     /**
11821      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11822      * (e.g., during paging).
11823      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11824      */
11825     getModifiedRecords : function(){
11826         return this.modified;
11827     },
11828
11829     // private
11830     createFilterFn : function(property, value, anyMatch){
11831         if(!value.exec){ // not a regex
11832             value = String(value);
11833             if(value.length == 0){
11834                 return false;
11835             }
11836             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11837         }
11838         return function(r){
11839             return value.test(r.data[property]);
11840         };
11841     },
11842
11843     /**
11844      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11845      * @param {String} property A field on your records
11846      * @param {Number} start The record index to start at (defaults to 0)
11847      * @param {Number} end The last record index to include (defaults to length - 1)
11848      * @return {Number} The sum
11849      */
11850     sum : function(property, start, end){
11851         var rs = this.data.items, v = 0;
11852         start = start || 0;
11853         end = (end || end === 0) ? end : rs.length-1;
11854
11855         for(var i = start; i <= end; i++){
11856             v += (rs[i].data[property] || 0);
11857         }
11858         return v;
11859     },
11860
11861     /**
11862      * Filter the records by a specified property.
11863      * @param {String} field A field on your records
11864      * @param {String/RegExp} value Either a string that the field
11865      * should start with or a RegExp to test against the field
11866      * @param {Boolean} anyMatch True to match any part not just the beginning
11867      */
11868     filter : function(property, value, anyMatch){
11869         var fn = this.createFilterFn(property, value, anyMatch);
11870         return fn ? this.filterBy(fn) : this.clearFilter();
11871     },
11872
11873     /**
11874      * Filter by a function. The specified function will be called with each
11875      * record in this data source. If the function returns true the record is included,
11876      * otherwise it is filtered.
11877      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11878      * @param {Object} scope (optional) The scope of the function (defaults to this)
11879      */
11880     filterBy : function(fn, scope){
11881         this.snapshot = this.snapshot || this.data;
11882         this.data = this.queryBy(fn, scope||this);
11883         this.fireEvent("datachanged", this);
11884     },
11885
11886     /**
11887      * Query the records by a specified property.
11888      * @param {String} field A field on your records
11889      * @param {String/RegExp} value Either a string that the field
11890      * should start with or a RegExp to test against the field
11891      * @param {Boolean} anyMatch True to match any part not just the beginning
11892      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11893      */
11894     query : function(property, value, anyMatch){
11895         var fn = this.createFilterFn(property, value, anyMatch);
11896         return fn ? this.queryBy(fn) : this.data.clone();
11897     },
11898
11899     /**
11900      * Query by a function. The specified function will be called with each
11901      * record in this data source. If the function returns true the record is included
11902      * in the results.
11903      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11904      * @param {Object} scope (optional) The scope of the function (defaults to this)
11905       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11906      **/
11907     queryBy : function(fn, scope){
11908         var data = this.snapshot || this.data;
11909         return data.filterBy(fn, scope||this);
11910     },
11911
11912     /**
11913      * Collects unique values for a particular dataIndex from this store.
11914      * @param {String} dataIndex The property to collect
11915      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11916      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11917      * @return {Array} An array of the unique values
11918      **/
11919     collect : function(dataIndex, allowNull, bypassFilter){
11920         var d = (bypassFilter === true && this.snapshot) ?
11921                 this.snapshot.items : this.data.items;
11922         var v, sv, r = [], l = {};
11923         for(var i = 0, len = d.length; i < len; i++){
11924             v = d[i].data[dataIndex];
11925             sv = String(v);
11926             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11927                 l[sv] = true;
11928                 r[r.length] = v;
11929             }
11930         }
11931         return r;
11932     },
11933
11934     /**
11935      * Revert to a view of the Record cache with no filtering applied.
11936      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11937      */
11938     clearFilter : function(suppressEvent){
11939         if(this.snapshot && this.snapshot != this.data){
11940             this.data = this.snapshot;
11941             delete this.snapshot;
11942             if(suppressEvent !== true){
11943                 this.fireEvent("datachanged", this);
11944             }
11945         }
11946     },
11947
11948     // private
11949     afterEdit : function(record){
11950         if(this.modified.indexOf(record) == -1){
11951             this.modified.push(record);
11952         }
11953         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11954     },
11955     
11956     // private
11957     afterReject : function(record){
11958         this.modified.remove(record);
11959         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11960     },
11961
11962     // private
11963     afterCommit : function(record){
11964         this.modified.remove(record);
11965         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11966     },
11967
11968     /**
11969      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11970      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11971      */
11972     commitChanges : function(){
11973         var m = this.modified.slice(0);
11974         this.modified = [];
11975         for(var i = 0, len = m.length; i < len; i++){
11976             m[i].commit();
11977         }
11978     },
11979
11980     /**
11981      * Cancel outstanding changes on all changed records.
11982      */
11983     rejectChanges : function(){
11984         var m = this.modified.slice(0);
11985         this.modified = [];
11986         for(var i = 0, len = m.length; i < len; i++){
11987             m[i].reject();
11988         }
11989     },
11990
11991     onMetaChange : function(meta, rtype, o){
11992         this.recordType = rtype;
11993         this.fields = rtype.prototype.fields;
11994         delete this.snapshot;
11995         this.sortInfo = meta.sortInfo || this.sortInfo;
11996         this.modified = [];
11997         this.fireEvent('metachange', this, this.reader.meta);
11998     },
11999     
12000     moveIndex : function(data, type)
12001     {
12002         var index = this.indexOf(data);
12003         
12004         var newIndex = index + type;
12005         
12006         this.remove(data);
12007         
12008         this.insert(newIndex, data);
12009         
12010     }
12011 });/*
12012  * Based on:
12013  * Ext JS Library 1.1.1
12014  * Copyright(c) 2006-2007, Ext JS, LLC.
12015  *
12016  * Originally Released Under LGPL - original licence link has changed is not relivant.
12017  *
12018  * Fork - LGPL
12019  * <script type="text/javascript">
12020  */
12021
12022 /**
12023  * @class Roo.data.SimpleStore
12024  * @extends Roo.data.Store
12025  * Small helper class to make creating Stores from Array data easier.
12026  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12027  * @cfg {Array} fields An array of field definition objects, or field name strings.
12028  * @cfg {Array} data The multi-dimensional array of data
12029  * @constructor
12030  * @param {Object} config
12031  */
12032 Roo.data.SimpleStore = function(config){
12033     Roo.data.SimpleStore.superclass.constructor.call(this, {
12034         isLocal : true,
12035         reader: new Roo.data.ArrayReader({
12036                 id: config.id
12037             },
12038             Roo.data.Record.create(config.fields)
12039         ),
12040         proxy : new Roo.data.MemoryProxy(config.data)
12041     });
12042     this.load();
12043 };
12044 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12045  * Based on:
12046  * Ext JS Library 1.1.1
12047  * Copyright(c) 2006-2007, Ext JS, LLC.
12048  *
12049  * Originally Released Under LGPL - original licence link has changed is not relivant.
12050  *
12051  * Fork - LGPL
12052  * <script type="text/javascript">
12053  */
12054
12055 /**
12056 /**
12057  * @extends Roo.data.Store
12058  * @class Roo.data.JsonStore
12059  * Small helper class to make creating Stores for JSON data easier. <br/>
12060 <pre><code>
12061 var store = new Roo.data.JsonStore({
12062     url: 'get-images.php',
12063     root: 'images',
12064     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12065 });
12066 </code></pre>
12067  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12068  * JsonReader and HttpProxy (unless inline data is provided).</b>
12069  * @cfg {Array} fields An array of field definition objects, or field name strings.
12070  * @constructor
12071  * @param {Object} config
12072  */
12073 Roo.data.JsonStore = function(c){
12074     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12075         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12076         reader: new Roo.data.JsonReader(c, c.fields)
12077     }));
12078 };
12079 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12080  * Based on:
12081  * Ext JS Library 1.1.1
12082  * Copyright(c) 2006-2007, Ext JS, LLC.
12083  *
12084  * Originally Released Under LGPL - original licence link has changed is not relivant.
12085  *
12086  * Fork - LGPL
12087  * <script type="text/javascript">
12088  */
12089
12090  
12091 Roo.data.Field = function(config){
12092     if(typeof config == "string"){
12093         config = {name: config};
12094     }
12095     Roo.apply(this, config);
12096     
12097     if(!this.type){
12098         this.type = "auto";
12099     }
12100     
12101     var st = Roo.data.SortTypes;
12102     // named sortTypes are supported, here we look them up
12103     if(typeof this.sortType == "string"){
12104         this.sortType = st[this.sortType];
12105     }
12106     
12107     // set default sortType for strings and dates
12108     if(!this.sortType){
12109         switch(this.type){
12110             case "string":
12111                 this.sortType = st.asUCString;
12112                 break;
12113             case "date":
12114                 this.sortType = st.asDate;
12115                 break;
12116             default:
12117                 this.sortType = st.none;
12118         }
12119     }
12120
12121     // define once
12122     var stripRe = /[\$,%]/g;
12123
12124     // prebuilt conversion function for this field, instead of
12125     // switching every time we're reading a value
12126     if(!this.convert){
12127         var cv, dateFormat = this.dateFormat;
12128         switch(this.type){
12129             case "":
12130             case "auto":
12131             case undefined:
12132                 cv = function(v){ return v; };
12133                 break;
12134             case "string":
12135                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12136                 break;
12137             case "int":
12138                 cv = function(v){
12139                     return v !== undefined && v !== null && v !== '' ?
12140                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12141                     };
12142                 break;
12143             case "float":
12144                 cv = function(v){
12145                     return v !== undefined && v !== null && v !== '' ?
12146                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12147                     };
12148                 break;
12149             case "bool":
12150             case "boolean":
12151                 cv = function(v){ return v === true || v === "true" || v == 1; };
12152                 break;
12153             case "date":
12154                 cv = function(v){
12155                     if(!v){
12156                         return '';
12157                     }
12158                     if(v instanceof Date){
12159                         return v;
12160                     }
12161                     if(dateFormat){
12162                         if(dateFormat == "timestamp"){
12163                             return new Date(v*1000);
12164                         }
12165                         return Date.parseDate(v, dateFormat);
12166                     }
12167                     var parsed = Date.parse(v);
12168                     return parsed ? new Date(parsed) : null;
12169                 };
12170              break;
12171             
12172         }
12173         this.convert = cv;
12174     }
12175 };
12176
12177 Roo.data.Field.prototype = {
12178     dateFormat: null,
12179     defaultValue: "",
12180     mapping: null,
12181     sortType : null,
12182     sortDir : "ASC"
12183 };/*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193  
12194 // Base class for reading structured data from a data source.  This class is intended to be
12195 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12196
12197 /**
12198  * @class Roo.data.DataReader
12199  * Base class for reading structured data from a data source.  This class is intended to be
12200  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12201  */
12202
12203 Roo.data.DataReader = function(meta, recordType){
12204     
12205     this.meta = meta;
12206     
12207     this.recordType = recordType instanceof Array ? 
12208         Roo.data.Record.create(recordType) : recordType;
12209 };
12210
12211 Roo.data.DataReader.prototype = {
12212      /**
12213      * Create an empty record
12214      * @param {Object} data (optional) - overlay some values
12215      * @return {Roo.data.Record} record created.
12216      */
12217     newRow :  function(d) {
12218         var da =  {};
12219         this.recordType.prototype.fields.each(function(c) {
12220             switch( c.type) {
12221                 case 'int' : da[c.name] = 0; break;
12222                 case 'date' : da[c.name] = new Date(); break;
12223                 case 'float' : da[c.name] = 0.0; break;
12224                 case 'boolean' : da[c.name] = false; break;
12225                 default : da[c.name] = ""; break;
12226             }
12227             
12228         });
12229         return new this.recordType(Roo.apply(da, d));
12230     }
12231     
12232 };/*
12233  * Based on:
12234  * Ext JS Library 1.1.1
12235  * Copyright(c) 2006-2007, Ext JS, LLC.
12236  *
12237  * Originally Released Under LGPL - original licence link has changed is not relivant.
12238  *
12239  * Fork - LGPL
12240  * <script type="text/javascript">
12241  */
12242
12243 /**
12244  * @class Roo.data.DataProxy
12245  * @extends Roo.data.Observable
12246  * This class is an abstract base class for implementations which provide retrieval of
12247  * unformatted data objects.<br>
12248  * <p>
12249  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12250  * (of the appropriate type which knows how to parse the data object) to provide a block of
12251  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12252  * <p>
12253  * Custom implementations must implement the load method as described in
12254  * {@link Roo.data.HttpProxy#load}.
12255  */
12256 Roo.data.DataProxy = function(){
12257     this.addEvents({
12258         /**
12259          * @event beforeload
12260          * Fires before a network request is made to retrieve a data object.
12261          * @param {Object} This DataProxy object.
12262          * @param {Object} params The params parameter to the load function.
12263          */
12264         beforeload : true,
12265         /**
12266          * @event load
12267          * Fires before the load method's callback is called.
12268          * @param {Object} This DataProxy object.
12269          * @param {Object} o The data object.
12270          * @param {Object} arg The callback argument object passed to the load function.
12271          */
12272         load : true,
12273         /**
12274          * @event loadexception
12275          * Fires if an Exception occurs during data retrieval.
12276          * @param {Object} This DataProxy object.
12277          * @param {Object} o The data object.
12278          * @param {Object} arg The callback argument object passed to the load function.
12279          * @param {Object} e The Exception.
12280          */
12281         loadexception : true
12282     });
12283     Roo.data.DataProxy.superclass.constructor.call(this);
12284 };
12285
12286 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12287
12288     /**
12289      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12290      */
12291 /*
12292  * Based on:
12293  * Ext JS Library 1.1.1
12294  * Copyright(c) 2006-2007, Ext JS, LLC.
12295  *
12296  * Originally Released Under LGPL - original licence link has changed is not relivant.
12297  *
12298  * Fork - LGPL
12299  * <script type="text/javascript">
12300  */
12301 /**
12302  * @class Roo.data.MemoryProxy
12303  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12304  * to the Reader when its load method is called.
12305  * @constructor
12306  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12307  */
12308 Roo.data.MemoryProxy = function(data){
12309     if (data.data) {
12310         data = data.data;
12311     }
12312     Roo.data.MemoryProxy.superclass.constructor.call(this);
12313     this.data = data;
12314 };
12315
12316 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12317     
12318     /**
12319      * Load data from the requested source (in this case an in-memory
12320      * data object passed to the constructor), read the data object into
12321      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12322      * process that block using the passed callback.
12323      * @param {Object} params This parameter is not used by the MemoryProxy class.
12324      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12325      * object into a block of Roo.data.Records.
12326      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12327      * The function must be passed <ul>
12328      * <li>The Record block object</li>
12329      * <li>The "arg" argument from the load function</li>
12330      * <li>A boolean success indicator</li>
12331      * </ul>
12332      * @param {Object} scope The scope in which to call the callback
12333      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12334      */
12335     load : function(params, reader, callback, scope, arg){
12336         params = params || {};
12337         var result;
12338         try {
12339             result = reader.readRecords(this.data);
12340         }catch(e){
12341             this.fireEvent("loadexception", this, arg, null, e);
12342             callback.call(scope, null, arg, false);
12343             return;
12344         }
12345         callback.call(scope, result, arg, true);
12346     },
12347     
12348     // private
12349     update : function(params, records){
12350         
12351     }
12352 });/*
12353  * Based on:
12354  * Ext JS Library 1.1.1
12355  * Copyright(c) 2006-2007, Ext JS, LLC.
12356  *
12357  * Originally Released Under LGPL - original licence link has changed is not relivant.
12358  *
12359  * Fork - LGPL
12360  * <script type="text/javascript">
12361  */
12362 /**
12363  * @class Roo.data.HttpProxy
12364  * @extends Roo.data.DataProxy
12365  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12366  * configured to reference a certain URL.<br><br>
12367  * <p>
12368  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12369  * from which the running page was served.<br><br>
12370  * <p>
12371  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12372  * <p>
12373  * Be aware that to enable the browser to parse an XML document, the server must set
12374  * the Content-Type header in the HTTP response to "text/xml".
12375  * @constructor
12376  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12377  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12378  * will be used to make the request.
12379  */
12380 Roo.data.HttpProxy = function(conn){
12381     Roo.data.HttpProxy.superclass.constructor.call(this);
12382     // is conn a conn config or a real conn?
12383     this.conn = conn;
12384     this.useAjax = !conn || !conn.events;
12385   
12386 };
12387
12388 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12389     // thse are take from connection...
12390     
12391     /**
12392      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12393      */
12394     /**
12395      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12396      * extra parameters to each request made by this object. (defaults to undefined)
12397      */
12398     /**
12399      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12400      *  to each request made by this object. (defaults to undefined)
12401      */
12402     /**
12403      * @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)
12404      */
12405     /**
12406      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12407      */
12408      /**
12409      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12410      * @type Boolean
12411      */
12412   
12413
12414     /**
12415      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12416      * @type Boolean
12417      */
12418     /**
12419      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12420      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12421      * a finer-grained basis than the DataProxy events.
12422      */
12423     getConnection : function(){
12424         return this.useAjax ? Roo.Ajax : this.conn;
12425     },
12426
12427     /**
12428      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12429      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12430      * process that block using the passed callback.
12431      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12432      * for the request to the remote server.
12433      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12434      * object into a block of Roo.data.Records.
12435      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12436      * The function must be passed <ul>
12437      * <li>The Record block object</li>
12438      * <li>The "arg" argument from the load function</li>
12439      * <li>A boolean success indicator</li>
12440      * </ul>
12441      * @param {Object} scope The scope in which to call the callback
12442      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12443      */
12444     load : function(params, reader, callback, scope, arg){
12445         if(this.fireEvent("beforeload", this, params) !== false){
12446             var  o = {
12447                 params : params || {},
12448                 request: {
12449                     callback : callback,
12450                     scope : scope,
12451                     arg : arg
12452                 },
12453                 reader: reader,
12454                 callback : this.loadResponse,
12455                 scope: this
12456             };
12457             if(this.useAjax){
12458                 Roo.applyIf(o, this.conn);
12459                 if(this.activeRequest){
12460                     Roo.Ajax.abort(this.activeRequest);
12461                 }
12462                 this.activeRequest = Roo.Ajax.request(o);
12463             }else{
12464                 this.conn.request(o);
12465             }
12466         }else{
12467             callback.call(scope||this, null, arg, false);
12468         }
12469     },
12470
12471     // private
12472     loadResponse : function(o, success, response){
12473         delete this.activeRequest;
12474         if(!success){
12475             this.fireEvent("loadexception", this, o, response);
12476             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12477             return;
12478         }
12479         var result;
12480         try {
12481             result = o.reader.read(response);
12482         }catch(e){
12483             this.fireEvent("loadexception", this, o, response, e);
12484             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12485             return;
12486         }
12487         
12488         this.fireEvent("load", this, o, o.request.arg);
12489         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12490     },
12491
12492     // private
12493     update : function(dataSet){
12494
12495     },
12496
12497     // private
12498     updateResponse : function(dataSet){
12499
12500     }
12501 });/*
12502  * Based on:
12503  * Ext JS Library 1.1.1
12504  * Copyright(c) 2006-2007, Ext JS, LLC.
12505  *
12506  * Originally Released Under LGPL - original licence link has changed is not relivant.
12507  *
12508  * Fork - LGPL
12509  * <script type="text/javascript">
12510  */
12511
12512 /**
12513  * @class Roo.data.ScriptTagProxy
12514  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12515  * other than the originating domain of the running page.<br><br>
12516  * <p>
12517  * <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
12518  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12519  * <p>
12520  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12521  * source code that is used as the source inside a &lt;script> tag.<br><br>
12522  * <p>
12523  * In order for the browser to process the returned data, the server must wrap the data object
12524  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12525  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12526  * depending on whether the callback name was passed:
12527  * <p>
12528  * <pre><code>
12529 boolean scriptTag = false;
12530 String cb = request.getParameter("callback");
12531 if (cb != null) {
12532     scriptTag = true;
12533     response.setContentType("text/javascript");
12534 } else {
12535     response.setContentType("application/x-json");
12536 }
12537 Writer out = response.getWriter();
12538 if (scriptTag) {
12539     out.write(cb + "(");
12540 }
12541 out.print(dataBlock.toJsonString());
12542 if (scriptTag) {
12543     out.write(");");
12544 }
12545 </pre></code>
12546  *
12547  * @constructor
12548  * @param {Object} config A configuration object.
12549  */
12550 Roo.data.ScriptTagProxy = function(config){
12551     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12552     Roo.apply(this, config);
12553     this.head = document.getElementsByTagName("head")[0];
12554 };
12555
12556 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12557
12558 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12559     /**
12560      * @cfg {String} url The URL from which to request the data object.
12561      */
12562     /**
12563      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12564      */
12565     timeout : 30000,
12566     /**
12567      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12568      * the server the name of the callback function set up by the load call to process the returned data object.
12569      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12570      * javascript output which calls this named function passing the data object as its only parameter.
12571      */
12572     callbackParam : "callback",
12573     /**
12574      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12575      * name to the request.
12576      */
12577     nocache : true,
12578
12579     /**
12580      * Load data from the configured URL, read the data object into
12581      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12582      * process that block using the passed callback.
12583      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12584      * for the request to the remote server.
12585      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12586      * object into a block of Roo.data.Records.
12587      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12588      * The function must be passed <ul>
12589      * <li>The Record block object</li>
12590      * <li>The "arg" argument from the load function</li>
12591      * <li>A boolean success indicator</li>
12592      * </ul>
12593      * @param {Object} scope The scope in which to call the callback
12594      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12595      */
12596     load : function(params, reader, callback, scope, arg){
12597         if(this.fireEvent("beforeload", this, params) !== false){
12598
12599             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12600
12601             var url = this.url;
12602             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12603             if(this.nocache){
12604                 url += "&_dc=" + (new Date().getTime());
12605             }
12606             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12607             var trans = {
12608                 id : transId,
12609                 cb : "stcCallback"+transId,
12610                 scriptId : "stcScript"+transId,
12611                 params : params,
12612                 arg : arg,
12613                 url : url,
12614                 callback : callback,
12615                 scope : scope,
12616                 reader : reader
12617             };
12618             var conn = this;
12619
12620             window[trans.cb] = function(o){
12621                 conn.handleResponse(o, trans);
12622             };
12623
12624             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12625
12626             if(this.autoAbort !== false){
12627                 this.abort();
12628             }
12629
12630             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12631
12632             var script = document.createElement("script");
12633             script.setAttribute("src", url);
12634             script.setAttribute("type", "text/javascript");
12635             script.setAttribute("id", trans.scriptId);
12636             this.head.appendChild(script);
12637
12638             this.trans = trans;
12639         }else{
12640             callback.call(scope||this, null, arg, false);
12641         }
12642     },
12643
12644     // private
12645     isLoading : function(){
12646         return this.trans ? true : false;
12647     },
12648
12649     /**
12650      * Abort the current server request.
12651      */
12652     abort : function(){
12653         if(this.isLoading()){
12654             this.destroyTrans(this.trans);
12655         }
12656     },
12657
12658     // private
12659     destroyTrans : function(trans, isLoaded){
12660         this.head.removeChild(document.getElementById(trans.scriptId));
12661         clearTimeout(trans.timeoutId);
12662         if(isLoaded){
12663             window[trans.cb] = undefined;
12664             try{
12665                 delete window[trans.cb];
12666             }catch(e){}
12667         }else{
12668             // if hasn't been loaded, wait for load to remove it to prevent script error
12669             window[trans.cb] = function(){
12670                 window[trans.cb] = undefined;
12671                 try{
12672                     delete window[trans.cb];
12673                 }catch(e){}
12674             };
12675         }
12676     },
12677
12678     // private
12679     handleResponse : function(o, trans){
12680         this.trans = false;
12681         this.destroyTrans(trans, true);
12682         var result;
12683         try {
12684             result = trans.reader.readRecords(o);
12685         }catch(e){
12686             this.fireEvent("loadexception", this, o, trans.arg, e);
12687             trans.callback.call(trans.scope||window, null, trans.arg, false);
12688             return;
12689         }
12690         this.fireEvent("load", this, o, trans.arg);
12691         trans.callback.call(trans.scope||window, result, trans.arg, true);
12692     },
12693
12694     // private
12695     handleFailure : function(trans){
12696         this.trans = false;
12697         this.destroyTrans(trans, false);
12698         this.fireEvent("loadexception", this, null, trans.arg);
12699         trans.callback.call(trans.scope||window, null, trans.arg, false);
12700     }
12701 });/*
12702  * Based on:
12703  * Ext JS Library 1.1.1
12704  * Copyright(c) 2006-2007, Ext JS, LLC.
12705  *
12706  * Originally Released Under LGPL - original licence link has changed is not relivant.
12707  *
12708  * Fork - LGPL
12709  * <script type="text/javascript">
12710  */
12711
12712 /**
12713  * @class Roo.data.JsonReader
12714  * @extends Roo.data.DataReader
12715  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12716  * based on mappings in a provided Roo.data.Record constructor.
12717  * 
12718  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12719  * in the reply previously. 
12720  * 
12721  * <p>
12722  * Example code:
12723  * <pre><code>
12724 var RecordDef = Roo.data.Record.create([
12725     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12726     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12727 ]);
12728 var myReader = new Roo.data.JsonReader({
12729     totalProperty: "results",    // The property which contains the total dataset size (optional)
12730     root: "rows",                // The property which contains an Array of row objects
12731     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12732 }, RecordDef);
12733 </code></pre>
12734  * <p>
12735  * This would consume a JSON file like this:
12736  * <pre><code>
12737 { 'results': 2, 'rows': [
12738     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12739     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12740 }
12741 </code></pre>
12742  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12743  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12744  * paged from the remote server.
12745  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12746  * @cfg {String} root name of the property which contains the Array of row objects.
12747  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12748  * @cfg {Array} fields Array of field definition objects
12749  * @constructor
12750  * Create a new JsonReader
12751  * @param {Object} meta Metadata configuration options
12752  * @param {Object} recordType Either an Array of field definition objects,
12753  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12754  */
12755 Roo.data.JsonReader = function(meta, recordType){
12756     
12757     meta = meta || {};
12758     // set some defaults:
12759     Roo.applyIf(meta, {
12760         totalProperty: 'total',
12761         successProperty : 'success',
12762         root : 'data',
12763         id : 'id'
12764     });
12765     
12766     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12767 };
12768 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12769     
12770     /**
12771      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12772      * Used by Store query builder to append _requestMeta to params.
12773      * 
12774      */
12775     metaFromRemote : false,
12776     /**
12777      * This method is only used by a DataProxy which has retrieved data from a remote server.
12778      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12779      * @return {Object} data A data block which is used by an Roo.data.Store object as
12780      * a cache of Roo.data.Records.
12781      */
12782     read : function(response){
12783         var json = response.responseText;
12784        
12785         var o = /* eval:var:o */ eval("("+json+")");
12786         if(!o) {
12787             throw {message: "JsonReader.read: Json object not found"};
12788         }
12789         
12790         if(o.metaData){
12791             
12792             delete this.ef;
12793             this.metaFromRemote = true;
12794             this.meta = o.metaData;
12795             this.recordType = Roo.data.Record.create(o.metaData.fields);
12796             this.onMetaChange(this.meta, this.recordType, o);
12797         }
12798         return this.readRecords(o);
12799     },
12800
12801     // private function a store will implement
12802     onMetaChange : function(meta, recordType, o){
12803
12804     },
12805
12806     /**
12807          * @ignore
12808          */
12809     simpleAccess: function(obj, subsc) {
12810         return obj[subsc];
12811     },
12812
12813         /**
12814          * @ignore
12815          */
12816     getJsonAccessor: function(){
12817         var re = /[\[\.]/;
12818         return function(expr) {
12819             try {
12820                 return(re.test(expr))
12821                     ? new Function("obj", "return obj." + expr)
12822                     : function(obj){
12823                         return obj[expr];
12824                     };
12825             } catch(e){}
12826             return Roo.emptyFn;
12827         };
12828     }(),
12829
12830     /**
12831      * Create a data block containing Roo.data.Records from an XML document.
12832      * @param {Object} o An object which contains an Array of row objects in the property specified
12833      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12834      * which contains the total size of the dataset.
12835      * @return {Object} data A data block which is used by an Roo.data.Store object as
12836      * a cache of Roo.data.Records.
12837      */
12838     readRecords : function(o){
12839         /**
12840          * After any data loads, the raw JSON data is available for further custom processing.
12841          * @type Object
12842          */
12843         this.o = o;
12844         var s = this.meta, Record = this.recordType,
12845             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12846
12847 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12848         if (!this.ef) {
12849             if(s.totalProperty) {
12850                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12851                 }
12852                 if(s.successProperty) {
12853                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12854                 }
12855                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12856                 if (s.id) {
12857                         var g = this.getJsonAccessor(s.id);
12858                         this.getId = function(rec) {
12859                                 var r = g(rec);  
12860                                 return (r === undefined || r === "") ? null : r;
12861                         };
12862                 } else {
12863                         this.getId = function(){return null;};
12864                 }
12865             this.ef = [];
12866             for(var jj = 0; jj < fl; jj++){
12867                 f = fi[jj];
12868                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12869                 this.ef[jj] = this.getJsonAccessor(map);
12870             }
12871         }
12872
12873         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12874         if(s.totalProperty){
12875             var vt = parseInt(this.getTotal(o), 10);
12876             if(!isNaN(vt)){
12877                 totalRecords = vt;
12878             }
12879         }
12880         if(s.successProperty){
12881             var vs = this.getSuccess(o);
12882             if(vs === false || vs === 'false'){
12883                 success = false;
12884             }
12885         }
12886         var records = [];
12887         for(var i = 0; i < c; i++){
12888                 var n = root[i];
12889             var values = {};
12890             var id = this.getId(n);
12891             for(var j = 0; j < fl; j++){
12892                 f = fi[j];
12893             var v = this.ef[j](n);
12894             if (!f.convert) {
12895                 Roo.log('missing convert for ' + f.name);
12896                 Roo.log(f);
12897                 continue;
12898             }
12899             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12900             }
12901             var record = new Record(values, id);
12902             record.json = n;
12903             records[i] = record;
12904         }
12905         return {
12906             raw : o,
12907             success : success,
12908             records : records,
12909             totalRecords : totalRecords
12910         };
12911     }
12912 });/*
12913  * Based on:
12914  * Ext JS Library 1.1.1
12915  * Copyright(c) 2006-2007, Ext JS, LLC.
12916  *
12917  * Originally Released Under LGPL - original licence link has changed is not relivant.
12918  *
12919  * Fork - LGPL
12920  * <script type="text/javascript">
12921  */
12922
12923 /**
12924  * @class Roo.data.ArrayReader
12925  * @extends Roo.data.DataReader
12926  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12927  * Each element of that Array represents a row of data fields. The
12928  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12929  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12930  * <p>
12931  * Example code:.
12932  * <pre><code>
12933 var RecordDef = Roo.data.Record.create([
12934     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12935     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12936 ]);
12937 var myReader = new Roo.data.ArrayReader({
12938     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12939 }, RecordDef);
12940 </code></pre>
12941  * <p>
12942  * This would consume an Array like this:
12943  * <pre><code>
12944 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12945   </code></pre>
12946  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12947  * @constructor
12948  * Create a new JsonReader
12949  * @param {Object} meta Metadata configuration options.
12950  * @param {Object} recordType Either an Array of field definition objects
12951  * as specified to {@link Roo.data.Record#create},
12952  * or an {@link Roo.data.Record} object
12953  * created using {@link Roo.data.Record#create}.
12954  */
12955 Roo.data.ArrayReader = function(meta, recordType){
12956     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12957 };
12958
12959 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12960     /**
12961      * Create a data block containing Roo.data.Records from an XML document.
12962      * @param {Object} o An Array of row objects which represents the dataset.
12963      * @return {Object} data A data block which is used by an Roo.data.Store object as
12964      * a cache of Roo.data.Records.
12965      */
12966     readRecords : function(o){
12967         var sid = this.meta ? this.meta.id : null;
12968         var recordType = this.recordType, fields = recordType.prototype.fields;
12969         var records = [];
12970         var root = o;
12971             for(var i = 0; i < root.length; i++){
12972                     var n = root[i];
12973                 var values = {};
12974                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12975                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12976                 var f = fields.items[j];
12977                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12978                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12979                 v = f.convert(v);
12980                 values[f.name] = v;
12981             }
12982                 var record = new recordType(values, id);
12983                 record.json = n;
12984                 records[records.length] = record;
12985             }
12986             return {
12987                 records : records,
12988                 totalRecords : records.length
12989             };
12990     }
12991 });/*
12992  * - LGPL
12993  * * 
12994  */
12995
12996 /**
12997  * @class Roo.bootstrap.ComboBox
12998  * @extends Roo.bootstrap.TriggerField
12999  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13000  * @cfg {Boolean} append (true|false) default false
13001  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13002  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13003  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13004  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13005  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13006  * @cfg {Boolean} animate default true
13007  * @cfg {Boolean} emptyResultText only for touch device
13008  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13009  * @cfg {String} emptyTitle default ''
13010  * @constructor
13011  * Create a new ComboBox.
13012  * @param {Object} config Configuration options
13013  */
13014 Roo.bootstrap.ComboBox = function(config){
13015     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13016     this.addEvents({
13017         /**
13018          * @event expand
13019          * Fires when the dropdown list is expanded
13020         * @param {Roo.bootstrap.ComboBox} combo This combo box
13021         */
13022         'expand' : true,
13023         /**
13024          * @event collapse
13025          * Fires when the dropdown list is collapsed
13026         * @param {Roo.bootstrap.ComboBox} combo This combo box
13027         */
13028         'collapse' : true,
13029         /**
13030          * @event beforeselect
13031          * Fires before a list item is selected. Return false to cancel the selection.
13032         * @param {Roo.bootstrap.ComboBox} combo This combo box
13033         * @param {Roo.data.Record} record The data record returned from the underlying store
13034         * @param {Number} index The index of the selected item in the dropdown list
13035         */
13036         'beforeselect' : true,
13037         /**
13038          * @event select
13039          * Fires when a list item is selected
13040         * @param {Roo.bootstrap.ComboBox} combo This combo box
13041         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13042         * @param {Number} index The index of the selected item in the dropdown list
13043         */
13044         'select' : true,
13045         /**
13046          * @event beforequery
13047          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13048          * The event object passed has these properties:
13049         * @param {Roo.bootstrap.ComboBox} combo This combo box
13050         * @param {String} query The query
13051         * @param {Boolean} forceAll true to force "all" query
13052         * @param {Boolean} cancel true to cancel the query
13053         * @param {Object} e The query event object
13054         */
13055         'beforequery': true,
13056          /**
13057          * @event add
13058          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13059         * @param {Roo.bootstrap.ComboBox} combo This combo box
13060         */
13061         'add' : true,
13062         /**
13063          * @event edit
13064          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13065         * @param {Roo.bootstrap.ComboBox} combo This combo box
13066         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13067         */
13068         'edit' : true,
13069         /**
13070          * @event remove
13071          * Fires when the remove value from the combobox array
13072         * @param {Roo.bootstrap.ComboBox} combo This combo box
13073         */
13074         'remove' : true,
13075         /**
13076          * @event afterremove
13077          * Fires when the remove value from the combobox array
13078         * @param {Roo.bootstrap.ComboBox} combo This combo box
13079         */
13080         'afterremove' : true,
13081         /**
13082          * @event specialfilter
13083          * Fires when specialfilter
13084             * @param {Roo.bootstrap.ComboBox} combo This combo box
13085             */
13086         'specialfilter' : true,
13087         /**
13088          * @event tick
13089          * Fires when tick the element
13090             * @param {Roo.bootstrap.ComboBox} combo This combo box
13091             */
13092         'tick' : true,
13093         /**
13094          * @event touchviewdisplay
13095          * Fires when touch view require special display (default is using displayField)
13096             * @param {Roo.bootstrap.ComboBox} combo This combo box
13097             * @param {Object} cfg set html .
13098             */
13099         'touchviewdisplay' : true
13100         
13101     });
13102     
13103     this.item = [];
13104     this.tickItems = [];
13105     
13106     this.selectedIndex = -1;
13107     if(this.mode == 'local'){
13108         if(config.queryDelay === undefined){
13109             this.queryDelay = 10;
13110         }
13111         if(config.minChars === undefined){
13112             this.minChars = 0;
13113         }
13114     }
13115 };
13116
13117 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13118      
13119     /**
13120      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13121      * rendering into an Roo.Editor, defaults to false)
13122      */
13123     /**
13124      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13125      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13126      */
13127     /**
13128      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13129      */
13130     /**
13131      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13132      * the dropdown list (defaults to undefined, with no header element)
13133      */
13134
13135      /**
13136      * @cfg {String/Roo.Template} tpl The template to use to render the output
13137      */
13138      
13139      /**
13140      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13141      */
13142     listWidth: undefined,
13143     /**
13144      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13145      * mode = 'remote' or 'text' if mode = 'local')
13146      */
13147     displayField: undefined,
13148     
13149     /**
13150      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13151      * mode = 'remote' or 'value' if mode = 'local'). 
13152      * Note: use of a valueField requires the user make a selection
13153      * in order for a value to be mapped.
13154      */
13155     valueField: undefined,
13156     /**
13157      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13158      */
13159     modalTitle : '',
13160     
13161     /**
13162      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13163      * field's data value (defaults to the underlying DOM element's name)
13164      */
13165     hiddenName: undefined,
13166     /**
13167      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13168      */
13169     listClass: '',
13170     /**
13171      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13172      */
13173     selectedClass: 'active',
13174     
13175     /**
13176      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13177      */
13178     shadow:'sides',
13179     /**
13180      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13181      * anchor positions (defaults to 'tl-bl')
13182      */
13183     listAlign: 'tl-bl?',
13184     /**
13185      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13186      */
13187     maxHeight: 300,
13188     /**
13189      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13190      * query specified by the allQuery config option (defaults to 'query')
13191      */
13192     triggerAction: 'query',
13193     /**
13194      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13195      * (defaults to 4, does not apply if editable = false)
13196      */
13197     minChars : 4,
13198     /**
13199      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13200      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13201      */
13202     typeAhead: false,
13203     /**
13204      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13205      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13206      */
13207     queryDelay: 500,
13208     /**
13209      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13210      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13211      */
13212     pageSize: 0,
13213     /**
13214      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13215      * when editable = true (defaults to false)
13216      */
13217     selectOnFocus:false,
13218     /**
13219      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13220      */
13221     queryParam: 'query',
13222     /**
13223      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13224      * when mode = 'remote' (defaults to 'Loading...')
13225      */
13226     loadingText: 'Loading...',
13227     /**
13228      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13229      */
13230     resizable: false,
13231     /**
13232      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13233      */
13234     handleHeight : 8,
13235     /**
13236      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13237      * traditional select (defaults to true)
13238      */
13239     editable: true,
13240     /**
13241      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13242      */
13243     allQuery: '',
13244     /**
13245      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13246      */
13247     mode: 'remote',
13248     /**
13249      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13250      * listWidth has a higher value)
13251      */
13252     minListWidth : 70,
13253     /**
13254      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13255      * allow the user to set arbitrary text into the field (defaults to false)
13256      */
13257     forceSelection:false,
13258     /**
13259      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13260      * if typeAhead = true (defaults to 250)
13261      */
13262     typeAheadDelay : 250,
13263     /**
13264      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13265      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13266      */
13267     valueNotFoundText : undefined,
13268     /**
13269      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13270      */
13271     blockFocus : false,
13272     
13273     /**
13274      * @cfg {Boolean} disableClear Disable showing of clear button.
13275      */
13276     disableClear : false,
13277     /**
13278      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13279      */
13280     alwaysQuery : false,
13281     
13282     /**
13283      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13284      */
13285     multiple : false,
13286     
13287     /**
13288      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13289      */
13290     invalidClass : "has-warning",
13291     
13292     /**
13293      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13294      */
13295     validClass : "has-success",
13296     
13297     /**
13298      * @cfg {Boolean} specialFilter (true|false) special filter default false
13299      */
13300     specialFilter : false,
13301     
13302     /**
13303      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13304      */
13305     mobileTouchView : true,
13306     
13307     /**
13308      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13309      */
13310     useNativeIOS : false,
13311     
13312     /**
13313      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13314      */
13315     mobile_restrict_height : false,
13316     
13317     ios_options : false,
13318     
13319     //private
13320     addicon : false,
13321     editicon: false,
13322     
13323     page: 0,
13324     hasQuery: false,
13325     append: false,
13326     loadNext: false,
13327     autoFocus : true,
13328     tickable : false,
13329     btnPosition : 'right',
13330     triggerList : true,
13331     showToggleBtn : true,
13332     animate : true,
13333     emptyResultText: 'Empty',
13334     triggerText : 'Select',
13335     emptyTitle : '',
13336     
13337     // element that contains real text value.. (when hidden is used..)
13338     
13339     getAutoCreate : function()
13340     {   
13341         var cfg = false;
13342         //render
13343         /*
13344          * Render classic select for iso
13345          */
13346         
13347         if(Roo.isIOS && this.useNativeIOS){
13348             cfg = this.getAutoCreateNativeIOS();
13349             return cfg;
13350         }
13351         
13352         /*
13353          * Touch Devices
13354          */
13355         
13356         if(Roo.isTouch && this.mobileTouchView){
13357             cfg = this.getAutoCreateTouchView();
13358             return cfg;;
13359         }
13360         
13361         /*
13362          *  Normal ComboBox
13363          */
13364         if(!this.tickable){
13365             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13366             return cfg;
13367         }
13368         
13369         /*
13370          *  ComboBox with tickable selections
13371          */
13372              
13373         var align = this.labelAlign || this.parentLabelAlign();
13374         
13375         cfg = {
13376             cls : 'form-group roo-combobox-tickable' //input-group
13377         };
13378         
13379         var btn_text_select = '';
13380         var btn_text_done = '';
13381         var btn_text_cancel = '';
13382         
13383         if (this.btn_text_show) {
13384             btn_text_select = 'Select';
13385             btn_text_done = 'Done';
13386             btn_text_cancel = 'Cancel'; 
13387         }
13388         
13389         var buttons = {
13390             tag : 'div',
13391             cls : 'tickable-buttons',
13392             cn : [
13393                 {
13394                     tag : 'button',
13395                     type : 'button',
13396                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13397                     //html : this.triggerText
13398                     html: btn_text_select
13399                 },
13400                 {
13401                     tag : 'button',
13402                     type : 'button',
13403                     name : 'ok',
13404                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13405                     //html : 'Done'
13406                     html: btn_text_done
13407                 },
13408                 {
13409                     tag : 'button',
13410                     type : 'button',
13411                     name : 'cancel',
13412                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13413                     //html : 'Cancel'
13414                     html: btn_text_cancel
13415                 }
13416             ]
13417         };
13418         
13419         if(this.editable){
13420             buttons.cn.unshift({
13421                 tag: 'input',
13422                 cls: 'roo-select2-search-field-input'
13423             });
13424         }
13425         
13426         var _this = this;
13427         
13428         Roo.each(buttons.cn, function(c){
13429             if (_this.size) {
13430                 c.cls += ' btn-' + _this.size;
13431             }
13432
13433             if (_this.disabled) {
13434                 c.disabled = true;
13435             }
13436         });
13437         
13438         var box = {
13439             tag: 'div',
13440             style : 'display: contents',
13441             cn: [
13442                 {
13443                     tag: 'input',
13444                     type : 'hidden',
13445                     cls: 'form-hidden-field'
13446                 },
13447                 {
13448                     tag: 'ul',
13449                     cls: 'roo-select2-choices',
13450                     cn:[
13451                         {
13452                             tag: 'li',
13453                             cls: 'roo-select2-search-field',
13454                             cn: [
13455                                 buttons
13456                             ]
13457                         }
13458                     ]
13459                 }
13460             ]
13461         };
13462         
13463         var combobox = {
13464             cls: 'roo-select2-container input-group roo-select2-container-multi',
13465             cn: [
13466                 
13467                 box
13468 //                {
13469 //                    tag: 'ul',
13470 //                    cls: 'typeahead typeahead-long dropdown-menu',
13471 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13472 //                }
13473             ]
13474         };
13475         
13476         if(this.hasFeedback && !this.allowBlank){
13477             
13478             var feedback = {
13479                 tag: 'span',
13480                 cls: 'glyphicon form-control-feedback'
13481             };
13482
13483             combobox.cn.push(feedback);
13484         }
13485         
13486         var indicator = {
13487             tag : 'i',
13488             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13489             tooltip : 'This field is required'
13490         };
13491         if (Roo.bootstrap.version == 4) {
13492             indicator = {
13493                 tag : 'i',
13494                 style : 'display:none'
13495             };
13496         }
13497         if (align ==='left' && this.fieldLabel.length) {
13498             
13499             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13500             
13501             cfg.cn = [
13502                 indicator,
13503                 {
13504                     tag: 'label',
13505                     'for' :  id,
13506                     cls : 'control-label col-form-label',
13507                     html : this.fieldLabel
13508
13509                 },
13510                 {
13511                     cls : "", 
13512                     cn: [
13513                         combobox
13514                     ]
13515                 }
13516
13517             ];
13518             
13519             var labelCfg = cfg.cn[1];
13520             var contentCfg = cfg.cn[2];
13521             
13522
13523             if(this.indicatorpos == 'right'){
13524                 
13525                 cfg.cn = [
13526                     {
13527                         tag: 'label',
13528                         'for' :  id,
13529                         cls : 'control-label col-form-label',
13530                         cn : [
13531                             {
13532                                 tag : 'span',
13533                                 html : this.fieldLabel
13534                             },
13535                             indicator
13536                         ]
13537                     },
13538                     {
13539                         cls : "",
13540                         cn: [
13541                             combobox
13542                         ]
13543                     }
13544
13545                 ];
13546                 
13547                 
13548                 
13549                 labelCfg = cfg.cn[0];
13550                 contentCfg = cfg.cn[1];
13551             
13552             }
13553             
13554             if(this.labelWidth > 12){
13555                 labelCfg.style = "width: " + this.labelWidth + 'px';
13556             }
13557             
13558             if(this.labelWidth < 13 && this.labelmd == 0){
13559                 this.labelmd = this.labelWidth;
13560             }
13561             
13562             if(this.labellg > 0){
13563                 labelCfg.cls += ' col-lg-' + this.labellg;
13564                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13565             }
13566             
13567             if(this.labelmd > 0){
13568                 labelCfg.cls += ' col-md-' + this.labelmd;
13569                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13570             }
13571             
13572             if(this.labelsm > 0){
13573                 labelCfg.cls += ' col-sm-' + this.labelsm;
13574                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13575             }
13576             
13577             if(this.labelxs > 0){
13578                 labelCfg.cls += ' col-xs-' + this.labelxs;
13579                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13580             }
13581                 
13582                 
13583         } else if ( this.fieldLabel.length) {
13584 //                Roo.log(" label");
13585                  cfg.cn = [
13586                    indicator,
13587                     {
13588                         tag: 'label',
13589                         //cls : 'input-group-addon',
13590                         html : this.fieldLabel
13591                     },
13592                     combobox
13593                 ];
13594                 
13595                 if(this.indicatorpos == 'right'){
13596                     cfg.cn = [
13597                         {
13598                             tag: 'label',
13599                             //cls : 'input-group-addon',
13600                             html : this.fieldLabel
13601                         },
13602                         indicator,
13603                         combobox
13604                     ];
13605                     
13606                 }
13607
13608         } else {
13609             
13610 //                Roo.log(" no label && no align");
13611                 cfg = combobox
13612                      
13613                 
13614         }
13615          
13616         var settings=this;
13617         ['xs','sm','md','lg'].map(function(size){
13618             if (settings[size]) {
13619                 cfg.cls += ' col-' + size + '-' + settings[size];
13620             }
13621         });
13622         
13623         return cfg;
13624         
13625     },
13626     
13627     _initEventsCalled : false,
13628     
13629     // private
13630     initEvents: function()
13631     {   
13632         if (this._initEventsCalled) { // as we call render... prevent looping...
13633             return;
13634         }
13635         this._initEventsCalled = true;
13636         
13637         if (!this.store) {
13638             throw "can not find store for combo";
13639         }
13640         
13641         this.indicator = this.indicatorEl();
13642         
13643         this.store = Roo.factory(this.store, Roo.data);
13644         this.store.parent = this;
13645         
13646         // if we are building from html. then this element is so complex, that we can not really
13647         // use the rendered HTML.
13648         // so we have to trash and replace the previous code.
13649         if (Roo.XComponent.build_from_html) {
13650             // remove this element....
13651             var e = this.el.dom, k=0;
13652             while (e ) { e = e.previousSibling;  ++k;}
13653
13654             this.el.remove();
13655             
13656             this.el=false;
13657             this.rendered = false;
13658             
13659             this.render(this.parent().getChildContainer(true), k);
13660         }
13661         
13662         if(Roo.isIOS && this.useNativeIOS){
13663             this.initIOSView();
13664             return;
13665         }
13666         
13667         /*
13668          * Touch Devices
13669          */
13670         
13671         if(Roo.isTouch && this.mobileTouchView){
13672             this.initTouchView();
13673             return;
13674         }
13675         
13676         if(this.tickable){
13677             this.initTickableEvents();
13678             return;
13679         }
13680         
13681         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13682         
13683         if(this.hiddenName){
13684             
13685             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13686             
13687             this.hiddenField.dom.value =
13688                 this.hiddenValue !== undefined ? this.hiddenValue :
13689                 this.value !== undefined ? this.value : '';
13690
13691             // prevent input submission
13692             this.el.dom.removeAttribute('name');
13693             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13694              
13695              
13696         }
13697         //if(Roo.isGecko){
13698         //    this.el.dom.setAttribute('autocomplete', 'off');
13699         //}
13700         
13701         var cls = 'x-combo-list';
13702         
13703         //this.list = new Roo.Layer({
13704         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13705         //});
13706         
13707         var _this = this;
13708         
13709         (function(){
13710             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13711             _this.list.setWidth(lw);
13712         }).defer(100);
13713         
13714         this.list.on('mouseover', this.onViewOver, this);
13715         this.list.on('mousemove', this.onViewMove, this);
13716         this.list.on('scroll', this.onViewScroll, this);
13717         
13718         /*
13719         this.list.swallowEvent('mousewheel');
13720         this.assetHeight = 0;
13721
13722         if(this.title){
13723             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13724             this.assetHeight += this.header.getHeight();
13725         }
13726
13727         this.innerList = this.list.createChild({cls:cls+'-inner'});
13728         this.innerList.on('mouseover', this.onViewOver, this);
13729         this.innerList.on('mousemove', this.onViewMove, this);
13730         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13731         
13732         if(this.allowBlank && !this.pageSize && !this.disableClear){
13733             this.footer = this.list.createChild({cls:cls+'-ft'});
13734             this.pageTb = new Roo.Toolbar(this.footer);
13735            
13736         }
13737         if(this.pageSize){
13738             this.footer = this.list.createChild({cls:cls+'-ft'});
13739             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13740                     {pageSize: this.pageSize});
13741             
13742         }
13743         
13744         if (this.pageTb && this.allowBlank && !this.disableClear) {
13745             var _this = this;
13746             this.pageTb.add(new Roo.Toolbar.Fill(), {
13747                 cls: 'x-btn-icon x-btn-clear',
13748                 text: '&#160;',
13749                 handler: function()
13750                 {
13751                     _this.collapse();
13752                     _this.clearValue();
13753                     _this.onSelect(false, -1);
13754                 }
13755             });
13756         }
13757         if (this.footer) {
13758             this.assetHeight += this.footer.getHeight();
13759         }
13760         */
13761             
13762         if(!this.tpl){
13763             this.tpl = Roo.bootstrap.version == 4 ?
13764                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13765                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13766         }
13767
13768         this.view = new Roo.View(this.list, this.tpl, {
13769             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13770         });
13771         //this.view.wrapEl.setDisplayed(false);
13772         this.view.on('click', this.onViewClick, this);
13773         
13774         
13775         this.store.on('beforeload', this.onBeforeLoad, this);
13776         this.store.on('load', this.onLoad, this);
13777         this.store.on('loadexception', this.onLoadException, this);
13778         /*
13779         if(this.resizable){
13780             this.resizer = new Roo.Resizable(this.list,  {
13781                pinned:true, handles:'se'
13782             });
13783             this.resizer.on('resize', function(r, w, h){
13784                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13785                 this.listWidth = w;
13786                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13787                 this.restrictHeight();
13788             }, this);
13789             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13790         }
13791         */
13792         if(!this.editable){
13793             this.editable = true;
13794             this.setEditable(false);
13795         }
13796         
13797         /*
13798         
13799         if (typeof(this.events.add.listeners) != 'undefined') {
13800             
13801             this.addicon = this.wrap.createChild(
13802                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13803        
13804             this.addicon.on('click', function(e) {
13805                 this.fireEvent('add', this);
13806             }, this);
13807         }
13808         if (typeof(this.events.edit.listeners) != 'undefined') {
13809             
13810             this.editicon = this.wrap.createChild(
13811                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13812             if (this.addicon) {
13813                 this.editicon.setStyle('margin-left', '40px');
13814             }
13815             this.editicon.on('click', function(e) {
13816                 
13817                 // we fire even  if inothing is selected..
13818                 this.fireEvent('edit', this, this.lastData );
13819                 
13820             }, this);
13821         }
13822         */
13823         
13824         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13825             "up" : function(e){
13826                 this.inKeyMode = true;
13827                 this.selectPrev();
13828             },
13829
13830             "down" : function(e){
13831                 if(!this.isExpanded()){
13832                     this.onTriggerClick();
13833                 }else{
13834                     this.inKeyMode = true;
13835                     this.selectNext();
13836                 }
13837             },
13838
13839             "enter" : function(e){
13840 //                this.onViewClick();
13841                 //return true;
13842                 this.collapse();
13843                 
13844                 if(this.fireEvent("specialkey", this, e)){
13845                     this.onViewClick(false);
13846                 }
13847                 
13848                 return true;
13849             },
13850
13851             "esc" : function(e){
13852                 this.collapse();
13853             },
13854
13855             "tab" : function(e){
13856                 this.collapse();
13857                 
13858                 if(this.fireEvent("specialkey", this, e)){
13859                     this.onViewClick(false);
13860                 }
13861                 
13862                 return true;
13863             },
13864
13865             scope : this,
13866
13867             doRelay : function(foo, bar, hname){
13868                 if(hname == 'down' || this.scope.isExpanded()){
13869                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13870                 }
13871                 return true;
13872             },
13873
13874             forceKeyDown: true
13875         });
13876         
13877         
13878         this.queryDelay = Math.max(this.queryDelay || 10,
13879                 this.mode == 'local' ? 10 : 250);
13880         
13881         
13882         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13883         
13884         if(this.typeAhead){
13885             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13886         }
13887         if(this.editable !== false){
13888             this.inputEl().on("keyup", this.onKeyUp, this);
13889         }
13890         if(this.forceSelection){
13891             this.inputEl().on('blur', this.doForce, this);
13892         }
13893         
13894         if(this.multiple){
13895             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13896             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13897         }
13898     },
13899     
13900     initTickableEvents: function()
13901     {   
13902         this.createList();
13903         
13904         if(this.hiddenName){
13905             
13906             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13907             
13908             this.hiddenField.dom.value =
13909                 this.hiddenValue !== undefined ? this.hiddenValue :
13910                 this.value !== undefined ? this.value : '';
13911
13912             // prevent input submission
13913             this.el.dom.removeAttribute('name');
13914             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13915              
13916              
13917         }
13918         
13919 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13920         
13921         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13922         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13923         if(this.triggerList){
13924             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13925         }
13926          
13927         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13928         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13929         
13930         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13931         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13932         
13933         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13934         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13935         
13936         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13937         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13938         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13939         
13940         this.okBtn.hide();
13941         this.cancelBtn.hide();
13942         
13943         var _this = this;
13944         
13945         (function(){
13946             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13947             _this.list.setWidth(lw);
13948         }).defer(100);
13949         
13950         this.list.on('mouseover', this.onViewOver, this);
13951         this.list.on('mousemove', this.onViewMove, this);
13952         
13953         this.list.on('scroll', this.onViewScroll, this);
13954         
13955         if(!this.tpl){
13956             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13957                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13958         }
13959
13960         this.view = new Roo.View(this.list, this.tpl, {
13961             singleSelect:true,
13962             tickable:true,
13963             parent:this,
13964             store: this.store,
13965             selectedClass: this.selectedClass
13966         });
13967         
13968         //this.view.wrapEl.setDisplayed(false);
13969         this.view.on('click', this.onViewClick, this);
13970         
13971         
13972         
13973         this.store.on('beforeload', this.onBeforeLoad, this);
13974         this.store.on('load', this.onLoad, this);
13975         this.store.on('loadexception', this.onLoadException, this);
13976         
13977         if(this.editable){
13978             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13979                 "up" : function(e){
13980                     this.inKeyMode = true;
13981                     this.selectPrev();
13982                 },
13983
13984                 "down" : function(e){
13985                     this.inKeyMode = true;
13986                     this.selectNext();
13987                 },
13988
13989                 "enter" : function(e){
13990                     if(this.fireEvent("specialkey", this, e)){
13991                         this.onViewClick(false);
13992                     }
13993                     
13994                     return true;
13995                 },
13996
13997                 "esc" : function(e){
13998                     this.onTickableFooterButtonClick(e, false, false);
13999                 },
14000
14001                 "tab" : function(e){
14002                     this.fireEvent("specialkey", this, e);
14003                     
14004                     this.onTickableFooterButtonClick(e, false, false);
14005                     
14006                     return true;
14007                 },
14008
14009                 scope : this,
14010
14011                 doRelay : function(e, fn, key){
14012                     if(this.scope.isExpanded()){
14013                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14014                     }
14015                     return true;
14016                 },
14017
14018                 forceKeyDown: true
14019             });
14020         }
14021         
14022         this.queryDelay = Math.max(this.queryDelay || 10,
14023                 this.mode == 'local' ? 10 : 250);
14024         
14025         
14026         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14027         
14028         if(this.typeAhead){
14029             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14030         }
14031         
14032         if(this.editable !== false){
14033             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14034         }
14035         
14036         this.indicator = this.indicatorEl();
14037         
14038         if(this.indicator){
14039             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14040             this.indicator.hide();
14041         }
14042         
14043     },
14044
14045     onDestroy : function(){
14046         if(this.view){
14047             this.view.setStore(null);
14048             this.view.el.removeAllListeners();
14049             this.view.el.remove();
14050             this.view.purgeListeners();
14051         }
14052         if(this.list){
14053             this.list.dom.innerHTML  = '';
14054         }
14055         
14056         if(this.store){
14057             this.store.un('beforeload', this.onBeforeLoad, this);
14058             this.store.un('load', this.onLoad, this);
14059             this.store.un('loadexception', this.onLoadException, this);
14060         }
14061         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14062     },
14063
14064     // private
14065     fireKey : function(e){
14066         if(e.isNavKeyPress() && !this.list.isVisible()){
14067             this.fireEvent("specialkey", this, e);
14068         }
14069     },
14070
14071     // private
14072     onResize: function(w, h){
14073 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14074 //        
14075 //        if(typeof w != 'number'){
14076 //            // we do not handle it!?!?
14077 //            return;
14078 //        }
14079 //        var tw = this.trigger.getWidth();
14080 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14081 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14082 //        var x = w - tw;
14083 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14084 //            
14085 //        //this.trigger.setStyle('left', x+'px');
14086 //        
14087 //        if(this.list && this.listWidth === undefined){
14088 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14089 //            this.list.setWidth(lw);
14090 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14091 //        }
14092         
14093     
14094         
14095     },
14096
14097     /**
14098      * Allow or prevent the user from directly editing the field text.  If false is passed,
14099      * the user will only be able to select from the items defined in the dropdown list.  This method
14100      * is the runtime equivalent of setting the 'editable' config option at config time.
14101      * @param {Boolean} value True to allow the user to directly edit the field text
14102      */
14103     setEditable : function(value){
14104         if(value == this.editable){
14105             return;
14106         }
14107         this.editable = value;
14108         if(!value){
14109             this.inputEl().dom.setAttribute('readOnly', true);
14110             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14111             this.inputEl().addClass('x-combo-noedit');
14112         }else{
14113             this.inputEl().dom.setAttribute('readOnly', false);
14114             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14115             this.inputEl().removeClass('x-combo-noedit');
14116         }
14117     },
14118
14119     // private
14120     
14121     onBeforeLoad : function(combo,opts){
14122         if(!this.hasFocus){
14123             return;
14124         }
14125          if (!opts.add) {
14126             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14127          }
14128         this.restrictHeight();
14129         this.selectedIndex = -1;
14130     },
14131
14132     // private
14133     onLoad : function(){
14134         
14135         this.hasQuery = false;
14136         
14137         if(!this.hasFocus){
14138             return;
14139         }
14140         
14141         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14142             this.loading.hide();
14143         }
14144         
14145         if(this.store.getCount() > 0){
14146             
14147             this.expand();
14148             this.restrictHeight();
14149             if(this.lastQuery == this.allQuery){
14150                 if(this.editable && !this.tickable){
14151                     this.inputEl().dom.select();
14152                 }
14153                 
14154                 if(
14155                     !this.selectByValue(this.value, true) &&
14156                     this.autoFocus && 
14157                     (
14158                         !this.store.lastOptions ||
14159                         typeof(this.store.lastOptions.add) == 'undefined' || 
14160                         this.store.lastOptions.add != true
14161                     )
14162                 ){
14163                     this.select(0, true);
14164                 }
14165             }else{
14166                 if(this.autoFocus){
14167                     this.selectNext();
14168                 }
14169                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14170                     this.taTask.delay(this.typeAheadDelay);
14171                 }
14172             }
14173         }else{
14174             this.onEmptyResults();
14175         }
14176         
14177         //this.el.focus();
14178     },
14179     // private
14180     onLoadException : function()
14181     {
14182         this.hasQuery = false;
14183         
14184         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14185             this.loading.hide();
14186         }
14187         
14188         if(this.tickable && this.editable){
14189             return;
14190         }
14191         
14192         this.collapse();
14193         // only causes errors at present
14194         //Roo.log(this.store.reader.jsonData);
14195         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14196             // fixme
14197             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14198         //}
14199         
14200         
14201     },
14202     // private
14203     onTypeAhead : function(){
14204         if(this.store.getCount() > 0){
14205             var r = this.store.getAt(0);
14206             var newValue = r.data[this.displayField];
14207             var len = newValue.length;
14208             var selStart = this.getRawValue().length;
14209             
14210             if(selStart != len){
14211                 this.setRawValue(newValue);
14212                 this.selectText(selStart, newValue.length);
14213             }
14214         }
14215     },
14216
14217     // private
14218     onSelect : function(record, index){
14219         
14220         if(this.fireEvent('beforeselect', this, record, index) !== false){
14221         
14222             this.setFromData(index > -1 ? record.data : false);
14223             
14224             this.collapse();
14225             this.fireEvent('select', this, record, index);
14226         }
14227     },
14228
14229     /**
14230      * Returns the currently selected field value or empty string if no value is set.
14231      * @return {String} value The selected value
14232      */
14233     getValue : function()
14234     {
14235         if(Roo.isIOS && this.useNativeIOS){
14236             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14237         }
14238         
14239         if(this.multiple){
14240             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14241         }
14242         
14243         if(this.valueField){
14244             return typeof this.value != 'undefined' ? this.value : '';
14245         }else{
14246             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14247         }
14248     },
14249     
14250     getRawValue : function()
14251     {
14252         if(Roo.isIOS && this.useNativeIOS){
14253             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14254         }
14255         
14256         var v = this.inputEl().getValue();
14257         
14258         return v;
14259     },
14260
14261     /**
14262      * Clears any text/value currently set in the field
14263      */
14264     clearValue : function(){
14265         
14266         if(this.hiddenField){
14267             this.hiddenField.dom.value = '';
14268         }
14269         this.value = '';
14270         this.setRawValue('');
14271         this.lastSelectionText = '';
14272         this.lastData = false;
14273         
14274         var close = this.closeTriggerEl();
14275         
14276         if(close){
14277             close.hide();
14278         }
14279         
14280         this.validate();
14281         
14282     },
14283
14284     /**
14285      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14286      * will be displayed in the field.  If the value does not match the data value of an existing item,
14287      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14288      * Otherwise the field will be blank (although the value will still be set).
14289      * @param {String} value The value to match
14290      */
14291     setValue : function(v)
14292     {
14293         if(Roo.isIOS && this.useNativeIOS){
14294             this.setIOSValue(v);
14295             return;
14296         }
14297         
14298         if(this.multiple){
14299             this.syncValue();
14300             return;
14301         }
14302         
14303         var text = v;
14304         if(this.valueField){
14305             var r = this.findRecord(this.valueField, v);
14306             if(r){
14307                 text = r.data[this.displayField];
14308             }else if(this.valueNotFoundText !== undefined){
14309                 text = this.valueNotFoundText;
14310             }
14311         }
14312         this.lastSelectionText = text;
14313         if(this.hiddenField){
14314             this.hiddenField.dom.value = v;
14315         }
14316         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14317         this.value = v;
14318         
14319         var close = this.closeTriggerEl();
14320         
14321         if(close){
14322             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14323         }
14324         
14325         this.validate();
14326     },
14327     /**
14328      * @property {Object} the last set data for the element
14329      */
14330     
14331     lastData : false,
14332     /**
14333      * Sets the value of the field based on a object which is related to the record format for the store.
14334      * @param {Object} value the value to set as. or false on reset?
14335      */
14336     setFromData : function(o){
14337         
14338         if(this.multiple){
14339             this.addItem(o);
14340             return;
14341         }
14342             
14343         var dv = ''; // display value
14344         var vv = ''; // value value..
14345         this.lastData = o;
14346         if (this.displayField) {
14347             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14348         } else {
14349             // this is an error condition!!!
14350             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14351         }
14352         
14353         if(this.valueField){
14354             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14355         }
14356         
14357         var close = this.closeTriggerEl();
14358         
14359         if(close){
14360             if(dv.length || vv * 1 > 0){
14361                 close.show() ;
14362                 this.blockFocus=true;
14363             } else {
14364                 close.hide();
14365             }             
14366         }
14367         
14368         if(this.hiddenField){
14369             this.hiddenField.dom.value = vv;
14370             
14371             this.lastSelectionText = dv;
14372             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14373             this.value = vv;
14374             return;
14375         }
14376         // no hidden field.. - we store the value in 'value', but still display
14377         // display field!!!!
14378         this.lastSelectionText = dv;
14379         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14380         this.value = vv;
14381         
14382         
14383         
14384     },
14385     // private
14386     reset : function(){
14387         // overridden so that last data is reset..
14388         
14389         if(this.multiple){
14390             this.clearItem();
14391             return;
14392         }
14393         
14394         this.setValue(this.originalValue);
14395         //this.clearInvalid();
14396         this.lastData = false;
14397         if (this.view) {
14398             this.view.clearSelections();
14399         }
14400         
14401         this.validate();
14402     },
14403     // private
14404     findRecord : function(prop, value){
14405         var record;
14406         if(this.store.getCount() > 0){
14407             this.store.each(function(r){
14408                 if(r.data[prop] == value){
14409                     record = r;
14410                     return false;
14411                 }
14412                 return true;
14413             });
14414         }
14415         return record;
14416     },
14417     
14418     getName: function()
14419     {
14420         // returns hidden if it's set..
14421         if (!this.rendered) {return ''};
14422         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14423         
14424     },
14425     // private
14426     onViewMove : function(e, t){
14427         this.inKeyMode = false;
14428     },
14429
14430     // private
14431     onViewOver : function(e, t){
14432         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14433             return;
14434         }
14435         var item = this.view.findItemFromChild(t);
14436         
14437         if(item){
14438             var index = this.view.indexOf(item);
14439             this.select(index, false);
14440         }
14441     },
14442
14443     // private
14444     onViewClick : function(view, doFocus, el, e)
14445     {
14446         var index = this.view.getSelectedIndexes()[0];
14447         
14448         var r = this.store.getAt(index);
14449         
14450         if(this.tickable){
14451             
14452             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14453                 return;
14454             }
14455             
14456             var rm = false;
14457             var _this = this;
14458             
14459             Roo.each(this.tickItems, function(v,k){
14460                 
14461                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14462                     Roo.log(v);
14463                     _this.tickItems.splice(k, 1);
14464                     
14465                     if(typeof(e) == 'undefined' && view == false){
14466                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14467                     }
14468                     
14469                     rm = true;
14470                     return;
14471                 }
14472             });
14473             
14474             if(rm){
14475                 return;
14476             }
14477             
14478             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14479                 this.tickItems.push(r.data);
14480             }
14481             
14482             if(typeof(e) == 'undefined' && view == false){
14483                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14484             }
14485                     
14486             return;
14487         }
14488         
14489         if(r){
14490             this.onSelect(r, index);
14491         }
14492         if(doFocus !== false && !this.blockFocus){
14493             this.inputEl().focus();
14494         }
14495     },
14496
14497     // private
14498     restrictHeight : function(){
14499         //this.innerList.dom.style.height = '';
14500         //var inner = this.innerList.dom;
14501         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14502         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14503         //this.list.beginUpdate();
14504         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14505         this.list.alignTo(this.inputEl(), this.listAlign);
14506         this.list.alignTo(this.inputEl(), this.listAlign);
14507         //this.list.endUpdate();
14508     },
14509
14510     // private
14511     onEmptyResults : function(){
14512         
14513         if(this.tickable && this.editable){
14514             this.hasFocus = false;
14515             this.restrictHeight();
14516             return;
14517         }
14518         
14519         this.collapse();
14520     },
14521
14522     /**
14523      * Returns true if the dropdown list is expanded, else false.
14524      */
14525     isExpanded : function(){
14526         return this.list.isVisible();
14527     },
14528
14529     /**
14530      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14532      * @param {String} value The data value of the item to select
14533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14534      * selected item if it is not currently in view (defaults to true)
14535      * @return {Boolean} True if the value matched an item in the list, else false
14536      */
14537     selectByValue : function(v, scrollIntoView){
14538         if(v !== undefined && v !== null){
14539             var r = this.findRecord(this.valueField || this.displayField, v);
14540             if(r){
14541                 this.select(this.store.indexOf(r), scrollIntoView);
14542                 return true;
14543             }
14544         }
14545         return false;
14546     },
14547
14548     /**
14549      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14550      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14551      * @param {Number} index The zero-based index of the list item to select
14552      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14553      * selected item if it is not currently in view (defaults to true)
14554      */
14555     select : function(index, scrollIntoView){
14556         this.selectedIndex = index;
14557         this.view.select(index);
14558         if(scrollIntoView !== false){
14559             var el = this.view.getNode(index);
14560             /*
14561              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14562              */
14563             if(el){
14564                 this.list.scrollChildIntoView(el, false);
14565             }
14566         }
14567     },
14568
14569     // private
14570     selectNext : function(){
14571         var ct = this.store.getCount();
14572         if(ct > 0){
14573             if(this.selectedIndex == -1){
14574                 this.select(0);
14575             }else if(this.selectedIndex < ct-1){
14576                 this.select(this.selectedIndex+1);
14577             }
14578         }
14579     },
14580
14581     // private
14582     selectPrev : function(){
14583         var ct = this.store.getCount();
14584         if(ct > 0){
14585             if(this.selectedIndex == -1){
14586                 this.select(0);
14587             }else if(this.selectedIndex != 0){
14588                 this.select(this.selectedIndex-1);
14589             }
14590         }
14591     },
14592
14593     // private
14594     onKeyUp : function(e){
14595         if(this.editable !== false && !e.isSpecialKey()){
14596             this.lastKey = e.getKey();
14597             this.dqTask.delay(this.queryDelay);
14598         }
14599     },
14600
14601     // private
14602     validateBlur : function(){
14603         return !this.list || !this.list.isVisible();   
14604     },
14605
14606     // private
14607     initQuery : function(){
14608         
14609         var v = this.getRawValue();
14610         
14611         if(this.tickable && this.editable){
14612             v = this.tickableInputEl().getValue();
14613         }
14614         
14615         this.doQuery(v);
14616     },
14617
14618     // private
14619     doForce : function(){
14620         if(this.inputEl().dom.value.length > 0){
14621             this.inputEl().dom.value =
14622                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14623              
14624         }
14625     },
14626
14627     /**
14628      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14629      * query allowing the query action to be canceled if needed.
14630      * @param {String} query The SQL query to execute
14631      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14632      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14633      * saved in the current store (defaults to false)
14634      */
14635     doQuery : function(q, forceAll){
14636         
14637         if(q === undefined || q === null){
14638             q = '';
14639         }
14640         var qe = {
14641             query: q,
14642             forceAll: forceAll,
14643             combo: this,
14644             cancel:false
14645         };
14646         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14647             return false;
14648         }
14649         q = qe.query;
14650         
14651         forceAll = qe.forceAll;
14652         if(forceAll === true || (q.length >= this.minChars)){
14653             
14654             this.hasQuery = true;
14655             
14656             if(this.lastQuery != q || this.alwaysQuery){
14657                 this.lastQuery = q;
14658                 if(this.mode == 'local'){
14659                     this.selectedIndex = -1;
14660                     if(forceAll){
14661                         this.store.clearFilter();
14662                     }else{
14663                         
14664                         if(this.specialFilter){
14665                             this.fireEvent('specialfilter', this);
14666                             this.onLoad();
14667                             return;
14668                         }
14669                         
14670                         this.store.filter(this.displayField, q);
14671                     }
14672                     
14673                     this.store.fireEvent("datachanged", this.store);
14674                     
14675                     this.onLoad();
14676                     
14677                     
14678                 }else{
14679                     
14680                     this.store.baseParams[this.queryParam] = q;
14681                     
14682                     var options = {params : this.getParams(q)};
14683                     
14684                     if(this.loadNext){
14685                         options.add = true;
14686                         options.params.start = this.page * this.pageSize;
14687                     }
14688                     
14689                     this.store.load(options);
14690                     
14691                     /*
14692                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14693                      *  we should expand the list on onLoad
14694                      *  so command out it
14695                      */
14696 //                    this.expand();
14697                 }
14698             }else{
14699                 this.selectedIndex = -1;
14700                 this.onLoad();   
14701             }
14702         }
14703         
14704         this.loadNext = false;
14705     },
14706     
14707     // private
14708     getParams : function(q){
14709         var p = {};
14710         //p[this.queryParam] = q;
14711         
14712         if(this.pageSize){
14713             p.start = 0;
14714             p.limit = this.pageSize;
14715         }
14716         return p;
14717     },
14718
14719     /**
14720      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14721      */
14722     collapse : function(){
14723         if(!this.isExpanded()){
14724             return;
14725         }
14726         
14727         this.list.hide();
14728         
14729         this.hasFocus = false;
14730         
14731         if(this.tickable){
14732             this.okBtn.hide();
14733             this.cancelBtn.hide();
14734             this.trigger.show();
14735             
14736             if(this.editable){
14737                 this.tickableInputEl().dom.value = '';
14738                 this.tickableInputEl().blur();
14739             }
14740             
14741         }
14742         
14743         Roo.get(document).un('mousedown', this.collapseIf, this);
14744         Roo.get(document).un('mousewheel', this.collapseIf, this);
14745         if (!this.editable) {
14746             Roo.get(document).un('keydown', this.listKeyPress, this);
14747         }
14748         this.fireEvent('collapse', this);
14749         
14750         this.validate();
14751     },
14752
14753     // private
14754     collapseIf : function(e){
14755         var in_combo  = e.within(this.el);
14756         var in_list =  e.within(this.list);
14757         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14758         
14759         if (in_combo || in_list || is_list) {
14760             //e.stopPropagation();
14761             return;
14762         }
14763         
14764         if(this.tickable){
14765             this.onTickableFooterButtonClick(e, false, false);
14766         }
14767
14768         this.collapse();
14769         
14770     },
14771
14772     /**
14773      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14774      */
14775     expand : function(){
14776        
14777         if(this.isExpanded() || !this.hasFocus){
14778             return;
14779         }
14780         
14781         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14782         this.list.setWidth(lw);
14783         
14784         Roo.log('expand');
14785         
14786         this.list.show();
14787         
14788         this.restrictHeight();
14789         
14790         if(this.tickable){
14791             
14792             this.tickItems = Roo.apply([], this.item);
14793             
14794             this.okBtn.show();
14795             this.cancelBtn.show();
14796             this.trigger.hide();
14797             
14798             if(this.editable){
14799                 this.tickableInputEl().focus();
14800             }
14801             
14802         }
14803         
14804         Roo.get(document).on('mousedown', this.collapseIf, this);
14805         Roo.get(document).on('mousewheel', this.collapseIf, this);
14806         if (!this.editable) {
14807             Roo.get(document).on('keydown', this.listKeyPress, this);
14808         }
14809         
14810         this.fireEvent('expand', this);
14811     },
14812
14813     // private
14814     // Implements the default empty TriggerField.onTriggerClick function
14815     onTriggerClick : function(e)
14816     {
14817         Roo.log('trigger click');
14818         
14819         if(this.disabled || !this.triggerList){
14820             return;
14821         }
14822         
14823         this.page = 0;
14824         this.loadNext = false;
14825         
14826         if(this.isExpanded()){
14827             this.collapse();
14828             if (!this.blockFocus) {
14829                 this.inputEl().focus();
14830             }
14831             
14832         }else {
14833             this.hasFocus = true;
14834             if(this.triggerAction == 'all') {
14835                 this.doQuery(this.allQuery, true);
14836             } else {
14837                 this.doQuery(this.getRawValue());
14838             }
14839             if (!this.blockFocus) {
14840                 this.inputEl().focus();
14841             }
14842         }
14843     },
14844     
14845     onTickableTriggerClick : function(e)
14846     {
14847         if(this.disabled){
14848             return;
14849         }
14850         
14851         this.page = 0;
14852         this.loadNext = false;
14853         this.hasFocus = true;
14854         
14855         if(this.triggerAction == 'all') {
14856             this.doQuery(this.allQuery, true);
14857         } else {
14858             this.doQuery(this.getRawValue());
14859         }
14860     },
14861     
14862     onSearchFieldClick : function(e)
14863     {
14864         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14865             this.onTickableFooterButtonClick(e, false, false);
14866             return;
14867         }
14868         
14869         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14870             return;
14871         }
14872         
14873         this.page = 0;
14874         this.loadNext = false;
14875         this.hasFocus = true;
14876         
14877         if(this.triggerAction == 'all') {
14878             this.doQuery(this.allQuery, true);
14879         } else {
14880             this.doQuery(this.getRawValue());
14881         }
14882     },
14883     
14884     listKeyPress : function(e)
14885     {
14886         //Roo.log('listkeypress');
14887         // scroll to first matching element based on key pres..
14888         if (e.isSpecialKey()) {
14889             return false;
14890         }
14891         var k = String.fromCharCode(e.getKey()).toUpperCase();
14892         //Roo.log(k);
14893         var match  = false;
14894         var csel = this.view.getSelectedNodes();
14895         var cselitem = false;
14896         if (csel.length) {
14897             var ix = this.view.indexOf(csel[0]);
14898             cselitem  = this.store.getAt(ix);
14899             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14900                 cselitem = false;
14901             }
14902             
14903         }
14904         
14905         this.store.each(function(v) { 
14906             if (cselitem) {
14907                 // start at existing selection.
14908                 if (cselitem.id == v.id) {
14909                     cselitem = false;
14910                 }
14911                 return true;
14912             }
14913                 
14914             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14915                 match = this.store.indexOf(v);
14916                 return false;
14917             }
14918             return true;
14919         }, this);
14920         
14921         if (match === false) {
14922             return true; // no more action?
14923         }
14924         // scroll to?
14925         this.view.select(match);
14926         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14927         sn.scrollIntoView(sn.dom.parentNode, false);
14928     },
14929     
14930     onViewScroll : function(e, t){
14931         
14932         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){
14933             return;
14934         }
14935         
14936         this.hasQuery = true;
14937         
14938         this.loading = this.list.select('.loading', true).first();
14939         
14940         if(this.loading === null){
14941             this.list.createChild({
14942                 tag: 'div',
14943                 cls: 'loading roo-select2-more-results roo-select2-active',
14944                 html: 'Loading more results...'
14945             });
14946             
14947             this.loading = this.list.select('.loading', true).first();
14948             
14949             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14950             
14951             this.loading.hide();
14952         }
14953         
14954         this.loading.show();
14955         
14956         var _combo = this;
14957         
14958         this.page++;
14959         this.loadNext = true;
14960         
14961         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14962         
14963         return;
14964     },
14965     
14966     addItem : function(o)
14967     {   
14968         var dv = ''; // display value
14969         
14970         if (this.displayField) {
14971             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14972         } else {
14973             // this is an error condition!!!
14974             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14975         }
14976         
14977         if(!dv.length){
14978             return;
14979         }
14980         
14981         var choice = this.choices.createChild({
14982             tag: 'li',
14983             cls: 'roo-select2-search-choice',
14984             cn: [
14985                 {
14986                     tag: 'div',
14987                     html: dv
14988                 },
14989                 {
14990                     tag: 'a',
14991                     href: '#',
14992                     cls: 'roo-select2-search-choice-close fa fa-times',
14993                     tabindex: '-1'
14994                 }
14995             ]
14996             
14997         }, this.searchField);
14998         
14999         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15000         
15001         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15002         
15003         this.item.push(o);
15004         
15005         this.lastData = o;
15006         
15007         this.syncValue();
15008         
15009         this.inputEl().dom.value = '';
15010         
15011         this.validate();
15012     },
15013     
15014     onRemoveItem : function(e, _self, o)
15015     {
15016         e.preventDefault();
15017         
15018         this.lastItem = Roo.apply([], this.item);
15019         
15020         var index = this.item.indexOf(o.data) * 1;
15021         
15022         if( index < 0){
15023             Roo.log('not this item?!');
15024             return;
15025         }
15026         
15027         this.item.splice(index, 1);
15028         o.item.remove();
15029         
15030         this.syncValue();
15031         
15032         this.fireEvent('remove', this, e);
15033         
15034         this.validate();
15035         
15036     },
15037     
15038     syncValue : function()
15039     {
15040         if(!this.item.length){
15041             this.clearValue();
15042             return;
15043         }
15044             
15045         var value = [];
15046         var _this = this;
15047         Roo.each(this.item, function(i){
15048             if(_this.valueField){
15049                 value.push(i[_this.valueField]);
15050                 return;
15051             }
15052
15053             value.push(i);
15054         });
15055
15056         this.value = value.join(',');
15057
15058         if(this.hiddenField){
15059             this.hiddenField.dom.value = this.value;
15060         }
15061         
15062         this.store.fireEvent("datachanged", this.store);
15063         
15064         this.validate();
15065     },
15066     
15067     clearItem : function()
15068     {
15069         if(!this.multiple){
15070             return;
15071         }
15072         
15073         this.item = [];
15074         
15075         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15076            c.remove();
15077         });
15078         
15079         this.syncValue();
15080         
15081         this.validate();
15082         
15083         if(this.tickable && !Roo.isTouch){
15084             this.view.refresh();
15085         }
15086     },
15087     
15088     inputEl: function ()
15089     {
15090         if(Roo.isIOS && this.useNativeIOS){
15091             return this.el.select('select.roo-ios-select', true).first();
15092         }
15093         
15094         if(Roo.isTouch && this.mobileTouchView){
15095             return this.el.select('input.form-control',true).first();
15096         }
15097         
15098         if(this.tickable){
15099             return this.searchField;
15100         }
15101         
15102         return this.el.select('input.form-control',true).first();
15103     },
15104     
15105     onTickableFooterButtonClick : function(e, btn, el)
15106     {
15107         e.preventDefault();
15108         
15109         this.lastItem = Roo.apply([], this.item);
15110         
15111         if(btn && btn.name == 'cancel'){
15112             this.tickItems = Roo.apply([], this.item);
15113             this.collapse();
15114             return;
15115         }
15116         
15117         this.clearItem();
15118         
15119         var _this = this;
15120         
15121         Roo.each(this.tickItems, function(o){
15122             _this.addItem(o);
15123         });
15124         
15125         this.collapse();
15126         
15127     },
15128     
15129     validate : function()
15130     {
15131         if(this.getVisibilityEl().hasClass('hidden')){
15132             return true;
15133         }
15134         
15135         var v = this.getRawValue();
15136         
15137         if(this.multiple){
15138             v = this.getValue();
15139         }
15140         
15141         if(this.disabled || this.allowBlank || v.length){
15142             this.markValid();
15143             return true;
15144         }
15145         
15146         this.markInvalid();
15147         return false;
15148     },
15149     
15150     tickableInputEl : function()
15151     {
15152         if(!this.tickable || !this.editable){
15153             return this.inputEl();
15154         }
15155         
15156         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15157     },
15158     
15159     
15160     getAutoCreateTouchView : function()
15161     {
15162         var id = Roo.id();
15163         
15164         var cfg = {
15165             cls: 'form-group' //input-group
15166         };
15167         
15168         var input =  {
15169             tag: 'input',
15170             id : id,
15171             type : this.inputType,
15172             cls : 'form-control x-combo-noedit',
15173             autocomplete: 'new-password',
15174             placeholder : this.placeholder || '',
15175             readonly : true
15176         };
15177         
15178         if (this.name) {
15179             input.name = this.name;
15180         }
15181         
15182         if (this.size) {
15183             input.cls += ' input-' + this.size;
15184         }
15185         
15186         if (this.disabled) {
15187             input.disabled = true;
15188         }
15189         
15190         var inputblock = {
15191             cls : '',
15192             cn : [
15193                 input
15194             ]
15195         };
15196         
15197         if(this.before){
15198             inputblock.cls += ' input-group';
15199             
15200             inputblock.cn.unshift({
15201                 tag :'span',
15202                 cls : 'input-group-addon input-group-prepend input-group-text',
15203                 html : this.before
15204             });
15205         }
15206         
15207         if(this.removable && !this.multiple){
15208             inputblock.cls += ' roo-removable';
15209             
15210             inputblock.cn.push({
15211                 tag: 'button',
15212                 html : 'x',
15213                 cls : 'roo-combo-removable-btn close'
15214             });
15215         }
15216
15217         if(this.hasFeedback && !this.allowBlank){
15218             
15219             inputblock.cls += ' has-feedback';
15220             
15221             inputblock.cn.push({
15222                 tag: 'span',
15223                 cls: 'glyphicon form-control-feedback'
15224             });
15225             
15226         }
15227         
15228         if (this.after) {
15229             
15230             inputblock.cls += (this.before) ? '' : ' input-group';
15231             
15232             inputblock.cn.push({
15233                 tag :'span',
15234                 cls : 'input-group-addon input-group-append input-group-text',
15235                 html : this.after
15236             });
15237         }
15238
15239         
15240         var ibwrap = inputblock;
15241         
15242         if(this.multiple){
15243             ibwrap = {
15244                 tag: 'ul',
15245                 cls: 'roo-select2-choices',
15246                 cn:[
15247                     {
15248                         tag: 'li',
15249                         cls: 'roo-select2-search-field',
15250                         cn: [
15251
15252                             inputblock
15253                         ]
15254                     }
15255                 ]
15256             };
15257         
15258             
15259         }
15260         
15261         var combobox = {
15262             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15263             cn: [
15264                 {
15265                     tag: 'input',
15266                     type : 'hidden',
15267                     cls: 'form-hidden-field'
15268                 },
15269                 ibwrap
15270             ]
15271         };
15272         
15273         if(!this.multiple && this.showToggleBtn){
15274             
15275             var caret = {
15276                         tag: 'span',
15277                         cls: 'caret'
15278             };
15279             
15280             if (this.caret != false) {
15281                 caret = {
15282                      tag: 'i',
15283                      cls: 'fa fa-' + this.caret
15284                 };
15285                 
15286             }
15287             
15288             combobox.cn.push({
15289                 tag :'span',
15290                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15291                 cn : [
15292                     caret,
15293                     {
15294                         tag: 'span',
15295                         cls: 'combobox-clear',
15296                         cn  : [
15297                             {
15298                                 tag : 'i',
15299                                 cls: 'icon-remove'
15300                             }
15301                         ]
15302                     }
15303                 ]
15304
15305             })
15306         }
15307         
15308         if(this.multiple){
15309             combobox.cls += ' roo-select2-container-multi';
15310         }
15311         
15312         var align = this.labelAlign || this.parentLabelAlign();
15313         
15314         if (align ==='left' && this.fieldLabel.length) {
15315
15316             cfg.cn = [
15317                 {
15318                    tag : 'i',
15319                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15320                    tooltip : 'This field is required'
15321                 },
15322                 {
15323                     tag: 'label',
15324                     cls : 'control-label col-form-label',
15325                     html : this.fieldLabel
15326
15327                 },
15328                 {
15329                     cls : '', 
15330                     cn: [
15331                         combobox
15332                     ]
15333                 }
15334             ];
15335             
15336             var labelCfg = cfg.cn[1];
15337             var contentCfg = cfg.cn[2];
15338             
15339
15340             if(this.indicatorpos == 'right'){
15341                 cfg.cn = [
15342                     {
15343                         tag: 'label',
15344                         'for' :  id,
15345                         cls : 'control-label col-form-label',
15346                         cn : [
15347                             {
15348                                 tag : 'span',
15349                                 html : this.fieldLabel
15350                             },
15351                             {
15352                                 tag : 'i',
15353                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15354                                 tooltip : 'This field is required'
15355                             }
15356                         ]
15357                     },
15358                     {
15359                         cls : "",
15360                         cn: [
15361                             combobox
15362                         ]
15363                     }
15364
15365                 ];
15366                 
15367                 labelCfg = cfg.cn[0];
15368                 contentCfg = cfg.cn[1];
15369             }
15370             
15371            
15372             
15373             if(this.labelWidth > 12){
15374                 labelCfg.style = "width: " + this.labelWidth + 'px';
15375             }
15376             
15377             if(this.labelWidth < 13 && this.labelmd == 0){
15378                 this.labelmd = this.labelWidth;
15379             }
15380             
15381             if(this.labellg > 0){
15382                 labelCfg.cls += ' col-lg-' + this.labellg;
15383                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15384             }
15385             
15386             if(this.labelmd > 0){
15387                 labelCfg.cls += ' col-md-' + this.labelmd;
15388                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15389             }
15390             
15391             if(this.labelsm > 0){
15392                 labelCfg.cls += ' col-sm-' + this.labelsm;
15393                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15394             }
15395             
15396             if(this.labelxs > 0){
15397                 labelCfg.cls += ' col-xs-' + this.labelxs;
15398                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15399             }
15400                 
15401                 
15402         } else if ( this.fieldLabel.length) {
15403             cfg.cn = [
15404                 {
15405                    tag : 'i',
15406                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15407                    tooltip : 'This field is required'
15408                 },
15409                 {
15410                     tag: 'label',
15411                     cls : 'control-label',
15412                     html : this.fieldLabel
15413
15414                 },
15415                 {
15416                     cls : '', 
15417                     cn: [
15418                         combobox
15419                     ]
15420                 }
15421             ];
15422             
15423             if(this.indicatorpos == 'right'){
15424                 cfg.cn = [
15425                     {
15426                         tag: 'label',
15427                         cls : 'control-label',
15428                         html : this.fieldLabel,
15429                         cn : [
15430                             {
15431                                tag : 'i',
15432                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15433                                tooltip : 'This field is required'
15434                             }
15435                         ]
15436                     },
15437                     {
15438                         cls : '', 
15439                         cn: [
15440                             combobox
15441                         ]
15442                     }
15443                 ];
15444             }
15445         } else {
15446             cfg.cn = combobox;    
15447         }
15448         
15449         
15450         var settings = this;
15451         
15452         ['xs','sm','md','lg'].map(function(size){
15453             if (settings[size]) {
15454                 cfg.cls += ' col-' + size + '-' + settings[size];
15455             }
15456         });
15457         
15458         return cfg;
15459     },
15460     
15461     initTouchView : function()
15462     {
15463         this.renderTouchView();
15464         
15465         this.touchViewEl.on('scroll', function(){
15466             this.el.dom.scrollTop = 0;
15467         }, this);
15468         
15469         this.originalValue = this.getValue();
15470         
15471         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15472         
15473         this.inputEl().on("click", this.showTouchView, this);
15474         if (this.triggerEl) {
15475             this.triggerEl.on("click", this.showTouchView, this);
15476         }
15477         
15478         
15479         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15480         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15481         
15482         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15483         
15484         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15485         this.store.on('load', this.onTouchViewLoad, this);
15486         this.store.on('loadexception', this.onTouchViewLoadException, this);
15487         
15488         if(this.hiddenName){
15489             
15490             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15491             
15492             this.hiddenField.dom.value =
15493                 this.hiddenValue !== undefined ? this.hiddenValue :
15494                 this.value !== undefined ? this.value : '';
15495         
15496             this.el.dom.removeAttribute('name');
15497             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15498         }
15499         
15500         if(this.multiple){
15501             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15502             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15503         }
15504         
15505         if(this.removable && !this.multiple){
15506             var close = this.closeTriggerEl();
15507             if(close){
15508                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15509                 close.on('click', this.removeBtnClick, this, close);
15510             }
15511         }
15512         /*
15513          * fix the bug in Safari iOS8
15514          */
15515         this.inputEl().on("focus", function(e){
15516             document.activeElement.blur();
15517         }, this);
15518         
15519         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15520         
15521         return;
15522         
15523         
15524     },
15525     
15526     renderTouchView : function()
15527     {
15528         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15529         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15530         
15531         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15532         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15533         
15534         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15535         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15536         this.touchViewBodyEl.setStyle('overflow', 'auto');
15537         
15538         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15539         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15540         
15541         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15542         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15543         
15544     },
15545     
15546     showTouchView : function()
15547     {
15548         if(this.disabled){
15549             return;
15550         }
15551         
15552         this.touchViewHeaderEl.hide();
15553
15554         if(this.modalTitle.length){
15555             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15556             this.touchViewHeaderEl.show();
15557         }
15558
15559         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15560         this.touchViewEl.show();
15561
15562         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15563         
15564         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15565         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15566
15567         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15568
15569         if(this.modalTitle.length){
15570             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15571         }
15572         
15573         this.touchViewBodyEl.setHeight(bodyHeight);
15574
15575         if(this.animate){
15576             var _this = this;
15577             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15578         }else{
15579             this.touchViewEl.addClass('in');
15580         }
15581         
15582         if(this._touchViewMask){
15583             Roo.get(document.body).addClass("x-body-masked");
15584             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15585             this._touchViewMask.setStyle('z-index', 10000);
15586             this._touchViewMask.addClass('show');
15587         }
15588         
15589         this.doTouchViewQuery();
15590         
15591     },
15592     
15593     hideTouchView : function()
15594     {
15595         this.touchViewEl.removeClass('in');
15596
15597         if(this.animate){
15598             var _this = this;
15599             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15600         }else{
15601             this.touchViewEl.setStyle('display', 'none');
15602         }
15603         
15604         if(this._touchViewMask){
15605             this._touchViewMask.removeClass('show');
15606             Roo.get(document.body).removeClass("x-body-masked");
15607         }
15608     },
15609     
15610     setTouchViewValue : function()
15611     {
15612         if(this.multiple){
15613             this.clearItem();
15614         
15615             var _this = this;
15616
15617             Roo.each(this.tickItems, function(o){
15618                 this.addItem(o);
15619             }, this);
15620         }
15621         
15622         this.hideTouchView();
15623     },
15624     
15625     doTouchViewQuery : function()
15626     {
15627         var qe = {
15628             query: '',
15629             forceAll: true,
15630             combo: this,
15631             cancel:false
15632         };
15633         
15634         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15635             return false;
15636         }
15637         
15638         if(!this.alwaysQuery || this.mode == 'local'){
15639             this.onTouchViewLoad();
15640             return;
15641         }
15642         
15643         this.store.load();
15644     },
15645     
15646     onTouchViewBeforeLoad : function(combo,opts)
15647     {
15648         return;
15649     },
15650
15651     // private
15652     onTouchViewLoad : function()
15653     {
15654         if(this.store.getCount() < 1){
15655             this.onTouchViewEmptyResults();
15656             return;
15657         }
15658         
15659         this.clearTouchView();
15660         
15661         var rawValue = this.getRawValue();
15662         
15663         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15664         
15665         this.tickItems = [];
15666         
15667         this.store.data.each(function(d, rowIndex){
15668             var row = this.touchViewListGroup.createChild(template);
15669             
15670             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15671                 row.addClass(d.data.cls);
15672             }
15673             
15674             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15675                 var cfg = {
15676                     data : d.data,
15677                     html : d.data[this.displayField]
15678                 };
15679                 
15680                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15681                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15682                 }
15683             }
15684             row.removeClass('selected');
15685             if(!this.multiple && this.valueField &&
15686                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15687             {
15688                 // radio buttons..
15689                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15690                 row.addClass('selected');
15691             }
15692             
15693             if(this.multiple && this.valueField &&
15694                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15695             {
15696                 
15697                 // checkboxes...
15698                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15699                 this.tickItems.push(d.data);
15700             }
15701             
15702             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15703             
15704         }, this);
15705         
15706         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15707         
15708         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15709
15710         if(this.modalTitle.length){
15711             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15712         }
15713
15714         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15715         
15716         if(this.mobile_restrict_height && listHeight < bodyHeight){
15717             this.touchViewBodyEl.setHeight(listHeight);
15718         }
15719         
15720         var _this = this;
15721         
15722         if(firstChecked && listHeight > bodyHeight){
15723             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15724         }
15725         
15726     },
15727     
15728     onTouchViewLoadException : function()
15729     {
15730         this.hideTouchView();
15731     },
15732     
15733     onTouchViewEmptyResults : function()
15734     {
15735         this.clearTouchView();
15736         
15737         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15738         
15739         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15740         
15741     },
15742     
15743     clearTouchView : function()
15744     {
15745         this.touchViewListGroup.dom.innerHTML = '';
15746     },
15747     
15748     onTouchViewClick : function(e, el, o)
15749     {
15750         e.preventDefault();
15751         
15752         var row = o.row;
15753         var rowIndex = o.rowIndex;
15754         
15755         var r = this.store.getAt(rowIndex);
15756         
15757         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15758             
15759             if(!this.multiple){
15760                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15761                     c.dom.removeAttribute('checked');
15762                 }, this);
15763
15764                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15765
15766                 this.setFromData(r.data);
15767
15768                 var close = this.closeTriggerEl();
15769
15770                 if(close){
15771                     close.show();
15772                 }
15773
15774                 this.hideTouchView();
15775
15776                 this.fireEvent('select', this, r, rowIndex);
15777
15778                 return;
15779             }
15780
15781             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15782                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15783                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15784                 return;
15785             }
15786
15787             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15788             this.addItem(r.data);
15789             this.tickItems.push(r.data);
15790         }
15791     },
15792     
15793     getAutoCreateNativeIOS : function()
15794     {
15795         var cfg = {
15796             cls: 'form-group' //input-group,
15797         };
15798         
15799         var combobox =  {
15800             tag: 'select',
15801             cls : 'roo-ios-select'
15802         };
15803         
15804         if (this.name) {
15805             combobox.name = this.name;
15806         }
15807         
15808         if (this.disabled) {
15809             combobox.disabled = true;
15810         }
15811         
15812         var settings = this;
15813         
15814         ['xs','sm','md','lg'].map(function(size){
15815             if (settings[size]) {
15816                 cfg.cls += ' col-' + size + '-' + settings[size];
15817             }
15818         });
15819         
15820         cfg.cn = combobox;
15821         
15822         return cfg;
15823         
15824     },
15825     
15826     initIOSView : function()
15827     {
15828         this.store.on('load', this.onIOSViewLoad, this);
15829         
15830         return;
15831     },
15832     
15833     onIOSViewLoad : function()
15834     {
15835         if(this.store.getCount() < 1){
15836             return;
15837         }
15838         
15839         this.clearIOSView();
15840         
15841         if(this.allowBlank) {
15842             
15843             var default_text = '-- SELECT --';
15844             
15845             if(this.placeholder.length){
15846                 default_text = this.placeholder;
15847             }
15848             
15849             if(this.emptyTitle.length){
15850                 default_text += ' - ' + this.emptyTitle + ' -';
15851             }
15852             
15853             var opt = this.inputEl().createChild({
15854                 tag: 'option',
15855                 value : 0,
15856                 html : default_text
15857             });
15858             
15859             var o = {};
15860             o[this.valueField] = 0;
15861             o[this.displayField] = default_text;
15862             
15863             this.ios_options.push({
15864                 data : o,
15865                 el : opt
15866             });
15867             
15868         }
15869         
15870         this.store.data.each(function(d, rowIndex){
15871             
15872             var html = '';
15873             
15874             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15875                 html = d.data[this.displayField];
15876             }
15877             
15878             var value = '';
15879             
15880             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15881                 value = d.data[this.valueField];
15882             }
15883             
15884             var option = {
15885                 tag: 'option',
15886                 value : value,
15887                 html : html
15888             };
15889             
15890             if(this.value == d.data[this.valueField]){
15891                 option['selected'] = true;
15892             }
15893             
15894             var opt = this.inputEl().createChild(option);
15895             
15896             this.ios_options.push({
15897                 data : d.data,
15898                 el : opt
15899             });
15900             
15901         }, this);
15902         
15903         this.inputEl().on('change', function(){
15904            this.fireEvent('select', this);
15905         }, this);
15906         
15907     },
15908     
15909     clearIOSView: function()
15910     {
15911         this.inputEl().dom.innerHTML = '';
15912         
15913         this.ios_options = [];
15914     },
15915     
15916     setIOSValue: function(v)
15917     {
15918         this.value = v;
15919         
15920         if(!this.ios_options){
15921             return;
15922         }
15923         
15924         Roo.each(this.ios_options, function(opts){
15925            
15926            opts.el.dom.removeAttribute('selected');
15927            
15928            if(opts.data[this.valueField] != v){
15929                return;
15930            }
15931            
15932            opts.el.dom.setAttribute('selected', true);
15933            
15934         }, this);
15935     }
15936
15937     /** 
15938     * @cfg {Boolean} grow 
15939     * @hide 
15940     */
15941     /** 
15942     * @cfg {Number} growMin 
15943     * @hide 
15944     */
15945     /** 
15946     * @cfg {Number} growMax 
15947     * @hide 
15948     */
15949     /**
15950      * @hide
15951      * @method autoSize
15952      */
15953 });
15954
15955 Roo.apply(Roo.bootstrap.ComboBox,  {
15956     
15957     header : {
15958         tag: 'div',
15959         cls: 'modal-header',
15960         cn: [
15961             {
15962                 tag: 'h4',
15963                 cls: 'modal-title'
15964             }
15965         ]
15966     },
15967     
15968     body : {
15969         tag: 'div',
15970         cls: 'modal-body',
15971         cn: [
15972             {
15973                 tag: 'ul',
15974                 cls: 'list-group'
15975             }
15976         ]
15977     },
15978     
15979     listItemRadio : {
15980         tag: 'li',
15981         cls: 'list-group-item',
15982         cn: [
15983             {
15984                 tag: 'span',
15985                 cls: 'roo-combobox-list-group-item-value'
15986             },
15987             {
15988                 tag: 'div',
15989                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15990                 cn: [
15991                     {
15992                         tag: 'input',
15993                         type: 'radio'
15994                     },
15995                     {
15996                         tag: 'label'
15997                     }
15998                 ]
15999             }
16000         ]
16001     },
16002     
16003     listItemCheckbox : {
16004         tag: 'li',
16005         cls: 'list-group-item',
16006         cn: [
16007             {
16008                 tag: 'span',
16009                 cls: 'roo-combobox-list-group-item-value'
16010             },
16011             {
16012                 tag: 'div',
16013                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16014                 cn: [
16015                     {
16016                         tag: 'input',
16017                         type: 'checkbox'
16018                     },
16019                     {
16020                         tag: 'label'
16021                     }
16022                 ]
16023             }
16024         ]
16025     },
16026     
16027     emptyResult : {
16028         tag: 'div',
16029         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16030     },
16031     
16032     footer : {
16033         tag: 'div',
16034         cls: 'modal-footer',
16035         cn: [
16036             {
16037                 tag: 'div',
16038                 cls: 'row',
16039                 cn: [
16040                     {
16041                         tag: 'div',
16042                         cls: 'col-xs-6 text-left',
16043                         cn: {
16044                             tag: 'button',
16045                             cls: 'btn btn-danger roo-touch-view-cancel',
16046                             html: 'Cancel'
16047                         }
16048                     },
16049                     {
16050                         tag: 'div',
16051                         cls: 'col-xs-6 text-right',
16052                         cn: {
16053                             tag: 'button',
16054                             cls: 'btn btn-success roo-touch-view-ok',
16055                             html: 'OK'
16056                         }
16057                     }
16058                 ]
16059             }
16060         ]
16061         
16062     }
16063 });
16064
16065 Roo.apply(Roo.bootstrap.ComboBox,  {
16066     
16067     touchViewTemplate : {
16068         tag: 'div',
16069         cls: 'modal fade roo-combobox-touch-view',
16070         cn: [
16071             {
16072                 tag: 'div',
16073                 cls: 'modal-dialog',
16074                 style : 'position:fixed', // we have to fix position....
16075                 cn: [
16076                     {
16077                         tag: 'div',
16078                         cls: 'modal-content',
16079                         cn: [
16080                             Roo.bootstrap.ComboBox.header,
16081                             Roo.bootstrap.ComboBox.body,
16082                             Roo.bootstrap.ComboBox.footer
16083                         ]
16084                     }
16085                 ]
16086             }
16087         ]
16088     }
16089 });/*
16090  * Based on:
16091  * Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  *
16094  * Originally Released Under LGPL - original licence link has changed is not relivant.
16095  *
16096  * Fork - LGPL
16097  * <script type="text/javascript">
16098  */
16099
16100 /**
16101  * @class Roo.View
16102  * @extends Roo.util.Observable
16103  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16104  * This class also supports single and multi selection modes. <br>
16105  * Create a data model bound view:
16106  <pre><code>
16107  var store = new Roo.data.Store(...);
16108
16109  var view = new Roo.View({
16110     el : "my-element",
16111     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16112  
16113     singleSelect: true,
16114     selectedClass: "ydataview-selected",
16115     store: store
16116  });
16117
16118  // listen for node click?
16119  view.on("click", function(vw, index, node, e){
16120  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16121  });
16122
16123  // load XML data
16124  dataModel.load("foobar.xml");
16125  </code></pre>
16126  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16127  * <br><br>
16128  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16129  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16130  * 
16131  * Note: old style constructor is still suported (container, template, config)
16132  * 
16133  * @constructor
16134  * Create a new View
16135  * @param {Object} config The config object
16136  * 
16137  */
16138 Roo.View = function(config, depreciated_tpl, depreciated_config){
16139     
16140     this.parent = false;
16141     
16142     if (typeof(depreciated_tpl) == 'undefined') {
16143         // new way.. - universal constructor.
16144         Roo.apply(this, config);
16145         this.el  = Roo.get(this.el);
16146     } else {
16147         // old format..
16148         this.el  = Roo.get(config);
16149         this.tpl = depreciated_tpl;
16150         Roo.apply(this, depreciated_config);
16151     }
16152     this.wrapEl  = this.el.wrap().wrap();
16153     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16154     
16155     
16156     if(typeof(this.tpl) == "string"){
16157         this.tpl = new Roo.Template(this.tpl);
16158     } else {
16159         // support xtype ctors..
16160         this.tpl = new Roo.factory(this.tpl, Roo);
16161     }
16162     
16163     
16164     this.tpl.compile();
16165     
16166     /** @private */
16167     this.addEvents({
16168         /**
16169          * @event beforeclick
16170          * Fires before a click is processed. Returns false to cancel the default action.
16171          * @param {Roo.View} this
16172          * @param {Number} index The index of the target node
16173          * @param {HTMLElement} node The target node
16174          * @param {Roo.EventObject} e The raw event object
16175          */
16176             "beforeclick" : true,
16177         /**
16178          * @event click
16179          * Fires when a template node is clicked.
16180          * @param {Roo.View} this
16181          * @param {Number} index The index of the target node
16182          * @param {HTMLElement} node The target node
16183          * @param {Roo.EventObject} e The raw event object
16184          */
16185             "click" : true,
16186         /**
16187          * @event dblclick
16188          * Fires when a template node is double clicked.
16189          * @param {Roo.View} this
16190          * @param {Number} index The index of the target node
16191          * @param {HTMLElement} node The target node
16192          * @param {Roo.EventObject} e The raw event object
16193          */
16194             "dblclick" : true,
16195         /**
16196          * @event contextmenu
16197          * Fires when a template node is right clicked.
16198          * @param {Roo.View} this
16199          * @param {Number} index The index of the target node
16200          * @param {HTMLElement} node The target node
16201          * @param {Roo.EventObject} e The raw event object
16202          */
16203             "contextmenu" : true,
16204         /**
16205          * @event selectionchange
16206          * Fires when the selected nodes change.
16207          * @param {Roo.View} this
16208          * @param {Array} selections Array of the selected nodes
16209          */
16210             "selectionchange" : true,
16211     
16212         /**
16213          * @event beforeselect
16214          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16215          * @param {Roo.View} this
16216          * @param {HTMLElement} node The node to be selected
16217          * @param {Array} selections Array of currently selected nodes
16218          */
16219             "beforeselect" : true,
16220         /**
16221          * @event preparedata
16222          * Fires on every row to render, to allow you to change the data.
16223          * @param {Roo.View} this
16224          * @param {Object} data to be rendered (change this)
16225          */
16226           "preparedata" : true
16227           
16228           
16229         });
16230
16231
16232
16233     this.el.on({
16234         "click": this.onClick,
16235         "dblclick": this.onDblClick,
16236         "contextmenu": this.onContextMenu,
16237         scope:this
16238     });
16239
16240     this.selections = [];
16241     this.nodes = [];
16242     this.cmp = new Roo.CompositeElementLite([]);
16243     if(this.store){
16244         this.store = Roo.factory(this.store, Roo.data);
16245         this.setStore(this.store, true);
16246     }
16247     
16248     if ( this.footer && this.footer.xtype) {
16249            
16250          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16251         
16252         this.footer.dataSource = this.store;
16253         this.footer.container = fctr;
16254         this.footer = Roo.factory(this.footer, Roo);
16255         fctr.insertFirst(this.el);
16256         
16257         // this is a bit insane - as the paging toolbar seems to detach the el..
16258 //        dom.parentNode.parentNode.parentNode
16259          // they get detached?
16260     }
16261     
16262     
16263     Roo.View.superclass.constructor.call(this);
16264     
16265     
16266 };
16267
16268 Roo.extend(Roo.View, Roo.util.Observable, {
16269     
16270      /**
16271      * @cfg {Roo.data.Store} store Data store to load data from.
16272      */
16273     store : false,
16274     
16275     /**
16276      * @cfg {String|Roo.Element} el The container element.
16277      */
16278     el : '',
16279     
16280     /**
16281      * @cfg {String|Roo.Template} tpl The template used by this View 
16282      */
16283     tpl : false,
16284     /**
16285      * @cfg {String} dataName the named area of the template to use as the data area
16286      *                          Works with domtemplates roo-name="name"
16287      */
16288     dataName: false,
16289     /**
16290      * @cfg {String} selectedClass The css class to add to selected nodes
16291      */
16292     selectedClass : "x-view-selected",
16293      /**
16294      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16295      */
16296     emptyText : "",
16297     
16298     /**
16299      * @cfg {String} text to display on mask (default Loading)
16300      */
16301     mask : false,
16302     /**
16303      * @cfg {Boolean} multiSelect Allow multiple selection
16304      */
16305     multiSelect : false,
16306     /**
16307      * @cfg {Boolean} singleSelect Allow single selection
16308      */
16309     singleSelect:  false,
16310     
16311     /**
16312      * @cfg {Boolean} toggleSelect - selecting 
16313      */
16314     toggleSelect : false,
16315     
16316     /**
16317      * @cfg {Boolean} tickable - selecting 
16318      */
16319     tickable : false,
16320     
16321     /**
16322      * Returns the element this view is bound to.
16323      * @return {Roo.Element}
16324      */
16325     getEl : function(){
16326         return this.wrapEl;
16327     },
16328     
16329     
16330
16331     /**
16332      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16333      */
16334     refresh : function(){
16335         //Roo.log('refresh');
16336         var t = this.tpl;
16337         
16338         // if we are using something like 'domtemplate', then
16339         // the what gets used is:
16340         // t.applySubtemplate(NAME, data, wrapping data..)
16341         // the outer template then get' applied with
16342         //     the store 'extra data'
16343         // and the body get's added to the
16344         //      roo-name="data" node?
16345         //      <span class='roo-tpl-{name}'></span> ?????
16346         
16347         
16348         
16349         this.clearSelections();
16350         this.el.update("");
16351         var html = [];
16352         var records = this.store.getRange();
16353         if(records.length < 1) {
16354             
16355             // is this valid??  = should it render a template??
16356             
16357             this.el.update(this.emptyText);
16358             return;
16359         }
16360         var el = this.el;
16361         if (this.dataName) {
16362             this.el.update(t.apply(this.store.meta)); //????
16363             el = this.el.child('.roo-tpl-' + this.dataName);
16364         }
16365         
16366         for(var i = 0, len = records.length; i < len; i++){
16367             var data = this.prepareData(records[i].data, i, records[i]);
16368             this.fireEvent("preparedata", this, data, i, records[i]);
16369             
16370             var d = Roo.apply({}, data);
16371             
16372             if(this.tickable){
16373                 Roo.apply(d, {'roo-id' : Roo.id()});
16374                 
16375                 var _this = this;
16376             
16377                 Roo.each(this.parent.item, function(item){
16378                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16379                         return;
16380                     }
16381                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16382                 });
16383             }
16384             
16385             html[html.length] = Roo.util.Format.trim(
16386                 this.dataName ?
16387                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16388                     t.apply(d)
16389             );
16390         }
16391         
16392         
16393         
16394         el.update(html.join(""));
16395         this.nodes = el.dom.childNodes;
16396         this.updateIndexes(0);
16397     },
16398     
16399
16400     /**
16401      * Function to override to reformat the data that is sent to
16402      * the template for each node.
16403      * DEPRICATED - use the preparedata event handler.
16404      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16405      * a JSON object for an UpdateManager bound view).
16406      */
16407     prepareData : function(data, index, record)
16408     {
16409         this.fireEvent("preparedata", this, data, index, record);
16410         return data;
16411     },
16412
16413     onUpdate : function(ds, record){
16414         // Roo.log('on update');   
16415         this.clearSelections();
16416         var index = this.store.indexOf(record);
16417         var n = this.nodes[index];
16418         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16419         n.parentNode.removeChild(n);
16420         this.updateIndexes(index, index);
16421     },
16422
16423     
16424     
16425 // --------- FIXME     
16426     onAdd : function(ds, records, index)
16427     {
16428         //Roo.log(['on Add', ds, records, index] );        
16429         this.clearSelections();
16430         if(this.nodes.length == 0){
16431             this.refresh();
16432             return;
16433         }
16434         var n = this.nodes[index];
16435         for(var i = 0, len = records.length; i < len; i++){
16436             var d = this.prepareData(records[i].data, i, records[i]);
16437             if(n){
16438                 this.tpl.insertBefore(n, d);
16439             }else{
16440                 
16441                 this.tpl.append(this.el, d);
16442             }
16443         }
16444         this.updateIndexes(index);
16445     },
16446
16447     onRemove : function(ds, record, index){
16448        // Roo.log('onRemove');
16449         this.clearSelections();
16450         var el = this.dataName  ?
16451             this.el.child('.roo-tpl-' + this.dataName) :
16452             this.el; 
16453         
16454         el.dom.removeChild(this.nodes[index]);
16455         this.updateIndexes(index);
16456     },
16457
16458     /**
16459      * Refresh an individual node.
16460      * @param {Number} index
16461      */
16462     refreshNode : function(index){
16463         this.onUpdate(this.store, this.store.getAt(index));
16464     },
16465
16466     updateIndexes : function(startIndex, endIndex){
16467         var ns = this.nodes;
16468         startIndex = startIndex || 0;
16469         endIndex = endIndex || ns.length - 1;
16470         for(var i = startIndex; i <= endIndex; i++){
16471             ns[i].nodeIndex = i;
16472         }
16473     },
16474
16475     /**
16476      * Changes the data store this view uses and refresh the view.
16477      * @param {Store} store
16478      */
16479     setStore : function(store, initial){
16480         if(!initial && this.store){
16481             this.store.un("datachanged", this.refresh);
16482             this.store.un("add", this.onAdd);
16483             this.store.un("remove", this.onRemove);
16484             this.store.un("update", this.onUpdate);
16485             this.store.un("clear", this.refresh);
16486             this.store.un("beforeload", this.onBeforeLoad);
16487             this.store.un("load", this.onLoad);
16488             this.store.un("loadexception", this.onLoad);
16489         }
16490         if(store){
16491           
16492             store.on("datachanged", this.refresh, this);
16493             store.on("add", this.onAdd, this);
16494             store.on("remove", this.onRemove, this);
16495             store.on("update", this.onUpdate, this);
16496             store.on("clear", this.refresh, this);
16497             store.on("beforeload", this.onBeforeLoad, this);
16498             store.on("load", this.onLoad, this);
16499             store.on("loadexception", this.onLoad, this);
16500         }
16501         
16502         if(store){
16503             this.refresh();
16504         }
16505     },
16506     /**
16507      * onbeforeLoad - masks the loading area.
16508      *
16509      */
16510     onBeforeLoad : function(store,opts)
16511     {
16512          //Roo.log('onBeforeLoad');   
16513         if (!opts.add) {
16514             this.el.update("");
16515         }
16516         this.el.mask(this.mask ? this.mask : "Loading" ); 
16517     },
16518     onLoad : function ()
16519     {
16520         this.el.unmask();
16521     },
16522     
16523
16524     /**
16525      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16526      * @param {HTMLElement} node
16527      * @return {HTMLElement} The template node
16528      */
16529     findItemFromChild : function(node){
16530         var el = this.dataName  ?
16531             this.el.child('.roo-tpl-' + this.dataName,true) :
16532             this.el.dom; 
16533         
16534         if(!node || node.parentNode == el){
16535                     return node;
16536             }
16537             var p = node.parentNode;
16538             while(p && p != el){
16539             if(p.parentNode == el){
16540                 return p;
16541             }
16542             p = p.parentNode;
16543         }
16544             return null;
16545     },
16546
16547     /** @ignore */
16548     onClick : function(e){
16549         var item = this.findItemFromChild(e.getTarget());
16550         if(item){
16551             var index = this.indexOf(item);
16552             if(this.onItemClick(item, index, e) !== false){
16553                 this.fireEvent("click", this, index, item, e);
16554             }
16555         }else{
16556             this.clearSelections();
16557         }
16558     },
16559
16560     /** @ignore */
16561     onContextMenu : function(e){
16562         var item = this.findItemFromChild(e.getTarget());
16563         if(item){
16564             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16565         }
16566     },
16567
16568     /** @ignore */
16569     onDblClick : function(e){
16570         var item = this.findItemFromChild(e.getTarget());
16571         if(item){
16572             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16573         }
16574     },
16575
16576     onItemClick : function(item, index, e)
16577     {
16578         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16579             return false;
16580         }
16581         if (this.toggleSelect) {
16582             var m = this.isSelected(item) ? 'unselect' : 'select';
16583             //Roo.log(m);
16584             var _t = this;
16585             _t[m](item, true, false);
16586             return true;
16587         }
16588         if(this.multiSelect || this.singleSelect){
16589             if(this.multiSelect && e.shiftKey && this.lastSelection){
16590                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16591             }else{
16592                 this.select(item, this.multiSelect && e.ctrlKey);
16593                 this.lastSelection = item;
16594             }
16595             
16596             if(!this.tickable){
16597                 e.preventDefault();
16598             }
16599             
16600         }
16601         return true;
16602     },
16603
16604     /**
16605      * Get the number of selected nodes.
16606      * @return {Number}
16607      */
16608     getSelectionCount : function(){
16609         return this.selections.length;
16610     },
16611
16612     /**
16613      * Get the currently selected nodes.
16614      * @return {Array} An array of HTMLElements
16615      */
16616     getSelectedNodes : function(){
16617         return this.selections;
16618     },
16619
16620     /**
16621      * Get the indexes of the selected nodes.
16622      * @return {Array}
16623      */
16624     getSelectedIndexes : function(){
16625         var indexes = [], s = this.selections;
16626         for(var i = 0, len = s.length; i < len; i++){
16627             indexes.push(s[i].nodeIndex);
16628         }
16629         return indexes;
16630     },
16631
16632     /**
16633      * Clear all selections
16634      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16635      */
16636     clearSelections : function(suppressEvent){
16637         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16638             this.cmp.elements = this.selections;
16639             this.cmp.removeClass(this.selectedClass);
16640             this.selections = [];
16641             if(!suppressEvent){
16642                 this.fireEvent("selectionchange", this, this.selections);
16643             }
16644         }
16645     },
16646
16647     /**
16648      * Returns true if the passed node is selected
16649      * @param {HTMLElement/Number} node The node or node index
16650      * @return {Boolean}
16651      */
16652     isSelected : function(node){
16653         var s = this.selections;
16654         if(s.length < 1){
16655             return false;
16656         }
16657         node = this.getNode(node);
16658         return s.indexOf(node) !== -1;
16659     },
16660
16661     /**
16662      * Selects nodes.
16663      * @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
16664      * @param {Boolean} keepExisting (optional) true to keep existing selections
16665      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16666      */
16667     select : function(nodeInfo, keepExisting, suppressEvent){
16668         if(nodeInfo instanceof Array){
16669             if(!keepExisting){
16670                 this.clearSelections(true);
16671             }
16672             for(var i = 0, len = nodeInfo.length; i < len; i++){
16673                 this.select(nodeInfo[i], true, true);
16674             }
16675             return;
16676         } 
16677         var node = this.getNode(nodeInfo);
16678         if(!node || this.isSelected(node)){
16679             return; // already selected.
16680         }
16681         if(!keepExisting){
16682             this.clearSelections(true);
16683         }
16684         
16685         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16686             Roo.fly(node).addClass(this.selectedClass);
16687             this.selections.push(node);
16688             if(!suppressEvent){
16689                 this.fireEvent("selectionchange", this, this.selections);
16690             }
16691         }
16692         
16693         
16694     },
16695       /**
16696      * Unselects nodes.
16697      * @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
16698      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16699      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16700      */
16701     unselect : function(nodeInfo, keepExisting, suppressEvent)
16702     {
16703         if(nodeInfo instanceof Array){
16704             Roo.each(this.selections, function(s) {
16705                 this.unselect(s, nodeInfo);
16706             }, this);
16707             return;
16708         }
16709         var node = this.getNode(nodeInfo);
16710         if(!node || !this.isSelected(node)){
16711             //Roo.log("not selected");
16712             return; // not selected.
16713         }
16714         // fireevent???
16715         var ns = [];
16716         Roo.each(this.selections, function(s) {
16717             if (s == node ) {
16718                 Roo.fly(node).removeClass(this.selectedClass);
16719
16720                 return;
16721             }
16722             ns.push(s);
16723         },this);
16724         
16725         this.selections= ns;
16726         this.fireEvent("selectionchange", this, this.selections);
16727     },
16728
16729     /**
16730      * Gets a template node.
16731      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16732      * @return {HTMLElement} The node or null if it wasn't found
16733      */
16734     getNode : function(nodeInfo){
16735         if(typeof nodeInfo == "string"){
16736             return document.getElementById(nodeInfo);
16737         }else if(typeof nodeInfo == "number"){
16738             return this.nodes[nodeInfo];
16739         }
16740         return nodeInfo;
16741     },
16742
16743     /**
16744      * Gets a range template nodes.
16745      * @param {Number} startIndex
16746      * @param {Number} endIndex
16747      * @return {Array} An array of nodes
16748      */
16749     getNodes : function(start, end){
16750         var ns = this.nodes;
16751         start = start || 0;
16752         end = typeof end == "undefined" ? ns.length - 1 : end;
16753         var nodes = [];
16754         if(start <= end){
16755             for(var i = start; i <= end; i++){
16756                 nodes.push(ns[i]);
16757             }
16758         } else{
16759             for(var i = start; i >= end; i--){
16760                 nodes.push(ns[i]);
16761             }
16762         }
16763         return nodes;
16764     },
16765
16766     /**
16767      * Finds the index of the passed node
16768      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16769      * @return {Number} The index of the node or -1
16770      */
16771     indexOf : function(node){
16772         node = this.getNode(node);
16773         if(typeof node.nodeIndex == "number"){
16774             return node.nodeIndex;
16775         }
16776         var ns = this.nodes;
16777         for(var i = 0, len = ns.length; i < len; i++){
16778             if(ns[i] == node){
16779                 return i;
16780             }
16781         }
16782         return -1;
16783     }
16784 });
16785 /*
16786  * - LGPL
16787  *
16788  * based on jquery fullcalendar
16789  * 
16790  */
16791
16792 Roo.bootstrap = Roo.bootstrap || {};
16793 /**
16794  * @class Roo.bootstrap.Calendar
16795  * @extends Roo.bootstrap.Component
16796  * Bootstrap Calendar class
16797  * @cfg {Boolean} loadMask (true|false) default false
16798  * @cfg {Object} header generate the user specific header of the calendar, default false
16799
16800  * @constructor
16801  * Create a new Container
16802  * @param {Object} config The config object
16803  */
16804
16805
16806
16807 Roo.bootstrap.Calendar = function(config){
16808     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16809      this.addEvents({
16810         /**
16811              * @event select
16812              * Fires when a date is selected
16813              * @param {DatePicker} this
16814              * @param {Date} date The selected date
16815              */
16816         'select': true,
16817         /**
16818              * @event monthchange
16819              * Fires when the displayed month changes 
16820              * @param {DatePicker} this
16821              * @param {Date} date The selected month
16822              */
16823         'monthchange': true,
16824         /**
16825              * @event evententer
16826              * Fires when mouse over an event
16827              * @param {Calendar} this
16828              * @param {event} Event
16829              */
16830         'evententer': true,
16831         /**
16832              * @event eventleave
16833              * Fires when the mouse leaves an
16834              * @param {Calendar} this
16835              * @param {event}
16836              */
16837         'eventleave': true,
16838         /**
16839              * @event eventclick
16840              * Fires when the mouse click an
16841              * @param {Calendar} this
16842              * @param {event}
16843              */
16844         'eventclick': true
16845         
16846     });
16847
16848 };
16849
16850 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16851     
16852      /**
16853      * @cfg {Number} startDay
16854      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16855      */
16856     startDay : 0,
16857     
16858     loadMask : false,
16859     
16860     header : false,
16861       
16862     getAutoCreate : function(){
16863         
16864         
16865         var fc_button = function(name, corner, style, content ) {
16866             return Roo.apply({},{
16867                 tag : 'span',
16868                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16869                          (corner.length ?
16870                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16871                             ''
16872                         ),
16873                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16874                 unselectable: 'on'
16875             });
16876         };
16877         
16878         var header = {};
16879         
16880         if(!this.header){
16881             header = {
16882                 tag : 'table',
16883                 cls : 'fc-header',
16884                 style : 'width:100%',
16885                 cn : [
16886                     {
16887                         tag: 'tr',
16888                         cn : [
16889                             {
16890                                 tag : 'td',
16891                                 cls : 'fc-header-left',
16892                                 cn : [
16893                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16894                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16895                                     { tag: 'span', cls: 'fc-header-space' },
16896                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16897
16898
16899                                 ]
16900                             },
16901
16902                             {
16903                                 tag : 'td',
16904                                 cls : 'fc-header-center',
16905                                 cn : [
16906                                     {
16907                                         tag: 'span',
16908                                         cls: 'fc-header-title',
16909                                         cn : {
16910                                             tag: 'H2',
16911                                             html : 'month / year'
16912                                         }
16913                                     }
16914
16915                                 ]
16916                             },
16917                             {
16918                                 tag : 'td',
16919                                 cls : 'fc-header-right',
16920                                 cn : [
16921                               /*      fc_button('month', 'left', '', 'month' ),
16922                                     fc_button('week', '', '', 'week' ),
16923                                     fc_button('day', 'right', '', 'day' )
16924                                 */    
16925
16926                                 ]
16927                             }
16928
16929                         ]
16930                     }
16931                 ]
16932             };
16933         }
16934         
16935         header = this.header;
16936         
16937        
16938         var cal_heads = function() {
16939             var ret = [];
16940             // fixme - handle this.
16941             
16942             for (var i =0; i < Date.dayNames.length; i++) {
16943                 var d = Date.dayNames[i];
16944                 ret.push({
16945                     tag: 'th',
16946                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16947                     html : d.substring(0,3)
16948                 });
16949                 
16950             }
16951             ret[0].cls += ' fc-first';
16952             ret[6].cls += ' fc-last';
16953             return ret;
16954         };
16955         var cal_cell = function(n) {
16956             return  {
16957                 tag: 'td',
16958                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16959                 cn : [
16960                     {
16961                         cn : [
16962                             {
16963                                 cls: 'fc-day-number',
16964                                 html: 'D'
16965                             },
16966                             {
16967                                 cls: 'fc-day-content',
16968                              
16969                                 cn : [
16970                                      {
16971                                         style: 'position: relative;' // height: 17px;
16972                                     }
16973                                 ]
16974                             }
16975                             
16976                             
16977                         ]
16978                     }
16979                 ]
16980                 
16981             }
16982         };
16983         var cal_rows = function() {
16984             
16985             var ret = [];
16986             for (var r = 0; r < 6; r++) {
16987                 var row= {
16988                     tag : 'tr',
16989                     cls : 'fc-week',
16990                     cn : []
16991                 };
16992                 
16993                 for (var i =0; i < Date.dayNames.length; i++) {
16994                     var d = Date.dayNames[i];
16995                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16996
16997                 }
16998                 row.cn[0].cls+=' fc-first';
16999                 row.cn[0].cn[0].style = 'min-height:90px';
17000                 row.cn[6].cls+=' fc-last';
17001                 ret.push(row);
17002                 
17003             }
17004             ret[0].cls += ' fc-first';
17005             ret[4].cls += ' fc-prev-last';
17006             ret[5].cls += ' fc-last';
17007             return ret;
17008             
17009         };
17010         
17011         var cal_table = {
17012             tag: 'table',
17013             cls: 'fc-border-separate',
17014             style : 'width:100%',
17015             cellspacing  : 0,
17016             cn : [
17017                 { 
17018                     tag: 'thead',
17019                     cn : [
17020                         { 
17021                             tag: 'tr',
17022                             cls : 'fc-first fc-last',
17023                             cn : cal_heads()
17024                         }
17025                     ]
17026                 },
17027                 { 
17028                     tag: 'tbody',
17029                     cn : cal_rows()
17030                 }
17031                   
17032             ]
17033         };
17034          
17035          var cfg = {
17036             cls : 'fc fc-ltr',
17037             cn : [
17038                 header,
17039                 {
17040                     cls : 'fc-content',
17041                     style : "position: relative;",
17042                     cn : [
17043                         {
17044                             cls : 'fc-view fc-view-month fc-grid',
17045                             style : 'position: relative',
17046                             unselectable : 'on',
17047                             cn : [
17048                                 {
17049                                     cls : 'fc-event-container',
17050                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17051                                 },
17052                                 cal_table
17053                             ]
17054                         }
17055                     ]
17056     
17057                 }
17058            ] 
17059             
17060         };
17061         
17062          
17063         
17064         return cfg;
17065     },
17066     
17067     
17068     initEvents : function()
17069     {
17070         if(!this.store){
17071             throw "can not find store for calendar";
17072         }
17073         
17074         var mark = {
17075             tag: "div",
17076             cls:"x-dlg-mask",
17077             style: "text-align:center",
17078             cn: [
17079                 {
17080                     tag: "div",
17081                     style: "background-color:white;width:50%;margin:250 auto",
17082                     cn: [
17083                         {
17084                             tag: "img",
17085                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17086                         },
17087                         {
17088                             tag: "span",
17089                             html: "Loading"
17090                         }
17091                         
17092                     ]
17093                 }
17094             ]
17095         };
17096         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17097         
17098         var size = this.el.select('.fc-content', true).first().getSize();
17099         this.maskEl.setSize(size.width, size.height);
17100         this.maskEl.enableDisplayMode("block");
17101         if(!this.loadMask){
17102             this.maskEl.hide();
17103         }
17104         
17105         this.store = Roo.factory(this.store, Roo.data);
17106         this.store.on('load', this.onLoad, this);
17107         this.store.on('beforeload', this.onBeforeLoad, this);
17108         
17109         this.resize();
17110         
17111         this.cells = this.el.select('.fc-day',true);
17112         //Roo.log(this.cells);
17113         this.textNodes = this.el.query('.fc-day-number');
17114         this.cells.addClassOnOver('fc-state-hover');
17115         
17116         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17117         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17118         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17119         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17120         
17121         this.on('monthchange', this.onMonthChange, this);
17122         
17123         this.update(new Date().clearTime());
17124     },
17125     
17126     resize : function() {
17127         var sz  = this.el.getSize();
17128         
17129         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17130         this.el.select('.fc-day-content div',true).setHeight(34);
17131     },
17132     
17133     
17134     // private
17135     showPrevMonth : function(e){
17136         this.update(this.activeDate.add("mo", -1));
17137     },
17138     showToday : function(e){
17139         this.update(new Date().clearTime());
17140     },
17141     // private
17142     showNextMonth : function(e){
17143         this.update(this.activeDate.add("mo", 1));
17144     },
17145
17146     // private
17147     showPrevYear : function(){
17148         this.update(this.activeDate.add("y", -1));
17149     },
17150
17151     // private
17152     showNextYear : function(){
17153         this.update(this.activeDate.add("y", 1));
17154     },
17155
17156     
17157    // private
17158     update : function(date)
17159     {
17160         var vd = this.activeDate;
17161         this.activeDate = date;
17162 //        if(vd && this.el){
17163 //            var t = date.getTime();
17164 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17165 //                Roo.log('using add remove');
17166 //                
17167 //                this.fireEvent('monthchange', this, date);
17168 //                
17169 //                this.cells.removeClass("fc-state-highlight");
17170 //                this.cells.each(function(c){
17171 //                   if(c.dateValue == t){
17172 //                       c.addClass("fc-state-highlight");
17173 //                       setTimeout(function(){
17174 //                            try{c.dom.firstChild.focus();}catch(e){}
17175 //                       }, 50);
17176 //                       return false;
17177 //                   }
17178 //                   return true;
17179 //                });
17180 //                return;
17181 //            }
17182 //        }
17183         
17184         var days = date.getDaysInMonth();
17185         
17186         var firstOfMonth = date.getFirstDateOfMonth();
17187         var startingPos = firstOfMonth.getDay()-this.startDay;
17188         
17189         if(startingPos < this.startDay){
17190             startingPos += 7;
17191         }
17192         
17193         var pm = date.add(Date.MONTH, -1);
17194         var prevStart = pm.getDaysInMonth()-startingPos;
17195 //        
17196         this.cells = this.el.select('.fc-day',true);
17197         this.textNodes = this.el.query('.fc-day-number');
17198         this.cells.addClassOnOver('fc-state-hover');
17199         
17200         var cells = this.cells.elements;
17201         var textEls = this.textNodes;
17202         
17203         Roo.each(cells, function(cell){
17204             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17205         });
17206         
17207         days += startingPos;
17208
17209         // convert everything to numbers so it's fast
17210         var day = 86400000;
17211         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17212         //Roo.log(d);
17213         //Roo.log(pm);
17214         //Roo.log(prevStart);
17215         
17216         var today = new Date().clearTime().getTime();
17217         var sel = date.clearTime().getTime();
17218         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17219         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17220         var ddMatch = this.disabledDatesRE;
17221         var ddText = this.disabledDatesText;
17222         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17223         var ddaysText = this.disabledDaysText;
17224         var format = this.format;
17225         
17226         var setCellClass = function(cal, cell){
17227             cell.row = 0;
17228             cell.events = [];
17229             cell.more = [];
17230             //Roo.log('set Cell Class');
17231             cell.title = "";
17232             var t = d.getTime();
17233             
17234             //Roo.log(d);
17235             
17236             cell.dateValue = t;
17237             if(t == today){
17238                 cell.className += " fc-today";
17239                 cell.className += " fc-state-highlight";
17240                 cell.title = cal.todayText;
17241             }
17242             if(t == sel){
17243                 // disable highlight in other month..
17244                 //cell.className += " fc-state-highlight";
17245                 
17246             }
17247             // disabling
17248             if(t < min) {
17249                 cell.className = " fc-state-disabled";
17250                 cell.title = cal.minText;
17251                 return;
17252             }
17253             if(t > max) {
17254                 cell.className = " fc-state-disabled";
17255                 cell.title = cal.maxText;
17256                 return;
17257             }
17258             if(ddays){
17259                 if(ddays.indexOf(d.getDay()) != -1){
17260                     cell.title = ddaysText;
17261                     cell.className = " fc-state-disabled";
17262                 }
17263             }
17264             if(ddMatch && format){
17265                 var fvalue = d.dateFormat(format);
17266                 if(ddMatch.test(fvalue)){
17267                     cell.title = ddText.replace("%0", fvalue);
17268                     cell.className = " fc-state-disabled";
17269                 }
17270             }
17271             
17272             if (!cell.initialClassName) {
17273                 cell.initialClassName = cell.dom.className;
17274             }
17275             
17276             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17277         };
17278
17279         var i = 0;
17280         
17281         for(; i < startingPos; i++) {
17282             textEls[i].innerHTML = (++prevStart);
17283             d.setDate(d.getDate()+1);
17284             
17285             cells[i].className = "fc-past fc-other-month";
17286             setCellClass(this, cells[i]);
17287         }
17288         
17289         var intDay = 0;
17290         
17291         for(; i < days; i++){
17292             intDay = i - startingPos + 1;
17293             textEls[i].innerHTML = (intDay);
17294             d.setDate(d.getDate()+1);
17295             
17296             cells[i].className = ''; // "x-date-active";
17297             setCellClass(this, cells[i]);
17298         }
17299         var extraDays = 0;
17300         
17301         for(; i < 42; i++) {
17302             textEls[i].innerHTML = (++extraDays);
17303             d.setDate(d.getDate()+1);
17304             
17305             cells[i].className = "fc-future fc-other-month";
17306             setCellClass(this, cells[i]);
17307         }
17308         
17309         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17310         
17311         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17312         
17313         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17314         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17315         
17316         if(totalRows != 6){
17317             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17318             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17319         }
17320         
17321         this.fireEvent('monthchange', this, date);
17322         
17323         
17324         /*
17325         if(!this.internalRender){
17326             var main = this.el.dom.firstChild;
17327             var w = main.offsetWidth;
17328             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17329             Roo.fly(main).setWidth(w);
17330             this.internalRender = true;
17331             // opera does not respect the auto grow header center column
17332             // then, after it gets a width opera refuses to recalculate
17333             // without a second pass
17334             if(Roo.isOpera && !this.secondPass){
17335                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17336                 this.secondPass = true;
17337                 this.update.defer(10, this, [date]);
17338             }
17339         }
17340         */
17341         
17342     },
17343     
17344     findCell : function(dt) {
17345         dt = dt.clearTime().getTime();
17346         var ret = false;
17347         this.cells.each(function(c){
17348             //Roo.log("check " +c.dateValue + '?=' + dt);
17349             if(c.dateValue == dt){
17350                 ret = c;
17351                 return false;
17352             }
17353             return true;
17354         });
17355         
17356         return ret;
17357     },
17358     
17359     findCells : function(ev) {
17360         var s = ev.start.clone().clearTime().getTime();
17361        // Roo.log(s);
17362         var e= ev.end.clone().clearTime().getTime();
17363        // Roo.log(e);
17364         var ret = [];
17365         this.cells.each(function(c){
17366              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17367             
17368             if(c.dateValue > e){
17369                 return ;
17370             }
17371             if(c.dateValue < s){
17372                 return ;
17373             }
17374             ret.push(c);
17375         });
17376         
17377         return ret;    
17378     },
17379     
17380 //    findBestRow: function(cells)
17381 //    {
17382 //        var ret = 0;
17383 //        
17384 //        for (var i =0 ; i < cells.length;i++) {
17385 //            ret  = Math.max(cells[i].rows || 0,ret);
17386 //        }
17387 //        return ret;
17388 //        
17389 //    },
17390     
17391     
17392     addItem : function(ev)
17393     {
17394         // look for vertical location slot in
17395         var cells = this.findCells(ev);
17396         
17397 //        ev.row = this.findBestRow(cells);
17398         
17399         // work out the location.
17400         
17401         var crow = false;
17402         var rows = [];
17403         for(var i =0; i < cells.length; i++) {
17404             
17405             cells[i].row = cells[0].row;
17406             
17407             if(i == 0){
17408                 cells[i].row = cells[i].row + 1;
17409             }
17410             
17411             if (!crow) {
17412                 crow = {
17413                     start : cells[i],
17414                     end :  cells[i]
17415                 };
17416                 continue;
17417             }
17418             if (crow.start.getY() == cells[i].getY()) {
17419                 // on same row.
17420                 crow.end = cells[i];
17421                 continue;
17422             }
17423             // different row.
17424             rows.push(crow);
17425             crow = {
17426                 start: cells[i],
17427                 end : cells[i]
17428             };
17429             
17430         }
17431         
17432         rows.push(crow);
17433         ev.els = [];
17434         ev.rows = rows;
17435         ev.cells = cells;
17436         
17437         cells[0].events.push(ev);
17438         
17439         this.calevents.push(ev);
17440     },
17441     
17442     clearEvents: function() {
17443         
17444         if(!this.calevents){
17445             return;
17446         }
17447         
17448         Roo.each(this.cells.elements, function(c){
17449             c.row = 0;
17450             c.events = [];
17451             c.more = [];
17452         });
17453         
17454         Roo.each(this.calevents, function(e) {
17455             Roo.each(e.els, function(el) {
17456                 el.un('mouseenter' ,this.onEventEnter, this);
17457                 el.un('mouseleave' ,this.onEventLeave, this);
17458                 el.remove();
17459             },this);
17460         },this);
17461         
17462         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17463             e.remove();
17464         });
17465         
17466     },
17467     
17468     renderEvents: function()
17469     {   
17470         var _this = this;
17471         
17472         this.cells.each(function(c) {
17473             
17474             if(c.row < 5){
17475                 return;
17476             }
17477             
17478             var ev = c.events;
17479             
17480             var r = 4;
17481             if(c.row != c.events.length){
17482                 r = 4 - (4 - (c.row - c.events.length));
17483             }
17484             
17485             c.events = ev.slice(0, r);
17486             c.more = ev.slice(r);
17487             
17488             if(c.more.length && c.more.length == 1){
17489                 c.events.push(c.more.pop());
17490             }
17491             
17492             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17493             
17494         });
17495             
17496         this.cells.each(function(c) {
17497             
17498             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17499             
17500             
17501             for (var e = 0; e < c.events.length; e++){
17502                 var ev = c.events[e];
17503                 var rows = ev.rows;
17504                 
17505                 for(var i = 0; i < rows.length; i++) {
17506                 
17507                     // how many rows should it span..
17508
17509                     var  cfg = {
17510                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17511                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17512
17513                         unselectable : "on",
17514                         cn : [
17515                             {
17516                                 cls: 'fc-event-inner',
17517                                 cn : [
17518     //                                {
17519     //                                  tag:'span',
17520     //                                  cls: 'fc-event-time',
17521     //                                  html : cells.length > 1 ? '' : ev.time
17522     //                                },
17523                                     {
17524                                       tag:'span',
17525                                       cls: 'fc-event-title',
17526                                       html : String.format('{0}', ev.title)
17527                                     }
17528
17529
17530                                 ]
17531                             },
17532                             {
17533                                 cls: 'ui-resizable-handle ui-resizable-e',
17534                                 html : '&nbsp;&nbsp;&nbsp'
17535                             }
17536
17537                         ]
17538                     };
17539
17540                     if (i == 0) {
17541                         cfg.cls += ' fc-event-start';
17542                     }
17543                     if ((i+1) == rows.length) {
17544                         cfg.cls += ' fc-event-end';
17545                     }
17546
17547                     var ctr = _this.el.select('.fc-event-container',true).first();
17548                     var cg = ctr.createChild(cfg);
17549
17550                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17551                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17552
17553                     var r = (c.more.length) ? 1 : 0;
17554                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17555                     cg.setWidth(ebox.right - sbox.x -2);
17556
17557                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17558                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17559                     cg.on('click', _this.onEventClick, _this, ev);
17560
17561                     ev.els.push(cg);
17562                     
17563                 }
17564                 
17565             }
17566             
17567             
17568             if(c.more.length){
17569                 var  cfg = {
17570                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17571                     style : 'position: absolute',
17572                     unselectable : "on",
17573                     cn : [
17574                         {
17575                             cls: 'fc-event-inner',
17576                             cn : [
17577                                 {
17578                                   tag:'span',
17579                                   cls: 'fc-event-title',
17580                                   html : 'More'
17581                                 }
17582
17583
17584                             ]
17585                         },
17586                         {
17587                             cls: 'ui-resizable-handle ui-resizable-e',
17588                             html : '&nbsp;&nbsp;&nbsp'
17589                         }
17590
17591                     ]
17592                 };
17593
17594                 var ctr = _this.el.select('.fc-event-container',true).first();
17595                 var cg = ctr.createChild(cfg);
17596
17597                 var sbox = c.select('.fc-day-content',true).first().getBox();
17598                 var ebox = c.select('.fc-day-content',true).first().getBox();
17599                 //Roo.log(cg);
17600                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17601                 cg.setWidth(ebox.right - sbox.x -2);
17602
17603                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17604                 
17605             }
17606             
17607         });
17608         
17609         
17610         
17611     },
17612     
17613     onEventEnter: function (e, el,event,d) {
17614         this.fireEvent('evententer', this, el, event);
17615     },
17616     
17617     onEventLeave: function (e, el,event,d) {
17618         this.fireEvent('eventleave', this, el, event);
17619     },
17620     
17621     onEventClick: function (e, el,event,d) {
17622         this.fireEvent('eventclick', this, el, event);
17623     },
17624     
17625     onMonthChange: function () {
17626         this.store.load();
17627     },
17628     
17629     onMoreEventClick: function(e, el, more)
17630     {
17631         var _this = this;
17632         
17633         this.calpopover.placement = 'right';
17634         this.calpopover.setTitle('More');
17635         
17636         this.calpopover.setContent('');
17637         
17638         var ctr = this.calpopover.el.select('.popover-content', true).first();
17639         
17640         Roo.each(more, function(m){
17641             var cfg = {
17642                 cls : 'fc-event-hori fc-event-draggable',
17643                 html : m.title
17644             };
17645             var cg = ctr.createChild(cfg);
17646             
17647             cg.on('click', _this.onEventClick, _this, m);
17648         });
17649         
17650         this.calpopover.show(el);
17651         
17652         
17653     },
17654     
17655     onLoad: function () 
17656     {   
17657         this.calevents = [];
17658         var cal = this;
17659         
17660         if(this.store.getCount() > 0){
17661             this.store.data.each(function(d){
17662                cal.addItem({
17663                     id : d.data.id,
17664                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17665                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17666                     time : d.data.start_time,
17667                     title : d.data.title,
17668                     description : d.data.description,
17669                     venue : d.data.venue
17670                 });
17671             });
17672         }
17673         
17674         this.renderEvents();
17675         
17676         if(this.calevents.length && this.loadMask){
17677             this.maskEl.hide();
17678         }
17679     },
17680     
17681     onBeforeLoad: function()
17682     {
17683         this.clearEvents();
17684         if(this.loadMask){
17685             this.maskEl.show();
17686         }
17687     }
17688 });
17689
17690  
17691  /*
17692  * - LGPL
17693  *
17694  * element
17695  * 
17696  */
17697
17698 /**
17699  * @class Roo.bootstrap.Popover
17700  * @extends Roo.bootstrap.Component
17701  * Bootstrap Popover class
17702  * @cfg {String} html contents of the popover   (or false to use children..)
17703  * @cfg {String} title of popover (or false to hide)
17704  * @cfg {String} placement how it is placed
17705  * @cfg {String} trigger click || hover (or false to trigger manually)
17706  * @cfg {String} over what (parent or false to trigger manually.)
17707  * @cfg {Number} delay - delay before showing
17708  
17709  * @constructor
17710  * Create a new Popover
17711  * @param {Object} config The config object
17712  */
17713
17714 Roo.bootstrap.Popover = function(config){
17715     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17716     
17717     this.addEvents({
17718         // raw events
17719          /**
17720          * @event show
17721          * After the popover show
17722          * 
17723          * @param {Roo.bootstrap.Popover} this
17724          */
17725         "show" : true,
17726         /**
17727          * @event hide
17728          * After the popover hide
17729          * 
17730          * @param {Roo.bootstrap.Popover} this
17731          */
17732         "hide" : true
17733     });
17734 };
17735
17736 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17737     
17738     title: 'Fill in a title',
17739     html: false,
17740     
17741     placement : 'right',
17742     trigger : 'hover', // hover
17743     
17744     delay : 0,
17745     
17746     over: 'parent',
17747     
17748     can_build_overlaid : false,
17749     
17750     getChildContainer : function()
17751     {
17752         return this.el.select('.popover-content',true).first();
17753     },
17754     
17755     getAutoCreate : function(){
17756          
17757         var cfg = {
17758            cls : 'popover roo-dynamic',
17759            style: 'display:block',
17760            cn : [
17761                 {
17762                     cls : 'arrow'
17763                 },
17764                 {
17765                     cls : 'popover-inner',
17766                     cn : [
17767                         {
17768                             tag: 'h3',
17769                             cls: 'popover-title popover-header',
17770                             html : this.title
17771                         },
17772                         {
17773                             cls : 'popover-content popover-body',
17774                             html : this.html
17775                         }
17776                     ]
17777                     
17778                 }
17779            ]
17780         };
17781         
17782         return cfg;
17783     },
17784     setTitle: function(str)
17785     {
17786         this.title = str;
17787         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17788     },
17789     setContent: function(str)
17790     {
17791         this.html = str;
17792         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17793     },
17794     // as it get's added to the bottom of the page.
17795     onRender : function(ct, position)
17796     {
17797         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17798         if(!this.el){
17799             var cfg = Roo.apply({},  this.getAutoCreate());
17800             cfg.id = Roo.id();
17801             
17802             if (this.cls) {
17803                 cfg.cls += ' ' + this.cls;
17804             }
17805             if (this.style) {
17806                 cfg.style = this.style;
17807             }
17808             //Roo.log("adding to ");
17809             this.el = Roo.get(document.body).createChild(cfg, position);
17810 //            Roo.log(this.el);
17811         }
17812         this.initEvents();
17813     },
17814     
17815     initEvents : function()
17816     {
17817         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17818         this.el.enableDisplayMode('block');
17819         this.el.hide();
17820         if (this.over === false) {
17821             return; 
17822         }
17823         if (this.triggers === false) {
17824             return;
17825         }
17826         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17827         var triggers = this.trigger ? this.trigger.split(' ') : [];
17828         Roo.each(triggers, function(trigger) {
17829         
17830             if (trigger == 'click') {
17831                 on_el.on('click', this.toggle, this);
17832             } else if (trigger != 'manual') {
17833                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17834                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17835       
17836                 on_el.on(eventIn  ,this.enter, this);
17837                 on_el.on(eventOut, this.leave, this);
17838             }
17839         }, this);
17840         
17841     },
17842     
17843     
17844     // private
17845     timeout : null,
17846     hoverState : null,
17847     
17848     toggle : function () {
17849         this.hoverState == 'in' ? this.leave() : this.enter();
17850     },
17851     
17852     enter : function () {
17853         
17854         clearTimeout(this.timeout);
17855     
17856         this.hoverState = 'in';
17857     
17858         if (!this.delay || !this.delay.show) {
17859             this.show();
17860             return;
17861         }
17862         var _t = this;
17863         this.timeout = setTimeout(function () {
17864             if (_t.hoverState == 'in') {
17865                 _t.show();
17866             }
17867         }, this.delay.show)
17868     },
17869     
17870     leave : function() {
17871         clearTimeout(this.timeout);
17872     
17873         this.hoverState = 'out';
17874     
17875         if (!this.delay || !this.delay.hide) {
17876             this.hide();
17877             return;
17878         }
17879         var _t = this;
17880         this.timeout = setTimeout(function () {
17881             if (_t.hoverState == 'out') {
17882                 _t.hide();
17883             }
17884         }, this.delay.hide)
17885     },
17886     
17887     show : function (on_el)
17888     {
17889         if (!on_el) {
17890             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17891         }
17892         
17893         // set content.
17894         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17895         if (this.html !== false) {
17896             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17897         }
17898         this.el.removeClass([
17899             'fade','top','bottom', 'left', 'right','in',
17900             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17901         ]);
17902         if (!this.title.length) {
17903             this.el.select('.popover-title',true).hide();
17904         }
17905         
17906         var placement = typeof this.placement == 'function' ?
17907             this.placement.call(this, this.el, on_el) :
17908             this.placement;
17909             
17910         var autoToken = /\s?auto?\s?/i;
17911         var autoPlace = autoToken.test(placement);
17912         if (autoPlace) {
17913             placement = placement.replace(autoToken, '') || 'top';
17914         }
17915         
17916         //this.el.detach()
17917         //this.el.setXY([0,0]);
17918         this.el.show();
17919         this.el.dom.style.display='block';
17920         this.el.addClass(placement);
17921         
17922         //this.el.appendTo(on_el);
17923         
17924         var p = this.getPosition();
17925         var box = this.el.getBox();
17926         
17927         if (autoPlace) {
17928             // fixme..
17929         }
17930         var align = Roo.bootstrap.Popover.alignment[placement];
17931         
17932 //        Roo.log(align);
17933         this.el.alignTo(on_el, align[0],align[1]);
17934         //var arrow = this.el.select('.arrow',true).first();
17935         //arrow.set(align[2], 
17936         
17937         this.el.addClass('in');
17938         
17939         
17940         if (this.el.hasClass('fade')) {
17941             // fade it?
17942         }
17943         
17944         this.hoverState = 'in';
17945         
17946         this.fireEvent('show', this);
17947         
17948     },
17949     hide : function()
17950     {
17951         this.el.setXY([0,0]);
17952         this.el.removeClass('in');
17953         this.el.hide();
17954         this.hoverState = null;
17955         
17956         this.fireEvent('hide', this);
17957     }
17958     
17959 });
17960
17961 Roo.bootstrap.Popover.alignment = {
17962     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17963     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17964     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17965     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17966 };
17967
17968  /*
17969  * - LGPL
17970  *
17971  * Progress
17972  * 
17973  */
17974
17975 /**
17976  * @class Roo.bootstrap.Progress
17977  * @extends Roo.bootstrap.Component
17978  * Bootstrap Progress class
17979  * @cfg {Boolean} striped striped of the progress bar
17980  * @cfg {Boolean} active animated of the progress bar
17981  * 
17982  * 
17983  * @constructor
17984  * Create a new Progress
17985  * @param {Object} config The config object
17986  */
17987
17988 Roo.bootstrap.Progress = function(config){
17989     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17990 };
17991
17992 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17993     
17994     striped : false,
17995     active: false,
17996     
17997     getAutoCreate : function(){
17998         var cfg = {
17999             tag: 'div',
18000             cls: 'progress'
18001         };
18002         
18003         
18004         if(this.striped){
18005             cfg.cls += ' progress-striped';
18006         }
18007       
18008         if(this.active){
18009             cfg.cls += ' active';
18010         }
18011         
18012         
18013         return cfg;
18014     }
18015    
18016 });
18017
18018  
18019
18020  /*
18021  * - LGPL
18022  *
18023  * ProgressBar
18024  * 
18025  */
18026
18027 /**
18028  * @class Roo.bootstrap.ProgressBar
18029  * @extends Roo.bootstrap.Component
18030  * Bootstrap ProgressBar class
18031  * @cfg {Number} aria_valuenow aria-value now
18032  * @cfg {Number} aria_valuemin aria-value min
18033  * @cfg {Number} aria_valuemax aria-value max
18034  * @cfg {String} label label for the progress bar
18035  * @cfg {String} panel (success | info | warning | danger )
18036  * @cfg {String} role role of the progress bar
18037  * @cfg {String} sr_only text
18038  * 
18039  * 
18040  * @constructor
18041  * Create a new ProgressBar
18042  * @param {Object} config The config object
18043  */
18044
18045 Roo.bootstrap.ProgressBar = function(config){
18046     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18047 };
18048
18049 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18050     
18051     aria_valuenow : 0,
18052     aria_valuemin : 0,
18053     aria_valuemax : 100,
18054     label : false,
18055     panel : false,
18056     role : false,
18057     sr_only: false,
18058     
18059     getAutoCreate : function()
18060     {
18061         
18062         var cfg = {
18063             tag: 'div',
18064             cls: 'progress-bar',
18065             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18066         };
18067         
18068         if(this.sr_only){
18069             cfg.cn = {
18070                 tag: 'span',
18071                 cls: 'sr-only',
18072                 html: this.sr_only
18073             }
18074         }
18075         
18076         if(this.role){
18077             cfg.role = this.role;
18078         }
18079         
18080         if(this.aria_valuenow){
18081             cfg['aria-valuenow'] = this.aria_valuenow;
18082         }
18083         
18084         if(this.aria_valuemin){
18085             cfg['aria-valuemin'] = this.aria_valuemin;
18086         }
18087         
18088         if(this.aria_valuemax){
18089             cfg['aria-valuemax'] = this.aria_valuemax;
18090         }
18091         
18092         if(this.label && !this.sr_only){
18093             cfg.html = this.label;
18094         }
18095         
18096         if(this.panel){
18097             cfg.cls += ' progress-bar-' + this.panel;
18098         }
18099         
18100         return cfg;
18101     },
18102     
18103     update : function(aria_valuenow)
18104     {
18105         this.aria_valuenow = aria_valuenow;
18106         
18107         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18108     }
18109    
18110 });
18111
18112  
18113
18114  /*
18115  * - LGPL
18116  *
18117  * column
18118  * 
18119  */
18120
18121 /**
18122  * @class Roo.bootstrap.TabGroup
18123  * @extends Roo.bootstrap.Column
18124  * Bootstrap Column class
18125  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18126  * @cfg {Boolean} carousel true to make the group behave like a carousel
18127  * @cfg {Boolean} bullets show bullets for the panels
18128  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18129  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18130  * @cfg {Boolean} showarrow (true|false) show arrow default true
18131  * 
18132  * @constructor
18133  * Create a new TabGroup
18134  * @param {Object} config The config object
18135  */
18136
18137 Roo.bootstrap.TabGroup = function(config){
18138     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18139     if (!this.navId) {
18140         this.navId = Roo.id();
18141     }
18142     this.tabs = [];
18143     Roo.bootstrap.TabGroup.register(this);
18144     
18145 };
18146
18147 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18148     
18149     carousel : false,
18150     transition : false,
18151     bullets : 0,
18152     timer : 0,
18153     autoslide : false,
18154     slideFn : false,
18155     slideOnTouch : false,
18156     showarrow : true,
18157     
18158     getAutoCreate : function()
18159     {
18160         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18161         
18162         cfg.cls += ' tab-content';
18163         
18164         if (this.carousel) {
18165             cfg.cls += ' carousel slide';
18166             
18167             cfg.cn = [{
18168                cls : 'carousel-inner',
18169                cn : []
18170             }];
18171         
18172             if(this.bullets  && !Roo.isTouch){
18173                 
18174                 var bullets = {
18175                     cls : 'carousel-bullets',
18176                     cn : []
18177                 };
18178                
18179                 if(this.bullets_cls){
18180                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18181                 }
18182                 
18183                 bullets.cn.push({
18184                     cls : 'clear'
18185                 });
18186                 
18187                 cfg.cn[0].cn.push(bullets);
18188             }
18189             
18190             if(this.showarrow){
18191                 cfg.cn[0].cn.push({
18192                     tag : 'div',
18193                     class : 'carousel-arrow',
18194                     cn : [
18195                         {
18196                             tag : 'div',
18197                             class : 'carousel-prev',
18198                             cn : [
18199                                 {
18200                                     tag : 'i',
18201                                     class : 'fa fa-chevron-left'
18202                                 }
18203                             ]
18204                         },
18205                         {
18206                             tag : 'div',
18207                             class : 'carousel-next',
18208                             cn : [
18209                                 {
18210                                     tag : 'i',
18211                                     class : 'fa fa-chevron-right'
18212                                 }
18213                             ]
18214                         }
18215                     ]
18216                 });
18217             }
18218             
18219         }
18220         
18221         return cfg;
18222     },
18223     
18224     initEvents:  function()
18225     {
18226 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18227 //            this.el.on("touchstart", this.onTouchStart, this);
18228 //        }
18229         
18230         if(this.autoslide){
18231             var _this = this;
18232             
18233             this.slideFn = window.setInterval(function() {
18234                 _this.showPanelNext();
18235             }, this.timer);
18236         }
18237         
18238         if(this.showarrow){
18239             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18240             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18241         }
18242         
18243         
18244     },
18245     
18246 //    onTouchStart : function(e, el, o)
18247 //    {
18248 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18249 //            return;
18250 //        }
18251 //        
18252 //        this.showPanelNext();
18253 //    },
18254     
18255     
18256     getChildContainer : function()
18257     {
18258         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18259     },
18260     
18261     /**
18262     * register a Navigation item
18263     * @param {Roo.bootstrap.NavItem} the navitem to add
18264     */
18265     register : function(item)
18266     {
18267         this.tabs.push( item);
18268         item.navId = this.navId; // not really needed..
18269         this.addBullet();
18270     
18271     },
18272     
18273     getActivePanel : function()
18274     {
18275         var r = false;
18276         Roo.each(this.tabs, function(t) {
18277             if (t.active) {
18278                 r = t;
18279                 return false;
18280             }
18281             return null;
18282         });
18283         return r;
18284         
18285     },
18286     getPanelByName : function(n)
18287     {
18288         var r = false;
18289         Roo.each(this.tabs, function(t) {
18290             if (t.tabId == n) {
18291                 r = t;
18292                 return false;
18293             }
18294             return null;
18295         });
18296         return r;
18297     },
18298     indexOfPanel : function(p)
18299     {
18300         var r = false;
18301         Roo.each(this.tabs, function(t,i) {
18302             if (t.tabId == p.tabId) {
18303                 r = i;
18304                 return false;
18305             }
18306             return null;
18307         });
18308         return r;
18309     },
18310     /**
18311      * show a specific panel
18312      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18313      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18314      */
18315     showPanel : function (pan)
18316     {
18317         if(this.transition || typeof(pan) == 'undefined'){
18318             Roo.log("waiting for the transitionend");
18319             return;
18320         }
18321         
18322         if (typeof(pan) == 'number') {
18323             pan = this.tabs[pan];
18324         }
18325         
18326         if (typeof(pan) == 'string') {
18327             pan = this.getPanelByName(pan);
18328         }
18329         
18330         var cur = this.getActivePanel();
18331         
18332         if(!pan || !cur){
18333             Roo.log('pan or acitve pan is undefined');
18334             return false;
18335         }
18336         
18337         if (pan.tabId == this.getActivePanel().tabId) {
18338             return true;
18339         }
18340         
18341         if (false === cur.fireEvent('beforedeactivate')) {
18342             return false;
18343         }
18344         
18345         if(this.bullets > 0 && !Roo.isTouch){
18346             this.setActiveBullet(this.indexOfPanel(pan));
18347         }
18348         
18349         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18350             
18351             this.transition = true;
18352             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18353             var lr = dir == 'next' ? 'left' : 'right';
18354             pan.el.addClass(dir); // or prev
18355             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18356             cur.el.addClass(lr); // or right
18357             pan.el.addClass(lr);
18358             
18359             var _this = this;
18360             cur.el.on('transitionend', function() {
18361                 Roo.log("trans end?");
18362                 
18363                 pan.el.removeClass([lr,dir]);
18364                 pan.setActive(true);
18365                 
18366                 cur.el.removeClass([lr]);
18367                 cur.setActive(false);
18368                 
18369                 _this.transition = false;
18370                 
18371             }, this, { single:  true } );
18372             
18373             return true;
18374         }
18375         
18376         cur.setActive(false);
18377         pan.setActive(true);
18378         
18379         return true;
18380         
18381     },
18382     showPanelNext : function()
18383     {
18384         var i = this.indexOfPanel(this.getActivePanel());
18385         
18386         if (i >= this.tabs.length - 1 && !this.autoslide) {
18387             return;
18388         }
18389         
18390         if (i >= this.tabs.length - 1 && this.autoslide) {
18391             i = -1;
18392         }
18393         
18394         this.showPanel(this.tabs[i+1]);
18395     },
18396     
18397     showPanelPrev : function()
18398     {
18399         var i = this.indexOfPanel(this.getActivePanel());
18400         
18401         if (i  < 1 && !this.autoslide) {
18402             return;
18403         }
18404         
18405         if (i < 1 && this.autoslide) {
18406             i = this.tabs.length;
18407         }
18408         
18409         this.showPanel(this.tabs[i-1]);
18410     },
18411     
18412     
18413     addBullet: function()
18414     {
18415         if(!this.bullets || Roo.isTouch){
18416             return;
18417         }
18418         var ctr = this.el.select('.carousel-bullets',true).first();
18419         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18420         var bullet = ctr.createChild({
18421             cls : 'bullet bullet-' + i
18422         },ctr.dom.lastChild);
18423         
18424         
18425         var _this = this;
18426         
18427         bullet.on('click', (function(e, el, o, ii, t){
18428
18429             e.preventDefault();
18430
18431             this.showPanel(ii);
18432
18433             if(this.autoslide && this.slideFn){
18434                 clearInterval(this.slideFn);
18435                 this.slideFn = window.setInterval(function() {
18436                     _this.showPanelNext();
18437                 }, this.timer);
18438             }
18439
18440         }).createDelegate(this, [i, bullet], true));
18441                 
18442         
18443     },
18444      
18445     setActiveBullet : function(i)
18446     {
18447         if(Roo.isTouch){
18448             return;
18449         }
18450         
18451         Roo.each(this.el.select('.bullet', true).elements, function(el){
18452             el.removeClass('selected');
18453         });
18454
18455         var bullet = this.el.select('.bullet-' + i, true).first();
18456         
18457         if(!bullet){
18458             return;
18459         }
18460         
18461         bullet.addClass('selected');
18462     }
18463     
18464     
18465   
18466 });
18467
18468  
18469
18470  
18471  
18472 Roo.apply(Roo.bootstrap.TabGroup, {
18473     
18474     groups: {},
18475      /**
18476     * register a Navigation Group
18477     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18478     */
18479     register : function(navgrp)
18480     {
18481         this.groups[navgrp.navId] = navgrp;
18482         
18483     },
18484     /**
18485     * fetch a Navigation Group based on the navigation ID
18486     * if one does not exist , it will get created.
18487     * @param {string} the navgroup to add
18488     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18489     */
18490     get: function(navId) {
18491         if (typeof(this.groups[navId]) == 'undefined') {
18492             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18493         }
18494         return this.groups[navId] ;
18495     }
18496     
18497     
18498     
18499 });
18500
18501  /*
18502  * - LGPL
18503  *
18504  * TabPanel
18505  * 
18506  */
18507
18508 /**
18509  * @class Roo.bootstrap.TabPanel
18510  * @extends Roo.bootstrap.Component
18511  * Bootstrap TabPanel class
18512  * @cfg {Boolean} active panel active
18513  * @cfg {String} html panel content
18514  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18515  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18516  * @cfg {String} href click to link..
18517  * 
18518  * 
18519  * @constructor
18520  * Create a new TabPanel
18521  * @param {Object} config The config object
18522  */
18523
18524 Roo.bootstrap.TabPanel = function(config){
18525     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18526     this.addEvents({
18527         /**
18528              * @event changed
18529              * Fires when the active status changes
18530              * @param {Roo.bootstrap.TabPanel} this
18531              * @param {Boolean} state the new state
18532             
18533          */
18534         'changed': true,
18535         /**
18536              * @event beforedeactivate
18537              * Fires before a tab is de-activated - can be used to do validation on a form.
18538              * @param {Roo.bootstrap.TabPanel} this
18539              * @return {Boolean} false if there is an error
18540             
18541          */
18542         'beforedeactivate': true
18543      });
18544     
18545     this.tabId = this.tabId || Roo.id();
18546   
18547 };
18548
18549 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18550     
18551     active: false,
18552     html: false,
18553     tabId: false,
18554     navId : false,
18555     href : '',
18556     
18557     getAutoCreate : function(){
18558         var cfg = {
18559             tag: 'div',
18560             // item is needed for carousel - not sure if it has any effect otherwise
18561             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18562             html: this.html || ''
18563         };
18564         
18565         if(this.active){
18566             cfg.cls += ' active';
18567         }
18568         
18569         if(this.tabId){
18570             cfg.tabId = this.tabId;
18571         }
18572         
18573         
18574         return cfg;
18575     },
18576     
18577     initEvents:  function()
18578     {
18579         var p = this.parent();
18580         
18581         this.navId = this.navId || p.navId;
18582         
18583         if (typeof(this.navId) != 'undefined') {
18584             // not really needed.. but just in case.. parent should be a NavGroup.
18585             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18586             
18587             tg.register(this);
18588             
18589             var i = tg.tabs.length - 1;
18590             
18591             if(this.active && tg.bullets > 0 && i < tg.bullets){
18592                 tg.setActiveBullet(i);
18593             }
18594         }
18595         
18596         this.el.on('click', this.onClick, this);
18597         
18598         if(Roo.isTouch){
18599             this.el.on("touchstart", this.onTouchStart, this);
18600             this.el.on("touchmove", this.onTouchMove, this);
18601             this.el.on("touchend", this.onTouchEnd, this);
18602         }
18603         
18604     },
18605     
18606     onRender : function(ct, position)
18607     {
18608         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18609     },
18610     
18611     setActive : function(state)
18612     {
18613         Roo.log("panel - set active " + this.tabId + "=" + state);
18614         
18615         this.active = state;
18616         if (!state) {
18617             this.el.removeClass('active');
18618             
18619         } else  if (!this.el.hasClass('active')) {
18620             this.el.addClass('active');
18621         }
18622         
18623         this.fireEvent('changed', this, state);
18624     },
18625     
18626     onClick : function(e)
18627     {
18628         e.preventDefault();
18629         
18630         if(!this.href.length){
18631             return;
18632         }
18633         
18634         window.location.href = this.href;
18635     },
18636     
18637     startX : 0,
18638     startY : 0,
18639     endX : 0,
18640     endY : 0,
18641     swiping : false,
18642     
18643     onTouchStart : function(e)
18644     {
18645         this.swiping = false;
18646         
18647         this.startX = e.browserEvent.touches[0].clientX;
18648         this.startY = e.browserEvent.touches[0].clientY;
18649     },
18650     
18651     onTouchMove : function(e)
18652     {
18653         this.swiping = true;
18654         
18655         this.endX = e.browserEvent.touches[0].clientX;
18656         this.endY = e.browserEvent.touches[0].clientY;
18657     },
18658     
18659     onTouchEnd : function(e)
18660     {
18661         if(!this.swiping){
18662             this.onClick(e);
18663             return;
18664         }
18665         
18666         var tabGroup = this.parent();
18667         
18668         if(this.endX > this.startX){ // swiping right
18669             tabGroup.showPanelPrev();
18670             return;
18671         }
18672         
18673         if(this.startX > this.endX){ // swiping left
18674             tabGroup.showPanelNext();
18675             return;
18676         }
18677     }
18678     
18679     
18680 });
18681  
18682
18683  
18684
18685  /*
18686  * - LGPL
18687  *
18688  * DateField
18689  * 
18690  */
18691
18692 /**
18693  * @class Roo.bootstrap.DateField
18694  * @extends Roo.bootstrap.Input
18695  * Bootstrap DateField class
18696  * @cfg {Number} weekStart default 0
18697  * @cfg {String} viewMode default empty, (months|years)
18698  * @cfg {String} minViewMode default empty, (months|years)
18699  * @cfg {Number} startDate default -Infinity
18700  * @cfg {Number} endDate default Infinity
18701  * @cfg {Boolean} todayHighlight default false
18702  * @cfg {Boolean} todayBtn default false
18703  * @cfg {Boolean} calendarWeeks default false
18704  * @cfg {Object} daysOfWeekDisabled default empty
18705  * @cfg {Boolean} singleMode default false (true | false)
18706  * 
18707  * @cfg {Boolean} keyboardNavigation default true
18708  * @cfg {String} language default en
18709  * 
18710  * @constructor
18711  * Create a new DateField
18712  * @param {Object} config The config object
18713  */
18714
18715 Roo.bootstrap.DateField = function(config){
18716     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18717      this.addEvents({
18718             /**
18719              * @event show
18720              * Fires when this field show.
18721              * @param {Roo.bootstrap.DateField} this
18722              * @param {Mixed} date The date value
18723              */
18724             show : true,
18725             /**
18726              * @event show
18727              * Fires when this field hide.
18728              * @param {Roo.bootstrap.DateField} this
18729              * @param {Mixed} date The date value
18730              */
18731             hide : true,
18732             /**
18733              * @event select
18734              * Fires when select a date.
18735              * @param {Roo.bootstrap.DateField} this
18736              * @param {Mixed} date The date value
18737              */
18738             select : true,
18739             /**
18740              * @event beforeselect
18741              * Fires when before select a date.
18742              * @param {Roo.bootstrap.DateField} this
18743              * @param {Mixed} date The date value
18744              */
18745             beforeselect : true
18746         });
18747 };
18748
18749 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18750     
18751     /**
18752      * @cfg {String} format
18753      * The default date format string which can be overriden for localization support.  The format must be
18754      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18755      */
18756     format : "m/d/y",
18757     /**
18758      * @cfg {String} altFormats
18759      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18760      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18761      */
18762     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18763     
18764     weekStart : 0,
18765     
18766     viewMode : '',
18767     
18768     minViewMode : '',
18769     
18770     todayHighlight : false,
18771     
18772     todayBtn: false,
18773     
18774     language: 'en',
18775     
18776     keyboardNavigation: true,
18777     
18778     calendarWeeks: false,
18779     
18780     startDate: -Infinity,
18781     
18782     endDate: Infinity,
18783     
18784     daysOfWeekDisabled: [],
18785     
18786     _events: [],
18787     
18788     singleMode : false,
18789     
18790     UTCDate: function()
18791     {
18792         return new Date(Date.UTC.apply(Date, arguments));
18793     },
18794     
18795     UTCToday: function()
18796     {
18797         var today = new Date();
18798         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18799     },
18800     
18801     getDate: function() {
18802             var d = this.getUTCDate();
18803             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18804     },
18805     
18806     getUTCDate: function() {
18807             return this.date;
18808     },
18809     
18810     setDate: function(d) {
18811             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18812     },
18813     
18814     setUTCDate: function(d) {
18815             this.date = d;
18816             this.setValue(this.formatDate(this.date));
18817     },
18818         
18819     onRender: function(ct, position)
18820     {
18821         
18822         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18823         
18824         this.language = this.language || 'en';
18825         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18826         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18827         
18828         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18829         this.format = this.format || 'm/d/y';
18830         this.isInline = false;
18831         this.isInput = true;
18832         this.component = this.el.select('.add-on', true).first() || false;
18833         this.component = (this.component && this.component.length === 0) ? false : this.component;
18834         this.hasInput = this.component && this.inputEl().length;
18835         
18836         if (typeof(this.minViewMode === 'string')) {
18837             switch (this.minViewMode) {
18838                 case 'months':
18839                     this.minViewMode = 1;
18840                     break;
18841                 case 'years':
18842                     this.minViewMode = 2;
18843                     break;
18844                 default:
18845                     this.minViewMode = 0;
18846                     break;
18847             }
18848         }
18849         
18850         if (typeof(this.viewMode === 'string')) {
18851             switch (this.viewMode) {
18852                 case 'months':
18853                     this.viewMode = 1;
18854                     break;
18855                 case 'years':
18856                     this.viewMode = 2;
18857                     break;
18858                 default:
18859                     this.viewMode = 0;
18860                     break;
18861             }
18862         }
18863                 
18864         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18865         
18866 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18867         
18868         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18869         
18870         this.picker().on('mousedown', this.onMousedown, this);
18871         this.picker().on('click', this.onClick, this);
18872         
18873         this.picker().addClass('datepicker-dropdown');
18874         
18875         this.startViewMode = this.viewMode;
18876         
18877         if(this.singleMode){
18878             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18879                 v.setVisibilityMode(Roo.Element.DISPLAY);
18880                 v.hide();
18881             });
18882             
18883             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18884                 v.setStyle('width', '189px');
18885             });
18886         }
18887         
18888         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18889             if(!this.calendarWeeks){
18890                 v.remove();
18891                 return;
18892             }
18893             
18894             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18895             v.attr('colspan', function(i, val){
18896                 return parseInt(val) + 1;
18897             });
18898         });
18899                         
18900         
18901         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18902         
18903         this.setStartDate(this.startDate);
18904         this.setEndDate(this.endDate);
18905         
18906         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18907         
18908         this.fillDow();
18909         this.fillMonths();
18910         this.update();
18911         this.showMode();
18912         
18913         if(this.isInline) {
18914             this.showPopup();
18915         }
18916     },
18917     
18918     picker : function()
18919     {
18920         return this.pickerEl;
18921 //        return this.el.select('.datepicker', true).first();
18922     },
18923     
18924     fillDow: function()
18925     {
18926         var dowCnt = this.weekStart;
18927         
18928         var dow = {
18929             tag: 'tr',
18930             cn: [
18931                 
18932             ]
18933         };
18934         
18935         if(this.calendarWeeks){
18936             dow.cn.push({
18937                 tag: 'th',
18938                 cls: 'cw',
18939                 html: '&nbsp;'
18940             })
18941         }
18942         
18943         while (dowCnt < this.weekStart + 7) {
18944             dow.cn.push({
18945                 tag: 'th',
18946                 cls: 'dow',
18947                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18948             });
18949         }
18950         
18951         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18952     },
18953     
18954     fillMonths: function()
18955     {    
18956         var i = 0;
18957         var months = this.picker().select('>.datepicker-months td', true).first();
18958         
18959         months.dom.innerHTML = '';
18960         
18961         while (i < 12) {
18962             var month = {
18963                 tag: 'span',
18964                 cls: 'month',
18965                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18966             };
18967             
18968             months.createChild(month);
18969         }
18970         
18971     },
18972     
18973     update: function()
18974     {
18975         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;
18976         
18977         if (this.date < this.startDate) {
18978             this.viewDate = new Date(this.startDate);
18979         } else if (this.date > this.endDate) {
18980             this.viewDate = new Date(this.endDate);
18981         } else {
18982             this.viewDate = new Date(this.date);
18983         }
18984         
18985         this.fill();
18986     },
18987     
18988     fill: function() 
18989     {
18990         var d = new Date(this.viewDate),
18991                 year = d.getUTCFullYear(),
18992                 month = d.getUTCMonth(),
18993                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18994                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18995                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18996                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18997                 currentDate = this.date && this.date.valueOf(),
18998                 today = this.UTCToday();
18999         
19000         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19001         
19002 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19003         
19004 //        this.picker.select('>tfoot th.today').
19005 //                                              .text(dates[this.language].today)
19006 //                                              .toggle(this.todayBtn !== false);
19007     
19008         this.updateNavArrows();
19009         this.fillMonths();
19010                                                 
19011         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19012         
19013         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19014          
19015         prevMonth.setUTCDate(day);
19016         
19017         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19018         
19019         var nextMonth = new Date(prevMonth);
19020         
19021         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19022         
19023         nextMonth = nextMonth.valueOf();
19024         
19025         var fillMonths = false;
19026         
19027         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19028         
19029         while(prevMonth.valueOf() <= nextMonth) {
19030             var clsName = '';
19031             
19032             if (prevMonth.getUTCDay() === this.weekStart) {
19033                 if(fillMonths){
19034                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19035                 }
19036                     
19037                 fillMonths = {
19038                     tag: 'tr',
19039                     cn: []
19040                 };
19041                 
19042                 if(this.calendarWeeks){
19043                     // ISO 8601: First week contains first thursday.
19044                     // ISO also states week starts on Monday, but we can be more abstract here.
19045                     var
19046                     // Start of current week: based on weekstart/current date
19047                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19048                     // Thursday of this week
19049                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19050                     // First Thursday of year, year from thursday
19051                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19052                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19053                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19054                     
19055                     fillMonths.cn.push({
19056                         tag: 'td',
19057                         cls: 'cw',
19058                         html: calWeek
19059                     });
19060                 }
19061             }
19062             
19063             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19064                 clsName += ' old';
19065             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19066                 clsName += ' new';
19067             }
19068             if (this.todayHighlight &&
19069                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19070                 prevMonth.getUTCMonth() == today.getMonth() &&
19071                 prevMonth.getUTCDate() == today.getDate()) {
19072                 clsName += ' today';
19073             }
19074             
19075             if (currentDate && prevMonth.valueOf() === currentDate) {
19076                 clsName += ' active';
19077             }
19078             
19079             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19080                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19081                     clsName += ' disabled';
19082             }
19083             
19084             fillMonths.cn.push({
19085                 tag: 'td',
19086                 cls: 'day ' + clsName,
19087                 html: prevMonth.getDate()
19088             });
19089             
19090             prevMonth.setDate(prevMonth.getDate()+1);
19091         }
19092           
19093         var currentYear = this.date && this.date.getUTCFullYear();
19094         var currentMonth = this.date && this.date.getUTCMonth();
19095         
19096         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19097         
19098         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19099             v.removeClass('active');
19100             
19101             if(currentYear === year && k === currentMonth){
19102                 v.addClass('active');
19103             }
19104             
19105             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19106                 v.addClass('disabled');
19107             }
19108             
19109         });
19110         
19111         
19112         year = parseInt(year/10, 10) * 10;
19113         
19114         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19115         
19116         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19117         
19118         year -= 1;
19119         for (var i = -1; i < 11; i++) {
19120             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19121                 tag: 'span',
19122                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19123                 html: year
19124             });
19125             
19126             year += 1;
19127         }
19128     },
19129     
19130     showMode: function(dir) 
19131     {
19132         if (dir) {
19133             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19134         }
19135         
19136         Roo.each(this.picker().select('>div',true).elements, function(v){
19137             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19138             v.hide();
19139         });
19140         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19141     },
19142     
19143     place: function()
19144     {
19145         if(this.isInline) {
19146             return;
19147         }
19148         
19149         this.picker().removeClass(['bottom', 'top']);
19150         
19151         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19152             /*
19153              * place to the top of element!
19154              *
19155              */
19156             
19157             this.picker().addClass('top');
19158             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19159             
19160             return;
19161         }
19162         
19163         this.picker().addClass('bottom');
19164         
19165         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19166     },
19167     
19168     parseDate : function(value)
19169     {
19170         if(!value || value instanceof Date){
19171             return value;
19172         }
19173         var v = Date.parseDate(value, this.format);
19174         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19175             v = Date.parseDate(value, 'Y-m-d');
19176         }
19177         if(!v && this.altFormats){
19178             if(!this.altFormatsArray){
19179                 this.altFormatsArray = this.altFormats.split("|");
19180             }
19181             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19182                 v = Date.parseDate(value, this.altFormatsArray[i]);
19183             }
19184         }
19185         return v;
19186     },
19187     
19188     formatDate : function(date, fmt)
19189     {   
19190         return (!date || !(date instanceof Date)) ?
19191         date : date.dateFormat(fmt || this.format);
19192     },
19193     
19194     onFocus : function()
19195     {
19196         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19197         this.showPopup();
19198     },
19199     
19200     onBlur : function()
19201     {
19202         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19203         
19204         var d = this.inputEl().getValue();
19205         
19206         this.setValue(d);
19207                 
19208         this.hidePopup();
19209     },
19210     
19211     showPopup : function()
19212     {
19213         this.picker().show();
19214         this.update();
19215         this.place();
19216         
19217         this.fireEvent('showpopup', this, this.date);
19218     },
19219     
19220     hidePopup : function()
19221     {
19222         if(this.isInline) {
19223             return;
19224         }
19225         this.picker().hide();
19226         this.viewMode = this.startViewMode;
19227         this.showMode();
19228         
19229         this.fireEvent('hidepopup', this, this.date);
19230         
19231     },
19232     
19233     onMousedown: function(e)
19234     {
19235         e.stopPropagation();
19236         e.preventDefault();
19237     },
19238     
19239     keyup: function(e)
19240     {
19241         Roo.bootstrap.DateField.superclass.keyup.call(this);
19242         this.update();
19243     },
19244
19245     setValue: function(v)
19246     {
19247         if(this.fireEvent('beforeselect', this, v) !== false){
19248             var d = new Date(this.parseDate(v) ).clearTime();
19249         
19250             if(isNaN(d.getTime())){
19251                 this.date = this.viewDate = '';
19252                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19253                 return;
19254             }
19255
19256             v = this.formatDate(d);
19257
19258             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19259
19260             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19261
19262             this.update();
19263
19264             this.fireEvent('select', this, this.date);
19265         }
19266     },
19267     
19268     getValue: function()
19269     {
19270         return this.formatDate(this.date);
19271     },
19272     
19273     fireKey: function(e)
19274     {
19275         if (!this.picker().isVisible()){
19276             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19277                 this.showPopup();
19278             }
19279             return;
19280         }
19281         
19282         var dateChanged = false,
19283         dir, day, month,
19284         newDate, newViewDate;
19285         
19286         switch(e.keyCode){
19287             case 27: // escape
19288                 this.hidePopup();
19289                 e.preventDefault();
19290                 break;
19291             case 37: // left
19292             case 39: // right
19293                 if (!this.keyboardNavigation) {
19294                     break;
19295                 }
19296                 dir = e.keyCode == 37 ? -1 : 1;
19297                 
19298                 if (e.ctrlKey){
19299                     newDate = this.moveYear(this.date, dir);
19300                     newViewDate = this.moveYear(this.viewDate, dir);
19301                 } else if (e.shiftKey){
19302                     newDate = this.moveMonth(this.date, dir);
19303                     newViewDate = this.moveMonth(this.viewDate, dir);
19304                 } else {
19305                     newDate = new Date(this.date);
19306                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19307                     newViewDate = new Date(this.viewDate);
19308                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19309                 }
19310                 if (this.dateWithinRange(newDate)){
19311                     this.date = newDate;
19312                     this.viewDate = newViewDate;
19313                     this.setValue(this.formatDate(this.date));
19314 //                    this.update();
19315                     e.preventDefault();
19316                     dateChanged = true;
19317                 }
19318                 break;
19319             case 38: // up
19320             case 40: // down
19321                 if (!this.keyboardNavigation) {
19322                     break;
19323                 }
19324                 dir = e.keyCode == 38 ? -1 : 1;
19325                 if (e.ctrlKey){
19326                     newDate = this.moveYear(this.date, dir);
19327                     newViewDate = this.moveYear(this.viewDate, dir);
19328                 } else if (e.shiftKey){
19329                     newDate = this.moveMonth(this.date, dir);
19330                     newViewDate = this.moveMonth(this.viewDate, dir);
19331                 } else {
19332                     newDate = new Date(this.date);
19333                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19334                     newViewDate = new Date(this.viewDate);
19335                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19336                 }
19337                 if (this.dateWithinRange(newDate)){
19338                     this.date = newDate;
19339                     this.viewDate = newViewDate;
19340                     this.setValue(this.formatDate(this.date));
19341 //                    this.update();
19342                     e.preventDefault();
19343                     dateChanged = true;
19344                 }
19345                 break;
19346             case 13: // enter
19347                 this.setValue(this.formatDate(this.date));
19348                 this.hidePopup();
19349                 e.preventDefault();
19350                 break;
19351             case 9: // tab
19352                 this.setValue(this.formatDate(this.date));
19353                 this.hidePopup();
19354                 break;
19355             case 16: // shift
19356             case 17: // ctrl
19357             case 18: // alt
19358                 break;
19359             default :
19360                 this.hidePopup();
19361                 
19362         }
19363     },
19364     
19365     
19366     onClick: function(e) 
19367     {
19368         e.stopPropagation();
19369         e.preventDefault();
19370         
19371         var target = e.getTarget();
19372         
19373         if(target.nodeName.toLowerCase() === 'i'){
19374             target = Roo.get(target).dom.parentNode;
19375         }
19376         
19377         var nodeName = target.nodeName;
19378         var className = target.className;
19379         var html = target.innerHTML;
19380         //Roo.log(nodeName);
19381         
19382         switch(nodeName.toLowerCase()) {
19383             case 'th':
19384                 switch(className) {
19385                     case 'switch':
19386                         this.showMode(1);
19387                         break;
19388                     case 'prev':
19389                     case 'next':
19390                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19391                         switch(this.viewMode){
19392                                 case 0:
19393                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19394                                         break;
19395                                 case 1:
19396                                 case 2:
19397                                         this.viewDate = this.moveYear(this.viewDate, dir);
19398                                         break;
19399                         }
19400                         this.fill();
19401                         break;
19402                     case 'today':
19403                         var date = new Date();
19404                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19405 //                        this.fill()
19406                         this.setValue(this.formatDate(this.date));
19407                         
19408                         this.hidePopup();
19409                         break;
19410                 }
19411                 break;
19412             case 'span':
19413                 if (className.indexOf('disabled') < 0) {
19414                     this.viewDate.setUTCDate(1);
19415                     if (className.indexOf('month') > -1) {
19416                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19417                     } else {
19418                         var year = parseInt(html, 10) || 0;
19419                         this.viewDate.setUTCFullYear(year);
19420                         
19421                     }
19422                     
19423                     if(this.singleMode){
19424                         this.setValue(this.formatDate(this.viewDate));
19425                         this.hidePopup();
19426                         return;
19427                     }
19428                     
19429                     this.showMode(-1);
19430                     this.fill();
19431                 }
19432                 break;
19433                 
19434             case 'td':
19435                 //Roo.log(className);
19436                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19437                     var day = parseInt(html, 10) || 1;
19438                     var year = this.viewDate.getUTCFullYear(),
19439                         month = this.viewDate.getUTCMonth();
19440
19441                     if (className.indexOf('old') > -1) {
19442                         if(month === 0 ){
19443                             month = 11;
19444                             year -= 1;
19445                         }else{
19446                             month -= 1;
19447                         }
19448                     } else if (className.indexOf('new') > -1) {
19449                         if (month == 11) {
19450                             month = 0;
19451                             year += 1;
19452                         } else {
19453                             month += 1;
19454                         }
19455                     }
19456                     //Roo.log([year,month,day]);
19457                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19458                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19459 //                    this.fill();
19460                     //Roo.log(this.formatDate(this.date));
19461                     this.setValue(this.formatDate(this.date));
19462                     this.hidePopup();
19463                 }
19464                 break;
19465         }
19466     },
19467     
19468     setStartDate: function(startDate)
19469     {
19470         this.startDate = startDate || -Infinity;
19471         if (this.startDate !== -Infinity) {
19472             this.startDate = this.parseDate(this.startDate);
19473         }
19474         this.update();
19475         this.updateNavArrows();
19476     },
19477
19478     setEndDate: function(endDate)
19479     {
19480         this.endDate = endDate || Infinity;
19481         if (this.endDate !== Infinity) {
19482             this.endDate = this.parseDate(this.endDate);
19483         }
19484         this.update();
19485         this.updateNavArrows();
19486     },
19487     
19488     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19489     {
19490         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19491         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19492             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19493         }
19494         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19495             return parseInt(d, 10);
19496         });
19497         this.update();
19498         this.updateNavArrows();
19499     },
19500     
19501     updateNavArrows: function() 
19502     {
19503         if(this.singleMode){
19504             return;
19505         }
19506         
19507         var d = new Date(this.viewDate),
19508         year = d.getUTCFullYear(),
19509         month = d.getUTCMonth();
19510         
19511         Roo.each(this.picker().select('.prev', true).elements, function(v){
19512             v.show();
19513             switch (this.viewMode) {
19514                 case 0:
19515
19516                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19517                         v.hide();
19518                     }
19519                     break;
19520                 case 1:
19521                 case 2:
19522                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19523                         v.hide();
19524                     }
19525                     break;
19526             }
19527         });
19528         
19529         Roo.each(this.picker().select('.next', true).elements, function(v){
19530             v.show();
19531             switch (this.viewMode) {
19532                 case 0:
19533
19534                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19535                         v.hide();
19536                     }
19537                     break;
19538                 case 1:
19539                 case 2:
19540                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19541                         v.hide();
19542                     }
19543                     break;
19544             }
19545         })
19546     },
19547     
19548     moveMonth: function(date, dir)
19549     {
19550         if (!dir) {
19551             return date;
19552         }
19553         var new_date = new Date(date.valueOf()),
19554         day = new_date.getUTCDate(),
19555         month = new_date.getUTCMonth(),
19556         mag = Math.abs(dir),
19557         new_month, test;
19558         dir = dir > 0 ? 1 : -1;
19559         if (mag == 1){
19560             test = dir == -1
19561             // If going back one month, make sure month is not current month
19562             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19563             ? function(){
19564                 return new_date.getUTCMonth() == month;
19565             }
19566             // If going forward one month, make sure month is as expected
19567             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19568             : function(){
19569                 return new_date.getUTCMonth() != new_month;
19570             };
19571             new_month = month + dir;
19572             new_date.setUTCMonth(new_month);
19573             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19574             if (new_month < 0 || new_month > 11) {
19575                 new_month = (new_month + 12) % 12;
19576             }
19577         } else {
19578             // For magnitudes >1, move one month at a time...
19579             for (var i=0; i<mag; i++) {
19580                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19581                 new_date = this.moveMonth(new_date, dir);
19582             }
19583             // ...then reset the day, keeping it in the new month
19584             new_month = new_date.getUTCMonth();
19585             new_date.setUTCDate(day);
19586             test = function(){
19587                 return new_month != new_date.getUTCMonth();
19588             };
19589         }
19590         // Common date-resetting loop -- if date is beyond end of month, make it
19591         // end of month
19592         while (test()){
19593             new_date.setUTCDate(--day);
19594             new_date.setUTCMonth(new_month);
19595         }
19596         return new_date;
19597     },
19598
19599     moveYear: function(date, dir)
19600     {
19601         return this.moveMonth(date, dir*12);
19602     },
19603
19604     dateWithinRange: function(date)
19605     {
19606         return date >= this.startDate && date <= this.endDate;
19607     },
19608
19609     
19610     remove: function() 
19611     {
19612         this.picker().remove();
19613     },
19614     
19615     validateValue : function(value)
19616     {
19617         if(this.getVisibilityEl().hasClass('hidden')){
19618             return true;
19619         }
19620         
19621         if(value.length < 1)  {
19622             if(this.allowBlank){
19623                 return true;
19624             }
19625             return false;
19626         }
19627         
19628         if(value.length < this.minLength){
19629             return false;
19630         }
19631         if(value.length > this.maxLength){
19632             return false;
19633         }
19634         if(this.vtype){
19635             var vt = Roo.form.VTypes;
19636             if(!vt[this.vtype](value, this)){
19637                 return false;
19638             }
19639         }
19640         if(typeof this.validator == "function"){
19641             var msg = this.validator(value);
19642             if(msg !== true){
19643                 return false;
19644             }
19645         }
19646         
19647         if(this.regex && !this.regex.test(value)){
19648             return false;
19649         }
19650         
19651         if(typeof(this.parseDate(value)) == 'undefined'){
19652             return false;
19653         }
19654         
19655         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19656             return false;
19657         }      
19658         
19659         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19660             return false;
19661         } 
19662         
19663         
19664         return true;
19665     },
19666     
19667     reset : function()
19668     {
19669         this.date = this.viewDate = '';
19670         
19671         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19672     }
19673    
19674 });
19675
19676 Roo.apply(Roo.bootstrap.DateField,  {
19677     
19678     head : {
19679         tag: 'thead',
19680         cn: [
19681         {
19682             tag: 'tr',
19683             cn: [
19684             {
19685                 tag: 'th',
19686                 cls: 'prev',
19687                 html: '<i class="fa fa-arrow-left"/>'
19688             },
19689             {
19690                 tag: 'th',
19691                 cls: 'switch',
19692                 colspan: '5'
19693             },
19694             {
19695                 tag: 'th',
19696                 cls: 'next',
19697                 html: '<i class="fa fa-arrow-right"/>'
19698             }
19699
19700             ]
19701         }
19702         ]
19703     },
19704     
19705     content : {
19706         tag: 'tbody',
19707         cn: [
19708         {
19709             tag: 'tr',
19710             cn: [
19711             {
19712                 tag: 'td',
19713                 colspan: '7'
19714             }
19715             ]
19716         }
19717         ]
19718     },
19719     
19720     footer : {
19721         tag: 'tfoot',
19722         cn: [
19723         {
19724             tag: 'tr',
19725             cn: [
19726             {
19727                 tag: 'th',
19728                 colspan: '7',
19729                 cls: 'today'
19730             }
19731                     
19732             ]
19733         }
19734         ]
19735     },
19736     
19737     dates:{
19738         en: {
19739             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19740             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19741             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19742             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19743             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19744             today: "Today"
19745         }
19746     },
19747     
19748     modes: [
19749     {
19750         clsName: 'days',
19751         navFnc: 'Month',
19752         navStep: 1
19753     },
19754     {
19755         clsName: 'months',
19756         navFnc: 'FullYear',
19757         navStep: 1
19758     },
19759     {
19760         clsName: 'years',
19761         navFnc: 'FullYear',
19762         navStep: 10
19763     }]
19764 });
19765
19766 Roo.apply(Roo.bootstrap.DateField,  {
19767   
19768     template : {
19769         tag: 'div',
19770         cls: 'datepicker dropdown-menu roo-dynamic',
19771         cn: [
19772         {
19773             tag: 'div',
19774             cls: 'datepicker-days',
19775             cn: [
19776             {
19777                 tag: 'table',
19778                 cls: 'table-condensed',
19779                 cn:[
19780                 Roo.bootstrap.DateField.head,
19781                 {
19782                     tag: 'tbody'
19783                 },
19784                 Roo.bootstrap.DateField.footer
19785                 ]
19786             }
19787             ]
19788         },
19789         {
19790             tag: 'div',
19791             cls: 'datepicker-months',
19792             cn: [
19793             {
19794                 tag: 'table',
19795                 cls: 'table-condensed',
19796                 cn:[
19797                 Roo.bootstrap.DateField.head,
19798                 Roo.bootstrap.DateField.content,
19799                 Roo.bootstrap.DateField.footer
19800                 ]
19801             }
19802             ]
19803         },
19804         {
19805             tag: 'div',
19806             cls: 'datepicker-years',
19807             cn: [
19808             {
19809                 tag: 'table',
19810                 cls: 'table-condensed',
19811                 cn:[
19812                 Roo.bootstrap.DateField.head,
19813                 Roo.bootstrap.DateField.content,
19814                 Roo.bootstrap.DateField.footer
19815                 ]
19816             }
19817             ]
19818         }
19819         ]
19820     }
19821 });
19822
19823  
19824
19825  /*
19826  * - LGPL
19827  *
19828  * TimeField
19829  * 
19830  */
19831
19832 /**
19833  * @class Roo.bootstrap.TimeField
19834  * @extends Roo.bootstrap.Input
19835  * Bootstrap DateField class
19836  * 
19837  * 
19838  * @constructor
19839  * Create a new TimeField
19840  * @param {Object} config The config object
19841  */
19842
19843 Roo.bootstrap.TimeField = function(config){
19844     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19845     this.addEvents({
19846             /**
19847              * @event show
19848              * Fires when this field show.
19849              * @param {Roo.bootstrap.DateField} thisthis
19850              * @param {Mixed} date The date value
19851              */
19852             show : true,
19853             /**
19854              * @event show
19855              * Fires when this field hide.
19856              * @param {Roo.bootstrap.DateField} this
19857              * @param {Mixed} date The date value
19858              */
19859             hide : true,
19860             /**
19861              * @event select
19862              * Fires when select a date.
19863              * @param {Roo.bootstrap.DateField} this
19864              * @param {Mixed} date The date value
19865              */
19866             select : true
19867         });
19868 };
19869
19870 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19871     
19872     /**
19873      * @cfg {String} format
19874      * The default time format string which can be overriden for localization support.  The format must be
19875      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19876      */
19877     format : "H:i",
19878        
19879     onRender: function(ct, position)
19880     {
19881         
19882         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19883                 
19884         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19885         
19886         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19887         
19888         this.pop = this.picker().select('>.datepicker-time',true).first();
19889         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19890         
19891         this.picker().on('mousedown', this.onMousedown, this);
19892         this.picker().on('click', this.onClick, this);
19893         
19894         this.picker().addClass('datepicker-dropdown');
19895     
19896         this.fillTime();
19897         this.update();
19898             
19899         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19900         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19901         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19902         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19903         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19904         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19905
19906     },
19907     
19908     fireKey: function(e){
19909         if (!this.picker().isVisible()){
19910             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19911                 this.show();
19912             }
19913             return;
19914         }
19915
19916         e.preventDefault();
19917         
19918         switch(e.keyCode){
19919             case 27: // escape
19920                 this.hide();
19921                 break;
19922             case 37: // left
19923             case 39: // right
19924                 this.onTogglePeriod();
19925                 break;
19926             case 38: // up
19927                 this.onIncrementMinutes();
19928                 break;
19929             case 40: // down
19930                 this.onDecrementMinutes();
19931                 break;
19932             case 13: // enter
19933             case 9: // tab
19934                 this.setTime();
19935                 break;
19936         }
19937     },
19938     
19939     onClick: function(e) {
19940         e.stopPropagation();
19941         e.preventDefault();
19942     },
19943     
19944     picker : function()
19945     {
19946         return this.el.select('.datepicker', true).first();
19947     },
19948     
19949     fillTime: function()
19950     {    
19951         var time = this.pop.select('tbody', true).first();
19952         
19953         time.dom.innerHTML = '';
19954         
19955         time.createChild({
19956             tag: 'tr',
19957             cn: [
19958                 {
19959                     tag: 'td',
19960                     cn: [
19961                         {
19962                             tag: 'a',
19963                             href: '#',
19964                             cls: 'btn',
19965                             cn: [
19966                                 {
19967                                     tag: 'span',
19968                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19969                                 }
19970                             ]
19971                         } 
19972                     ]
19973                 },
19974                 {
19975                     tag: 'td',
19976                     cls: 'separator'
19977                 },
19978                 {
19979                     tag: 'td',
19980                     cn: [
19981                         {
19982                             tag: 'a',
19983                             href: '#',
19984                             cls: 'btn',
19985                             cn: [
19986                                 {
19987                                     tag: 'span',
19988                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19989                                 }
19990                             ]
19991                         }
19992                     ]
19993                 },
19994                 {
19995                     tag: 'td',
19996                     cls: 'separator'
19997                 }
19998             ]
19999         });
20000         
20001         time.createChild({
20002             tag: 'tr',
20003             cn: [
20004                 {
20005                     tag: 'td',
20006                     cn: [
20007                         {
20008                             tag: 'span',
20009                             cls: 'timepicker-hour',
20010                             html: '00'
20011                         }  
20012                     ]
20013                 },
20014                 {
20015                     tag: 'td',
20016                     cls: 'separator',
20017                     html: ':'
20018                 },
20019                 {
20020                     tag: 'td',
20021                     cn: [
20022                         {
20023                             tag: 'span',
20024                             cls: 'timepicker-minute',
20025                             html: '00'
20026                         }  
20027                     ]
20028                 },
20029                 {
20030                     tag: 'td',
20031                     cls: 'separator'
20032                 },
20033                 {
20034                     tag: 'td',
20035                     cn: [
20036                         {
20037                             tag: 'button',
20038                             type: 'button',
20039                             cls: 'btn btn-primary period',
20040                             html: 'AM'
20041                             
20042                         }
20043                     ]
20044                 }
20045             ]
20046         });
20047         
20048         time.createChild({
20049             tag: 'tr',
20050             cn: [
20051                 {
20052                     tag: 'td',
20053                     cn: [
20054                         {
20055                             tag: 'a',
20056                             href: '#',
20057                             cls: 'btn',
20058                             cn: [
20059                                 {
20060                                     tag: 'span',
20061                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20062                                 }
20063                             ]
20064                         }
20065                     ]
20066                 },
20067                 {
20068                     tag: 'td',
20069                     cls: 'separator'
20070                 },
20071                 {
20072                     tag: 'td',
20073                     cn: [
20074                         {
20075                             tag: 'a',
20076                             href: '#',
20077                             cls: 'btn',
20078                             cn: [
20079                                 {
20080                                     tag: 'span',
20081                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20082                                 }
20083                             ]
20084                         }
20085                     ]
20086                 },
20087                 {
20088                     tag: 'td',
20089                     cls: 'separator'
20090                 }
20091             ]
20092         });
20093         
20094     },
20095     
20096     update: function()
20097     {
20098         
20099         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20100         
20101         this.fill();
20102     },
20103     
20104     fill: function() 
20105     {
20106         var hours = this.time.getHours();
20107         var minutes = this.time.getMinutes();
20108         var period = 'AM';
20109         
20110         if(hours > 11){
20111             period = 'PM';
20112         }
20113         
20114         if(hours == 0){
20115             hours = 12;
20116         }
20117         
20118         
20119         if(hours > 12){
20120             hours = hours - 12;
20121         }
20122         
20123         if(hours < 10){
20124             hours = '0' + hours;
20125         }
20126         
20127         if(minutes < 10){
20128             minutes = '0' + minutes;
20129         }
20130         
20131         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20132         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20133         this.pop.select('button', true).first().dom.innerHTML = period;
20134         
20135     },
20136     
20137     place: function()
20138     {   
20139         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20140         
20141         var cls = ['bottom'];
20142         
20143         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20144             cls.pop();
20145             cls.push('top');
20146         }
20147         
20148         cls.push('right');
20149         
20150         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20151             cls.pop();
20152             cls.push('left');
20153         }
20154         
20155         this.picker().addClass(cls.join('-'));
20156         
20157         var _this = this;
20158         
20159         Roo.each(cls, function(c){
20160             if(c == 'bottom'){
20161                 _this.picker().setTop(_this.inputEl().getHeight());
20162                 return;
20163             }
20164             if(c == 'top'){
20165                 _this.picker().setTop(0 - _this.picker().getHeight());
20166                 return;
20167             }
20168             
20169             if(c == 'left'){
20170                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20171                 return;
20172             }
20173             if(c == 'right'){
20174                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20175                 return;
20176             }
20177         });
20178         
20179     },
20180   
20181     onFocus : function()
20182     {
20183         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20184         this.show();
20185     },
20186     
20187     onBlur : function()
20188     {
20189         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20190         this.hide();
20191     },
20192     
20193     show : function()
20194     {
20195         this.picker().show();
20196         this.pop.show();
20197         this.update();
20198         this.place();
20199         
20200         this.fireEvent('show', this, this.date);
20201     },
20202     
20203     hide : function()
20204     {
20205         this.picker().hide();
20206         this.pop.hide();
20207         
20208         this.fireEvent('hide', this, this.date);
20209     },
20210     
20211     setTime : function()
20212     {
20213         this.hide();
20214         this.setValue(this.time.format(this.format));
20215         
20216         this.fireEvent('select', this, this.date);
20217         
20218         
20219     },
20220     
20221     onMousedown: function(e){
20222         e.stopPropagation();
20223         e.preventDefault();
20224     },
20225     
20226     onIncrementHours: function()
20227     {
20228         Roo.log('onIncrementHours');
20229         this.time = this.time.add(Date.HOUR, 1);
20230         this.update();
20231         
20232     },
20233     
20234     onDecrementHours: function()
20235     {
20236         Roo.log('onDecrementHours');
20237         this.time = this.time.add(Date.HOUR, -1);
20238         this.update();
20239     },
20240     
20241     onIncrementMinutes: function()
20242     {
20243         Roo.log('onIncrementMinutes');
20244         this.time = this.time.add(Date.MINUTE, 1);
20245         this.update();
20246     },
20247     
20248     onDecrementMinutes: function()
20249     {
20250         Roo.log('onDecrementMinutes');
20251         this.time = this.time.add(Date.MINUTE, -1);
20252         this.update();
20253     },
20254     
20255     onTogglePeriod: function()
20256     {
20257         Roo.log('onTogglePeriod');
20258         this.time = this.time.add(Date.HOUR, 12);
20259         this.update();
20260     }
20261     
20262    
20263 });
20264
20265 Roo.apply(Roo.bootstrap.TimeField,  {
20266     
20267     content : {
20268         tag: 'tbody',
20269         cn: [
20270             {
20271                 tag: 'tr',
20272                 cn: [
20273                 {
20274                     tag: 'td',
20275                     colspan: '7'
20276                 }
20277                 ]
20278             }
20279         ]
20280     },
20281     
20282     footer : {
20283         tag: 'tfoot',
20284         cn: [
20285             {
20286                 tag: 'tr',
20287                 cn: [
20288                 {
20289                     tag: 'th',
20290                     colspan: '7',
20291                     cls: '',
20292                     cn: [
20293                         {
20294                             tag: 'button',
20295                             cls: 'btn btn-info ok',
20296                             html: 'OK'
20297                         }
20298                     ]
20299                 }
20300
20301                 ]
20302             }
20303         ]
20304     }
20305 });
20306
20307 Roo.apply(Roo.bootstrap.TimeField,  {
20308   
20309     template : {
20310         tag: 'div',
20311         cls: 'datepicker dropdown-menu',
20312         cn: [
20313             {
20314                 tag: 'div',
20315                 cls: 'datepicker-time',
20316                 cn: [
20317                 {
20318                     tag: 'table',
20319                     cls: 'table-condensed',
20320                     cn:[
20321                     Roo.bootstrap.TimeField.content,
20322                     Roo.bootstrap.TimeField.footer
20323                     ]
20324                 }
20325                 ]
20326             }
20327         ]
20328     }
20329 });
20330
20331  
20332
20333  /*
20334  * - LGPL
20335  *
20336  * MonthField
20337  * 
20338  */
20339
20340 /**
20341  * @class Roo.bootstrap.MonthField
20342  * @extends Roo.bootstrap.Input
20343  * Bootstrap MonthField class
20344  * 
20345  * @cfg {String} language default en
20346  * 
20347  * @constructor
20348  * Create a new MonthField
20349  * @param {Object} config The config object
20350  */
20351
20352 Roo.bootstrap.MonthField = function(config){
20353     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20354     
20355     this.addEvents({
20356         /**
20357          * @event show
20358          * Fires when this field show.
20359          * @param {Roo.bootstrap.MonthField} this
20360          * @param {Mixed} date The date value
20361          */
20362         show : true,
20363         /**
20364          * @event show
20365          * Fires when this field hide.
20366          * @param {Roo.bootstrap.MonthField} this
20367          * @param {Mixed} date The date value
20368          */
20369         hide : true,
20370         /**
20371          * @event select
20372          * Fires when select a date.
20373          * @param {Roo.bootstrap.MonthField} this
20374          * @param {String} oldvalue The old value
20375          * @param {String} newvalue The new value
20376          */
20377         select : true
20378     });
20379 };
20380
20381 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20382     
20383     onRender: function(ct, position)
20384     {
20385         
20386         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20387         
20388         this.language = this.language || 'en';
20389         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20390         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20391         
20392         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20393         this.isInline = false;
20394         this.isInput = true;
20395         this.component = this.el.select('.add-on', true).first() || false;
20396         this.component = (this.component && this.component.length === 0) ? false : this.component;
20397         this.hasInput = this.component && this.inputEL().length;
20398         
20399         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20400         
20401         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20402         
20403         this.picker().on('mousedown', this.onMousedown, this);
20404         this.picker().on('click', this.onClick, this);
20405         
20406         this.picker().addClass('datepicker-dropdown');
20407         
20408         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20409             v.setStyle('width', '189px');
20410         });
20411         
20412         this.fillMonths();
20413         
20414         this.update();
20415         
20416         if(this.isInline) {
20417             this.show();
20418         }
20419         
20420     },
20421     
20422     setValue: function(v, suppressEvent)
20423     {   
20424         var o = this.getValue();
20425         
20426         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20427         
20428         this.update();
20429
20430         if(suppressEvent !== true){
20431             this.fireEvent('select', this, o, v);
20432         }
20433         
20434     },
20435     
20436     getValue: function()
20437     {
20438         return this.value;
20439     },
20440     
20441     onClick: function(e) 
20442     {
20443         e.stopPropagation();
20444         e.preventDefault();
20445         
20446         var target = e.getTarget();
20447         
20448         if(target.nodeName.toLowerCase() === 'i'){
20449             target = Roo.get(target).dom.parentNode;
20450         }
20451         
20452         var nodeName = target.nodeName;
20453         var className = target.className;
20454         var html = target.innerHTML;
20455         
20456         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20457             return;
20458         }
20459         
20460         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20461         
20462         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20463         
20464         this.hide();
20465                         
20466     },
20467     
20468     picker : function()
20469     {
20470         return this.pickerEl;
20471     },
20472     
20473     fillMonths: function()
20474     {    
20475         var i = 0;
20476         var months = this.picker().select('>.datepicker-months td', true).first();
20477         
20478         months.dom.innerHTML = '';
20479         
20480         while (i < 12) {
20481             var month = {
20482                 tag: 'span',
20483                 cls: 'month',
20484                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20485             };
20486             
20487             months.createChild(month);
20488         }
20489         
20490     },
20491     
20492     update: function()
20493     {
20494         var _this = this;
20495         
20496         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20497             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20498         }
20499         
20500         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20501             e.removeClass('active');
20502             
20503             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20504                 e.addClass('active');
20505             }
20506         })
20507     },
20508     
20509     place: function()
20510     {
20511         if(this.isInline) {
20512             return;
20513         }
20514         
20515         this.picker().removeClass(['bottom', 'top']);
20516         
20517         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20518             /*
20519              * place to the top of element!
20520              *
20521              */
20522             
20523             this.picker().addClass('top');
20524             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20525             
20526             return;
20527         }
20528         
20529         this.picker().addClass('bottom');
20530         
20531         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20532     },
20533     
20534     onFocus : function()
20535     {
20536         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20537         this.show();
20538     },
20539     
20540     onBlur : function()
20541     {
20542         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20543         
20544         var d = this.inputEl().getValue();
20545         
20546         this.setValue(d);
20547                 
20548         this.hide();
20549     },
20550     
20551     show : function()
20552     {
20553         this.picker().show();
20554         this.picker().select('>.datepicker-months', true).first().show();
20555         this.update();
20556         this.place();
20557         
20558         this.fireEvent('show', this, this.date);
20559     },
20560     
20561     hide : function()
20562     {
20563         if(this.isInline) {
20564             return;
20565         }
20566         this.picker().hide();
20567         this.fireEvent('hide', this, this.date);
20568         
20569     },
20570     
20571     onMousedown: function(e)
20572     {
20573         e.stopPropagation();
20574         e.preventDefault();
20575     },
20576     
20577     keyup: function(e)
20578     {
20579         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20580         this.update();
20581     },
20582
20583     fireKey: function(e)
20584     {
20585         if (!this.picker().isVisible()){
20586             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20587                 this.show();
20588             }
20589             return;
20590         }
20591         
20592         var dir;
20593         
20594         switch(e.keyCode){
20595             case 27: // escape
20596                 this.hide();
20597                 e.preventDefault();
20598                 break;
20599             case 37: // left
20600             case 39: // right
20601                 dir = e.keyCode == 37 ? -1 : 1;
20602                 
20603                 this.vIndex = this.vIndex + dir;
20604                 
20605                 if(this.vIndex < 0){
20606                     this.vIndex = 0;
20607                 }
20608                 
20609                 if(this.vIndex > 11){
20610                     this.vIndex = 11;
20611                 }
20612                 
20613                 if(isNaN(this.vIndex)){
20614                     this.vIndex = 0;
20615                 }
20616                 
20617                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20618                 
20619                 break;
20620             case 38: // up
20621             case 40: // down
20622                 
20623                 dir = e.keyCode == 38 ? -1 : 1;
20624                 
20625                 this.vIndex = this.vIndex + dir * 4;
20626                 
20627                 if(this.vIndex < 0){
20628                     this.vIndex = 0;
20629                 }
20630                 
20631                 if(this.vIndex > 11){
20632                     this.vIndex = 11;
20633                 }
20634                 
20635                 if(isNaN(this.vIndex)){
20636                     this.vIndex = 0;
20637                 }
20638                 
20639                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20640                 break;
20641                 
20642             case 13: // enter
20643                 
20644                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20645                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20646                 }
20647                 
20648                 this.hide();
20649                 e.preventDefault();
20650                 break;
20651             case 9: // tab
20652                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20653                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20654                 }
20655                 this.hide();
20656                 break;
20657             case 16: // shift
20658             case 17: // ctrl
20659             case 18: // alt
20660                 break;
20661             default :
20662                 this.hide();
20663                 
20664         }
20665     },
20666     
20667     remove: function() 
20668     {
20669         this.picker().remove();
20670     }
20671    
20672 });
20673
20674 Roo.apply(Roo.bootstrap.MonthField,  {
20675     
20676     content : {
20677         tag: 'tbody',
20678         cn: [
20679         {
20680             tag: 'tr',
20681             cn: [
20682             {
20683                 tag: 'td',
20684                 colspan: '7'
20685             }
20686             ]
20687         }
20688         ]
20689     },
20690     
20691     dates:{
20692         en: {
20693             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20694             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20695         }
20696     }
20697 });
20698
20699 Roo.apply(Roo.bootstrap.MonthField,  {
20700   
20701     template : {
20702         tag: 'div',
20703         cls: 'datepicker dropdown-menu roo-dynamic',
20704         cn: [
20705             {
20706                 tag: 'div',
20707                 cls: 'datepicker-months',
20708                 cn: [
20709                 {
20710                     tag: 'table',
20711                     cls: 'table-condensed',
20712                     cn:[
20713                         Roo.bootstrap.DateField.content
20714                     ]
20715                 }
20716                 ]
20717             }
20718         ]
20719     }
20720 });
20721
20722  
20723
20724  
20725  /*
20726  * - LGPL
20727  *
20728  * CheckBox
20729  * 
20730  */
20731
20732 /**
20733  * @class Roo.bootstrap.CheckBox
20734  * @extends Roo.bootstrap.Input
20735  * Bootstrap CheckBox class
20736  * 
20737  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20738  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20739  * @cfg {String} boxLabel The text that appears beside the checkbox
20740  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20741  * @cfg {Boolean} checked initnal the element
20742  * @cfg {Boolean} inline inline the element (default false)
20743  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20744  * @cfg {String} tooltip label tooltip
20745  * 
20746  * @constructor
20747  * Create a new CheckBox
20748  * @param {Object} config The config object
20749  */
20750
20751 Roo.bootstrap.CheckBox = function(config){
20752     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20753    
20754     this.addEvents({
20755         /**
20756         * @event check
20757         * Fires when the element is checked or unchecked.
20758         * @param {Roo.bootstrap.CheckBox} this This input
20759         * @param {Boolean} checked The new checked value
20760         */
20761        check : true,
20762        /**
20763         * @event click
20764         * Fires when the element is click.
20765         * @param {Roo.bootstrap.CheckBox} this This input
20766         */
20767        click : true
20768     });
20769     
20770 };
20771
20772 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20773   
20774     inputType: 'checkbox',
20775     inputValue: 1,
20776     valueOff: 0,
20777     boxLabel: false,
20778     checked: false,
20779     weight : false,
20780     inline: false,
20781     tooltip : '',
20782     
20783     getAutoCreate : function()
20784     {
20785         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20786         
20787         var id = Roo.id();
20788         
20789         var cfg = {};
20790         
20791         cfg.cls = 'form-group ' + this.inputType; //input-group
20792         
20793         if(this.inline){
20794             cfg.cls += ' ' + this.inputType + '-inline';
20795         }
20796         
20797         var input =  {
20798             tag: 'input',
20799             id : id,
20800             type : this.inputType,
20801             value : this.inputValue,
20802             cls : 'roo-' + this.inputType, //'form-box',
20803             placeholder : this.placeholder || ''
20804             
20805         };
20806         
20807         if(this.inputType != 'radio'){
20808             var hidden =  {
20809                 tag: 'input',
20810                 type : 'hidden',
20811                 cls : 'roo-hidden-value',
20812                 value : this.checked ? this.inputValue : this.valueOff
20813             };
20814         }
20815         
20816             
20817         if (this.weight) { // Validity check?
20818             cfg.cls += " " + this.inputType + "-" + this.weight;
20819         }
20820         
20821         if (this.disabled) {
20822             input.disabled=true;
20823         }
20824         
20825         if(this.checked){
20826             input.checked = this.checked;
20827         }
20828         
20829         if (this.name) {
20830             
20831             input.name = this.name;
20832             
20833             if(this.inputType != 'radio'){
20834                 hidden.name = this.name;
20835                 input.name = '_hidden_' + this.name;
20836             }
20837         }
20838         
20839         if (this.size) {
20840             input.cls += ' input-' + this.size;
20841         }
20842         
20843         var settings=this;
20844         
20845         ['xs','sm','md','lg'].map(function(size){
20846             if (settings[size]) {
20847                 cfg.cls += ' col-' + size + '-' + settings[size];
20848             }
20849         });
20850         
20851         var inputblock = input;
20852          
20853         if (this.before || this.after) {
20854             
20855             inputblock = {
20856                 cls : 'input-group',
20857                 cn :  [] 
20858             };
20859             
20860             if (this.before) {
20861                 inputblock.cn.push({
20862                     tag :'span',
20863                     cls : 'input-group-addon',
20864                     html : this.before
20865                 });
20866             }
20867             
20868             inputblock.cn.push(input);
20869             
20870             if(this.inputType != 'radio'){
20871                 inputblock.cn.push(hidden);
20872             }
20873             
20874             if (this.after) {
20875                 inputblock.cn.push({
20876                     tag :'span',
20877                     cls : 'input-group-addon',
20878                     html : this.after
20879                 });
20880             }
20881             
20882         }
20883         
20884         if (align ==='left' && this.fieldLabel.length) {
20885 //                Roo.log("left and has label");
20886             cfg.cn = [
20887                 {
20888                     tag: 'label',
20889                     'for' :  id,
20890                     cls : 'control-label',
20891                     html : this.fieldLabel
20892                 },
20893                 {
20894                     cls : "", 
20895                     cn: [
20896                         inputblock
20897                     ]
20898                 }
20899             ];
20900             
20901             if(this.labelWidth > 12){
20902                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20903             }
20904             
20905             if(this.labelWidth < 13 && this.labelmd == 0){
20906                 this.labelmd = this.labelWidth;
20907             }
20908             
20909             if(this.labellg > 0){
20910                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20911                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20912             }
20913             
20914             if(this.labelmd > 0){
20915                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20916                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20917             }
20918             
20919             if(this.labelsm > 0){
20920                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20921                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20922             }
20923             
20924             if(this.labelxs > 0){
20925                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20926                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20927             }
20928             
20929         } else if ( this.fieldLabel.length) {
20930 //                Roo.log(" label");
20931                 cfg.cn = [
20932                    
20933                     {
20934                         tag: this.boxLabel ? 'span' : 'label',
20935                         'for': id,
20936                         cls: 'control-label box-input-label',
20937                         //cls : 'input-group-addon',
20938                         html : this.fieldLabel
20939                     },
20940                     
20941                     inputblock
20942                     
20943                 ];
20944
20945         } else {
20946             
20947 //                Roo.log(" no label && no align");
20948                 cfg.cn = [  inputblock ] ;
20949                 
20950                 
20951         }
20952         
20953         if(this.boxLabel){
20954              var boxLabelCfg = {
20955                 tag: 'label',
20956                 //'for': id, // box label is handled by onclick - so no for...
20957                 cls: 'box-label',
20958                 html: this.boxLabel
20959             };
20960             
20961             if(this.tooltip){
20962                 boxLabelCfg.tooltip = this.tooltip;
20963             }
20964              
20965             cfg.cn.push(boxLabelCfg);
20966         }
20967         
20968         if(this.inputType != 'radio'){
20969             cfg.cn.push(hidden);
20970         }
20971         
20972         return cfg;
20973         
20974     },
20975     
20976     /**
20977      * return the real input element.
20978      */
20979     inputEl: function ()
20980     {
20981         return this.el.select('input.roo-' + this.inputType,true).first();
20982     },
20983     hiddenEl: function ()
20984     {
20985         return this.el.select('input.roo-hidden-value',true).first();
20986     },
20987     
20988     labelEl: function()
20989     {
20990         return this.el.select('label.control-label',true).first();
20991     },
20992     /* depricated... */
20993     
20994     label: function()
20995     {
20996         return this.labelEl();
20997     },
20998     
20999     boxLabelEl: function()
21000     {
21001         return this.el.select('label.box-label',true).first();
21002     },
21003     
21004     initEvents : function()
21005     {
21006 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21007         
21008         this.inputEl().on('click', this.onClick,  this);
21009         
21010         if (this.boxLabel) { 
21011             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21012         }
21013         
21014         this.startValue = this.getValue();
21015         
21016         if(this.groupId){
21017             Roo.bootstrap.CheckBox.register(this);
21018         }
21019     },
21020     
21021     onClick : function(e)
21022     {   
21023         if(this.fireEvent('click', this, e) !== false){
21024             this.setChecked(!this.checked);
21025         }
21026         
21027     },
21028     
21029     setChecked : function(state,suppressEvent)
21030     {
21031         this.startValue = this.getValue();
21032
21033         if(this.inputType == 'radio'){
21034             
21035             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21036                 e.dom.checked = false;
21037             });
21038             
21039             this.inputEl().dom.checked = true;
21040             
21041             this.inputEl().dom.value = this.inputValue;
21042             
21043             if(suppressEvent !== true){
21044                 this.fireEvent('check', this, true);
21045             }
21046             
21047             this.validate();
21048             
21049             return;
21050         }
21051         
21052         this.checked = state;
21053         
21054         this.inputEl().dom.checked = state;
21055         
21056         
21057         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21058         
21059         if(suppressEvent !== true){
21060             this.fireEvent('check', this, state);
21061         }
21062         
21063         this.validate();
21064     },
21065     
21066     getValue : function()
21067     {
21068         if(this.inputType == 'radio'){
21069             return this.getGroupValue();
21070         }
21071         
21072         return this.hiddenEl().dom.value;
21073         
21074     },
21075     
21076     getGroupValue : function()
21077     {
21078         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21079             return '';
21080         }
21081         
21082         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21083     },
21084     
21085     setValue : function(v,suppressEvent)
21086     {
21087         if(this.inputType == 'radio'){
21088             this.setGroupValue(v, suppressEvent);
21089             return;
21090         }
21091         
21092         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21093         
21094         this.validate();
21095     },
21096     
21097     setGroupValue : function(v, suppressEvent)
21098     {
21099         this.startValue = this.getValue();
21100         
21101         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21102             e.dom.checked = false;
21103             
21104             if(e.dom.value == v){
21105                 e.dom.checked = true;
21106             }
21107         });
21108         
21109         if(suppressEvent !== true){
21110             this.fireEvent('check', this, true);
21111         }
21112
21113         this.validate();
21114         
21115         return;
21116     },
21117     
21118     validate : function()
21119     {
21120         if(this.getVisibilityEl().hasClass('hidden')){
21121             return true;
21122         }
21123         
21124         if(
21125                 this.disabled || 
21126                 (this.inputType == 'radio' && this.validateRadio()) ||
21127                 (this.inputType == 'checkbox' && this.validateCheckbox())
21128         ){
21129             this.markValid();
21130             return true;
21131         }
21132         
21133         this.markInvalid();
21134         return false;
21135     },
21136     
21137     validateRadio : function()
21138     {
21139         if(this.getVisibilityEl().hasClass('hidden')){
21140             return true;
21141         }
21142         
21143         if(this.allowBlank){
21144             return true;
21145         }
21146         
21147         var valid = false;
21148         
21149         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150             if(!e.dom.checked){
21151                 return;
21152             }
21153             
21154             valid = true;
21155             
21156             return false;
21157         });
21158         
21159         return valid;
21160     },
21161     
21162     validateCheckbox : function()
21163     {
21164         if(!this.groupId){
21165             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21166             //return (this.getValue() == this.inputValue) ? true : false;
21167         }
21168         
21169         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21170         
21171         if(!group){
21172             return false;
21173         }
21174         
21175         var r = false;
21176         
21177         for(var i in group){
21178             if(group[i].el.isVisible(true)){
21179                 r = false;
21180                 break;
21181             }
21182             
21183             r = true;
21184         }
21185         
21186         for(var i in group){
21187             if(r){
21188                 break;
21189             }
21190             
21191             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21192         }
21193         
21194         return r;
21195     },
21196     
21197     /**
21198      * Mark this field as valid
21199      */
21200     markValid : function()
21201     {
21202         var _this = this;
21203         
21204         this.fireEvent('valid', this);
21205         
21206         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21207         
21208         if(this.groupId){
21209             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21210         }
21211         
21212         if(label){
21213             label.markValid();
21214         }
21215
21216         if(this.inputType == 'radio'){
21217             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21218                 var fg = e.findParent('.form-group', false, true);
21219                 if (Roo.bootstrap.version == 3) {
21220                     fg.removeClass([_this.invalidClass, _this.validClass]);
21221                     fg.addClass(_this.validClass);
21222                 } else {
21223                     fg.removeClass(['is-valid', 'is-invalid']);
21224                     fg.addClass('is-valid');
21225                 }
21226             });
21227             
21228             return;
21229         }
21230
21231         if(!this.groupId){
21232             var fg = this.el.findParent('.form-group', false, true);
21233             if (Roo.bootstrap.version == 3) {
21234                 fg.removeClass([this.invalidClass, this.validClass]);
21235                 fg.addClass(this.validClass);
21236             } else {
21237                 fg.removeClass(['is-valid', 'is-invalid']);
21238                 fg.addClass('is-valid');
21239             }
21240             return;
21241         }
21242         
21243         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21244         
21245         if(!group){
21246             return;
21247         }
21248         
21249         for(var i in group){
21250             var fg = group[i].el.findParent('.form-group', false, true);
21251             if (Roo.bootstrap.version == 3) {
21252                 fg.removeClass([this.invalidClass, this.validClass]);
21253                 fg.addClass(this.validClass);
21254             } else {
21255                 fg.removeClass(['is-valid', 'is-invalid']);
21256                 fg.addClass('is-valid');
21257             }
21258         }
21259     },
21260     
21261      /**
21262      * Mark this field as invalid
21263      * @param {String} msg The validation message
21264      */
21265     markInvalid : function(msg)
21266     {
21267         if(this.allowBlank){
21268             return;
21269         }
21270         
21271         var _this = this;
21272         
21273         this.fireEvent('invalid', this, msg);
21274         
21275         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21276         
21277         if(this.groupId){
21278             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21279         }
21280         
21281         if(label){
21282             label.markInvalid();
21283         }
21284             
21285         if(this.inputType == 'radio'){
21286             
21287             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21288                 var fg = e.findParent('.form-group', false, true);
21289                 if (Roo.bootstrap.version == 3) {
21290                     fg.removeClass([_this.invalidClass, _this.validClass]);
21291                     fg.addClass(_this.invalidClass);
21292                 } else {
21293                     fg.removeClass(['is-invalid', 'is-valid']);
21294                     fg.addClass('is-invalid');
21295                 }
21296             });
21297             
21298             return;
21299         }
21300         
21301         if(!this.groupId){
21302             var fg = this.el.findParent('.form-group', false, true);
21303             if (Roo.bootstrap.version == 3) {
21304                 fg.removeClass([_this.invalidClass, _this.validClass]);
21305                 fg.addClass(_this.invalidClass);
21306             } else {
21307                 fg.removeClass(['is-invalid', 'is-valid']);
21308                 fg.addClass('is-invalid');
21309             }
21310             return;
21311         }
21312         
21313         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21314         
21315         if(!group){
21316             return;
21317         }
21318         
21319         for(var i in group){
21320             var fg = group[i].el.findParent('.form-group', false, true);
21321             if (Roo.bootstrap.version == 3) {
21322                 fg.removeClass([_this.invalidClass, _this.validClass]);
21323                 fg.addClass(_this.invalidClass);
21324             } else {
21325                 fg.removeClass(['is-invalid', 'is-valid']);
21326                 fg.addClass('is-invalid');
21327             }
21328         }
21329         
21330     },
21331     
21332     clearInvalid : function()
21333     {
21334         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21335         
21336         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21337         
21338         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21339         
21340         if (label && label.iconEl) {
21341             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21342             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21343         }
21344     },
21345     
21346     disable : function()
21347     {
21348         if(this.inputType != 'radio'){
21349             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21350             return;
21351         }
21352         
21353         var _this = this;
21354         
21355         if(this.rendered){
21356             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21357                 _this.getActionEl().addClass(this.disabledClass);
21358                 e.dom.disabled = true;
21359             });
21360         }
21361         
21362         this.disabled = true;
21363         this.fireEvent("disable", this);
21364         return this;
21365     },
21366
21367     enable : function()
21368     {
21369         if(this.inputType != 'radio'){
21370             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21371             return;
21372         }
21373         
21374         var _this = this;
21375         
21376         if(this.rendered){
21377             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21378                 _this.getActionEl().removeClass(this.disabledClass);
21379                 e.dom.disabled = false;
21380             });
21381         }
21382         
21383         this.disabled = false;
21384         this.fireEvent("enable", this);
21385         return this;
21386     },
21387     
21388     setBoxLabel : function(v)
21389     {
21390         this.boxLabel = v;
21391         
21392         if(this.rendered){
21393             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21394         }
21395     }
21396
21397 });
21398
21399 Roo.apply(Roo.bootstrap.CheckBox, {
21400     
21401     groups: {},
21402     
21403      /**
21404     * register a CheckBox Group
21405     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21406     */
21407     register : function(checkbox)
21408     {
21409         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21410             this.groups[checkbox.groupId] = {};
21411         }
21412         
21413         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21414             return;
21415         }
21416         
21417         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21418         
21419     },
21420     /**
21421     * fetch a CheckBox Group based on the group ID
21422     * @param {string} the group ID
21423     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21424     */
21425     get: function(groupId) {
21426         if (typeof(this.groups[groupId]) == 'undefined') {
21427             return false;
21428         }
21429         
21430         return this.groups[groupId] ;
21431     }
21432     
21433     
21434 });
21435 /*
21436  * - LGPL
21437  *
21438  * RadioItem
21439  * 
21440  */
21441
21442 /**
21443  * @class Roo.bootstrap.Radio
21444  * @extends Roo.bootstrap.Component
21445  * Bootstrap Radio class
21446  * @cfg {String} boxLabel - the label associated
21447  * @cfg {String} value - the value of radio
21448  * 
21449  * @constructor
21450  * Create a new Radio
21451  * @param {Object} config The config object
21452  */
21453 Roo.bootstrap.Radio = function(config){
21454     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21455     
21456 };
21457
21458 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21459     
21460     boxLabel : '',
21461     
21462     value : '',
21463     
21464     getAutoCreate : function()
21465     {
21466         var cfg = {
21467             tag : 'div',
21468             cls : 'form-group radio',
21469             cn : [
21470                 {
21471                     tag : 'label',
21472                     cls : 'box-label',
21473                     html : this.boxLabel
21474                 }
21475             ]
21476         };
21477         
21478         return cfg;
21479     },
21480     
21481     initEvents : function() 
21482     {
21483         this.parent().register(this);
21484         
21485         this.el.on('click', this.onClick, this);
21486         
21487     },
21488     
21489     onClick : function(e)
21490     {
21491         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21492             this.setChecked(true);
21493         }
21494     },
21495     
21496     setChecked : function(state, suppressEvent)
21497     {
21498         this.parent().setValue(this.value, suppressEvent);
21499         
21500     },
21501     
21502     setBoxLabel : function(v)
21503     {
21504         this.boxLabel = v;
21505         
21506         if(this.rendered){
21507             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21508         }
21509     }
21510     
21511 });
21512  
21513
21514  /*
21515  * - LGPL
21516  *
21517  * Input
21518  * 
21519  */
21520
21521 /**
21522  * @class Roo.bootstrap.SecurePass
21523  * @extends Roo.bootstrap.Input
21524  * Bootstrap SecurePass class
21525  *
21526  * 
21527  * @constructor
21528  * Create a new SecurePass
21529  * @param {Object} config The config object
21530  */
21531  
21532 Roo.bootstrap.SecurePass = function (config) {
21533     // these go here, so the translation tool can replace them..
21534     this.errors = {
21535         PwdEmpty: "Please type a password, and then retype it to confirm.",
21536         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21537         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21538         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21539         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21540         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21541         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21542         TooWeak: "Your password is Too Weak."
21543     },
21544     this.meterLabel = "Password strength:";
21545     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21546     this.meterClass = [
21547         "roo-password-meter-tooweak", 
21548         "roo-password-meter-weak", 
21549         "roo-password-meter-medium", 
21550         "roo-password-meter-strong", 
21551         "roo-password-meter-grey"
21552     ];
21553     
21554     this.errors = {};
21555     
21556     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21557 }
21558
21559 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21560     /**
21561      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21562      * {
21563      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21564      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21565      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21566      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21567      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21568      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21569      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21570      * })
21571      */
21572     // private
21573     
21574     meterWidth: 300,
21575     errorMsg :'',    
21576     errors: false,
21577     imageRoot: '/',
21578     /**
21579      * @cfg {String/Object} Label for the strength meter (defaults to
21580      * 'Password strength:')
21581      */
21582     // private
21583     meterLabel: '',
21584     /**
21585      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21586      * ['Weak', 'Medium', 'Strong'])
21587      */
21588     // private    
21589     pwdStrengths: false,    
21590     // private
21591     strength: 0,
21592     // private
21593     _lastPwd: null,
21594     // private
21595     kCapitalLetter: 0,
21596     kSmallLetter: 1,
21597     kDigit: 2,
21598     kPunctuation: 3,
21599     
21600     insecure: false,
21601     // private
21602     initEvents: function ()
21603     {
21604         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21605
21606         if (this.el.is('input[type=password]') && Roo.isSafari) {
21607             this.el.on('keydown', this.SafariOnKeyDown, this);
21608         }
21609
21610         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21611     },
21612     // private
21613     onRender: function (ct, position)
21614     {
21615         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21616         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21617         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21618
21619         this.trigger.createChild({
21620                    cn: [
21621                     {
21622                     //id: 'PwdMeter',
21623                     tag: 'div',
21624                     cls: 'roo-password-meter-grey col-xs-12',
21625                     style: {
21626                         //width: 0,
21627                         //width: this.meterWidth + 'px'                                                
21628                         }
21629                     },
21630                     {                            
21631                          cls: 'roo-password-meter-text'                          
21632                     }
21633                 ]            
21634         });
21635
21636          
21637         if (this.hideTrigger) {
21638             this.trigger.setDisplayed(false);
21639         }
21640         this.setSize(this.width || '', this.height || '');
21641     },
21642     // private
21643     onDestroy: function ()
21644     {
21645         if (this.trigger) {
21646             this.trigger.removeAllListeners();
21647             this.trigger.remove();
21648         }
21649         if (this.wrap) {
21650             this.wrap.remove();
21651         }
21652         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21653     },
21654     // private
21655     checkStrength: function ()
21656     {
21657         var pwd = this.inputEl().getValue();
21658         if (pwd == this._lastPwd) {
21659             return;
21660         }
21661
21662         var strength;
21663         if (this.ClientSideStrongPassword(pwd)) {
21664             strength = 3;
21665         } else if (this.ClientSideMediumPassword(pwd)) {
21666             strength = 2;
21667         } else if (this.ClientSideWeakPassword(pwd)) {
21668             strength = 1;
21669         } else {
21670             strength = 0;
21671         }
21672         
21673         Roo.log('strength1: ' + strength);
21674         
21675         //var pm = this.trigger.child('div/div/div').dom;
21676         var pm = this.trigger.child('div/div');
21677         pm.removeClass(this.meterClass);
21678         pm.addClass(this.meterClass[strength]);
21679                 
21680         
21681         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21682                 
21683         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21684         
21685         this._lastPwd = pwd;
21686     },
21687     reset: function ()
21688     {
21689         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21690         
21691         this._lastPwd = '';
21692         
21693         var pm = this.trigger.child('div/div');
21694         pm.removeClass(this.meterClass);
21695         pm.addClass('roo-password-meter-grey');        
21696         
21697         
21698         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21699         
21700         pt.innerHTML = '';
21701         this.inputEl().dom.type='password';
21702     },
21703     // private
21704     validateValue: function (value)
21705     {
21706         
21707         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21708             return false;
21709         }
21710         if (value.length == 0) {
21711             if (this.allowBlank) {
21712                 this.clearInvalid();
21713                 return true;
21714             }
21715
21716             this.markInvalid(this.errors.PwdEmpty);
21717             this.errorMsg = this.errors.PwdEmpty;
21718             return false;
21719         }
21720         
21721         if(this.insecure){
21722             return true;
21723         }
21724         
21725         if ('[\x21-\x7e]*'.match(value)) {
21726             this.markInvalid(this.errors.PwdBadChar);
21727             this.errorMsg = this.errors.PwdBadChar;
21728             return false;
21729         }
21730         if (value.length < 6) {
21731             this.markInvalid(this.errors.PwdShort);
21732             this.errorMsg = this.errors.PwdShort;
21733             return false;
21734         }
21735         if (value.length > 16) {
21736             this.markInvalid(this.errors.PwdLong);
21737             this.errorMsg = this.errors.PwdLong;
21738             return false;
21739         }
21740         var strength;
21741         if (this.ClientSideStrongPassword(value)) {
21742             strength = 3;
21743         } else if (this.ClientSideMediumPassword(value)) {
21744             strength = 2;
21745         } else if (this.ClientSideWeakPassword(value)) {
21746             strength = 1;
21747         } else {
21748             strength = 0;
21749         }
21750
21751         
21752         if (strength < 2) {
21753             //this.markInvalid(this.errors.TooWeak);
21754             this.errorMsg = this.errors.TooWeak;
21755             //return false;
21756         }
21757         
21758         
21759         console.log('strength2: ' + strength);
21760         
21761         //var pm = this.trigger.child('div/div/div').dom;
21762         
21763         var pm = this.trigger.child('div/div');
21764         pm.removeClass(this.meterClass);
21765         pm.addClass(this.meterClass[strength]);
21766                 
21767         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21768                 
21769         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21770         
21771         this.errorMsg = ''; 
21772         return true;
21773     },
21774     // private
21775     CharacterSetChecks: function (type)
21776     {
21777         this.type = type;
21778         this.fResult = false;
21779     },
21780     // private
21781     isctype: function (character, type)
21782     {
21783         switch (type) {  
21784             case this.kCapitalLetter:
21785                 if (character >= 'A' && character <= 'Z') {
21786                     return true;
21787                 }
21788                 break;
21789             
21790             case this.kSmallLetter:
21791                 if (character >= 'a' && character <= 'z') {
21792                     return true;
21793                 }
21794                 break;
21795             
21796             case this.kDigit:
21797                 if (character >= '0' && character <= '9') {
21798                     return true;
21799                 }
21800                 break;
21801             
21802             case this.kPunctuation:
21803                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21804                     return true;
21805                 }
21806                 break;
21807             
21808             default:
21809                 return false;
21810         }
21811
21812     },
21813     // private
21814     IsLongEnough: function (pwd, size)
21815     {
21816         return !(pwd == null || isNaN(size) || pwd.length < size);
21817     },
21818     // private
21819     SpansEnoughCharacterSets: function (word, nb)
21820     {
21821         if (!this.IsLongEnough(word, nb))
21822         {
21823             return false;
21824         }
21825
21826         var characterSetChecks = new Array(
21827             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21828             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21829         );
21830         
21831         for (var index = 0; index < word.length; ++index) {
21832             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21833                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21834                     characterSetChecks[nCharSet].fResult = true;
21835                     break;
21836                 }
21837             }
21838         }
21839
21840         var nCharSets = 0;
21841         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21842             if (characterSetChecks[nCharSet].fResult) {
21843                 ++nCharSets;
21844             }
21845         }
21846
21847         if (nCharSets < nb) {
21848             return false;
21849         }
21850         return true;
21851     },
21852     // private
21853     ClientSideStrongPassword: function (pwd)
21854     {
21855         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21856     },
21857     // private
21858     ClientSideMediumPassword: function (pwd)
21859     {
21860         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21861     },
21862     // private
21863     ClientSideWeakPassword: function (pwd)
21864     {
21865         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21866     }
21867           
21868 })//<script type="text/javascript">
21869
21870 /*
21871  * Based  Ext JS Library 1.1.1
21872  * Copyright(c) 2006-2007, Ext JS, LLC.
21873  * LGPL
21874  *
21875  */
21876  
21877 /**
21878  * @class Roo.HtmlEditorCore
21879  * @extends Roo.Component
21880  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21881  *
21882  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21883  */
21884
21885 Roo.HtmlEditorCore = function(config){
21886     
21887     
21888     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21889     
21890     
21891     this.addEvents({
21892         /**
21893          * @event initialize
21894          * Fires when the editor is fully initialized (including the iframe)
21895          * @param {Roo.HtmlEditorCore} this
21896          */
21897         initialize: true,
21898         /**
21899          * @event activate
21900          * Fires when the editor is first receives the focus. Any insertion must wait
21901          * until after this event.
21902          * @param {Roo.HtmlEditorCore} this
21903          */
21904         activate: true,
21905          /**
21906          * @event beforesync
21907          * Fires before the textarea is updated with content from the editor iframe. Return false
21908          * to cancel the sync.
21909          * @param {Roo.HtmlEditorCore} this
21910          * @param {String} html
21911          */
21912         beforesync: true,
21913          /**
21914          * @event beforepush
21915          * Fires before the iframe editor is updated with content from the textarea. Return false
21916          * to cancel the push.
21917          * @param {Roo.HtmlEditorCore} this
21918          * @param {String} html
21919          */
21920         beforepush: true,
21921          /**
21922          * @event sync
21923          * Fires when the textarea is updated with content from the editor iframe.
21924          * @param {Roo.HtmlEditorCore} this
21925          * @param {String} html
21926          */
21927         sync: true,
21928          /**
21929          * @event push
21930          * Fires when the iframe editor is updated with content from the textarea.
21931          * @param {Roo.HtmlEditorCore} this
21932          * @param {String} html
21933          */
21934         push: true,
21935         
21936         /**
21937          * @event editorevent
21938          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21939          * @param {Roo.HtmlEditorCore} this
21940          */
21941         editorevent: true
21942         
21943     });
21944     
21945     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21946     
21947     // defaults : white / black...
21948     this.applyBlacklists();
21949     
21950     
21951     
21952 };
21953
21954
21955 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21956
21957
21958      /**
21959      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21960      */
21961     
21962     owner : false,
21963     
21964      /**
21965      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21966      *                        Roo.resizable.
21967      */
21968     resizable : false,
21969      /**
21970      * @cfg {Number} height (in pixels)
21971      */   
21972     height: 300,
21973    /**
21974      * @cfg {Number} width (in pixels)
21975      */   
21976     width: 500,
21977     
21978     /**
21979      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21980      * 
21981      */
21982     stylesheets: false,
21983     
21984     // id of frame..
21985     frameId: false,
21986     
21987     // private properties
21988     validationEvent : false,
21989     deferHeight: true,
21990     initialized : false,
21991     activated : false,
21992     sourceEditMode : false,
21993     onFocus : Roo.emptyFn,
21994     iframePad:3,
21995     hideMode:'offsets',
21996     
21997     clearUp: true,
21998     
21999     // blacklist + whitelisted elements..
22000     black: false,
22001     white: false,
22002      
22003     bodyCls : '',
22004
22005     /**
22006      * Protected method that will not generally be called directly. It
22007      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22008      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22009      */
22010     getDocMarkup : function(){
22011         // body styles..
22012         var st = '';
22013         
22014         // inherit styels from page...?? 
22015         if (this.stylesheets === false) {
22016             
22017             Roo.get(document.head).select('style').each(function(node) {
22018                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22019             });
22020             
22021             Roo.get(document.head).select('link').each(function(node) { 
22022                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22023             });
22024             
22025         } else if (!this.stylesheets.length) {
22026                 // simple..
22027                 st = '<style type="text/css">' +
22028                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22029                    '</style>';
22030         } else { 
22031             st = '<style type="text/css">' +
22032                     this.stylesheets +
22033                 '</style>';
22034         }
22035         
22036         st +=  '<style type="text/css">' +
22037             'IMG { cursor: pointer } ' +
22038         '</style>';
22039
22040         var cls = 'roo-htmleditor-body';
22041         
22042         if(this.bodyCls.length){
22043             cls += ' ' + this.bodyCls;
22044         }
22045         
22046         return '<html><head>' + st  +
22047             //<style type="text/css">' +
22048             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22049             //'</style>' +
22050             ' </head><body class="' +  cls + '"></body></html>';
22051     },
22052
22053     // private
22054     onRender : function(ct, position)
22055     {
22056         var _t = this;
22057         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22058         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22059         
22060         
22061         this.el.dom.style.border = '0 none';
22062         this.el.dom.setAttribute('tabIndex', -1);
22063         this.el.addClass('x-hidden hide');
22064         
22065         
22066         
22067         if(Roo.isIE){ // fix IE 1px bogus margin
22068             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22069         }
22070        
22071         
22072         this.frameId = Roo.id();
22073         
22074          
22075         
22076         var iframe = this.owner.wrap.createChild({
22077             tag: 'iframe',
22078             cls: 'form-control', // bootstrap..
22079             id: this.frameId,
22080             name: this.frameId,
22081             frameBorder : 'no',
22082             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22083         }, this.el
22084         );
22085         
22086         
22087         this.iframe = iframe.dom;
22088
22089          this.assignDocWin();
22090         
22091         this.doc.designMode = 'on';
22092        
22093         this.doc.open();
22094         this.doc.write(this.getDocMarkup());
22095         this.doc.close();
22096
22097         
22098         var task = { // must defer to wait for browser to be ready
22099             run : function(){
22100                 //console.log("run task?" + this.doc.readyState);
22101                 this.assignDocWin();
22102                 if(this.doc.body || this.doc.readyState == 'complete'){
22103                     try {
22104                         this.doc.designMode="on";
22105                     } catch (e) {
22106                         return;
22107                     }
22108                     Roo.TaskMgr.stop(task);
22109                     this.initEditor.defer(10, this);
22110                 }
22111             },
22112             interval : 10,
22113             duration: 10000,
22114             scope: this
22115         };
22116         Roo.TaskMgr.start(task);
22117
22118     },
22119
22120     // private
22121     onResize : function(w, h)
22122     {
22123          Roo.log('resize: ' +w + ',' + h );
22124         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22125         if(!this.iframe){
22126             return;
22127         }
22128         if(typeof w == 'number'){
22129             
22130             this.iframe.style.width = w + 'px';
22131         }
22132         if(typeof h == 'number'){
22133             
22134             this.iframe.style.height = h + 'px';
22135             if(this.doc){
22136                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22137             }
22138         }
22139         
22140     },
22141
22142     /**
22143      * Toggles the editor between standard and source edit mode.
22144      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22145      */
22146     toggleSourceEdit : function(sourceEditMode){
22147         
22148         this.sourceEditMode = sourceEditMode === true;
22149         
22150         if(this.sourceEditMode){
22151  
22152             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22153             
22154         }else{
22155             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22156             //this.iframe.className = '';
22157             this.deferFocus();
22158         }
22159         //this.setSize(this.owner.wrap.getSize());
22160         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22161     },
22162
22163     
22164   
22165
22166     /**
22167      * Protected method that will not generally be called directly. If you need/want
22168      * custom HTML cleanup, this is the method you should override.
22169      * @param {String} html The HTML to be cleaned
22170      * return {String} The cleaned HTML
22171      */
22172     cleanHtml : function(html){
22173         html = String(html);
22174         if(html.length > 5){
22175             if(Roo.isSafari){ // strip safari nonsense
22176                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22177             }
22178         }
22179         if(html == '&nbsp;'){
22180             html = '';
22181         }
22182         return html;
22183     },
22184
22185     /**
22186      * HTML Editor -> Textarea
22187      * Protected method that will not generally be called directly. Syncs the contents
22188      * of the editor iframe with the textarea.
22189      */
22190     syncValue : function(){
22191         if(this.initialized){
22192             var bd = (this.doc.body || this.doc.documentElement);
22193             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22194             var html = bd.innerHTML;
22195             if(Roo.isSafari){
22196                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22197                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22198                 if(m && m[1]){
22199                     html = '<div style="'+m[0]+'">' + html + '</div>';
22200                 }
22201             }
22202             html = this.cleanHtml(html);
22203             // fix up the special chars.. normaly like back quotes in word...
22204             // however we do not want to do this with chinese..
22205             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22206                 var cc = b.charCodeAt();
22207                 if (
22208                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22209                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22210                     (cc >= 0xf900 && cc < 0xfb00 )
22211                 ) {
22212                         return b;
22213                 }
22214                 return "&#"+cc+";" 
22215             });
22216             if(this.owner.fireEvent('beforesync', this, html) !== false){
22217                 this.el.dom.value = html;
22218                 this.owner.fireEvent('sync', this, html);
22219             }
22220         }
22221     },
22222
22223     /**
22224      * Protected method that will not generally be called directly. Pushes the value of the textarea
22225      * into the iframe editor.
22226      */
22227     pushValue : function(){
22228         if(this.initialized){
22229             var v = this.el.dom.value.trim();
22230             
22231 //            if(v.length < 1){
22232 //                v = '&#160;';
22233 //            }
22234             
22235             if(this.owner.fireEvent('beforepush', this, v) !== false){
22236                 var d = (this.doc.body || this.doc.documentElement);
22237                 d.innerHTML = v;
22238                 this.cleanUpPaste();
22239                 this.el.dom.value = d.innerHTML;
22240                 this.owner.fireEvent('push', this, v);
22241             }
22242         }
22243     },
22244
22245     // private
22246     deferFocus : function(){
22247         this.focus.defer(10, this);
22248     },
22249
22250     // doc'ed in Field
22251     focus : function(){
22252         if(this.win && !this.sourceEditMode){
22253             this.win.focus();
22254         }else{
22255             this.el.focus();
22256         }
22257     },
22258     
22259     assignDocWin: function()
22260     {
22261         var iframe = this.iframe;
22262         
22263          if(Roo.isIE){
22264             this.doc = iframe.contentWindow.document;
22265             this.win = iframe.contentWindow;
22266         } else {
22267 //            if (!Roo.get(this.frameId)) {
22268 //                return;
22269 //            }
22270 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22271 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22272             
22273             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22274                 return;
22275             }
22276             
22277             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22278             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22279         }
22280     },
22281     
22282     // private
22283     initEditor : function(){
22284         //console.log("INIT EDITOR");
22285         this.assignDocWin();
22286         
22287         
22288         
22289         this.doc.designMode="on";
22290         this.doc.open();
22291         this.doc.write(this.getDocMarkup());
22292         this.doc.close();
22293         
22294         var dbody = (this.doc.body || this.doc.documentElement);
22295         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22296         // this copies styles from the containing element into thsi one..
22297         // not sure why we need all of this..
22298         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22299         
22300         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22301         //ss['background-attachment'] = 'fixed'; // w3c
22302         dbody.bgProperties = 'fixed'; // ie
22303         //Roo.DomHelper.applyStyles(dbody, ss);
22304         Roo.EventManager.on(this.doc, {
22305             //'mousedown': this.onEditorEvent,
22306             'mouseup': this.onEditorEvent,
22307             'dblclick': this.onEditorEvent,
22308             'click': this.onEditorEvent,
22309             'keyup': this.onEditorEvent,
22310             buffer:100,
22311             scope: this
22312         });
22313         if(Roo.isGecko){
22314             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22315         }
22316         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22317             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22318         }
22319         this.initialized = true;
22320
22321         this.owner.fireEvent('initialize', this);
22322         this.pushValue();
22323     },
22324
22325     // private
22326     onDestroy : function(){
22327         
22328         
22329         
22330         if(this.rendered){
22331             
22332             //for (var i =0; i < this.toolbars.length;i++) {
22333             //    // fixme - ask toolbars for heights?
22334             //    this.toolbars[i].onDestroy();
22335            // }
22336             
22337             //this.wrap.dom.innerHTML = '';
22338             //this.wrap.remove();
22339         }
22340     },
22341
22342     // private
22343     onFirstFocus : function(){
22344         
22345         this.assignDocWin();
22346         
22347         
22348         this.activated = true;
22349          
22350     
22351         if(Roo.isGecko){ // prevent silly gecko errors
22352             this.win.focus();
22353             var s = this.win.getSelection();
22354             if(!s.focusNode || s.focusNode.nodeType != 3){
22355                 var r = s.getRangeAt(0);
22356                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22357                 r.collapse(true);
22358                 this.deferFocus();
22359             }
22360             try{
22361                 this.execCmd('useCSS', true);
22362                 this.execCmd('styleWithCSS', false);
22363             }catch(e){}
22364         }
22365         this.owner.fireEvent('activate', this);
22366     },
22367
22368     // private
22369     adjustFont: function(btn){
22370         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22371         //if(Roo.isSafari){ // safari
22372         //    adjust *= 2;
22373        // }
22374         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22375         if(Roo.isSafari){ // safari
22376             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22377             v =  (v < 10) ? 10 : v;
22378             v =  (v > 48) ? 48 : v;
22379             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22380             
22381         }
22382         
22383         
22384         v = Math.max(1, v+adjust);
22385         
22386         this.execCmd('FontSize', v  );
22387     },
22388
22389     onEditorEvent : function(e)
22390     {
22391         this.owner.fireEvent('editorevent', this, e);
22392       //  this.updateToolbar();
22393         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22394     },
22395
22396     insertTag : function(tg)
22397     {
22398         // could be a bit smarter... -> wrap the current selected tRoo..
22399         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22400             
22401             range = this.createRange(this.getSelection());
22402             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22403             wrappingNode.appendChild(range.extractContents());
22404             range.insertNode(wrappingNode);
22405
22406             return;
22407             
22408             
22409             
22410         }
22411         this.execCmd("formatblock",   tg);
22412         
22413     },
22414     
22415     insertText : function(txt)
22416     {
22417         
22418         
22419         var range = this.createRange();
22420         range.deleteContents();
22421                //alert(Sender.getAttribute('label'));
22422                
22423         range.insertNode(this.doc.createTextNode(txt));
22424     } ,
22425     
22426      
22427
22428     /**
22429      * Executes a Midas editor command on the editor document and performs necessary focus and
22430      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22431      * @param {String} cmd The Midas command
22432      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22433      */
22434     relayCmd : function(cmd, value){
22435         this.win.focus();
22436         this.execCmd(cmd, value);
22437         this.owner.fireEvent('editorevent', this);
22438         //this.updateToolbar();
22439         this.owner.deferFocus();
22440     },
22441
22442     /**
22443      * Executes a Midas editor command directly on the editor document.
22444      * For visual commands, you should use {@link #relayCmd} instead.
22445      * <b>This should only be called after the editor is initialized.</b>
22446      * @param {String} cmd The Midas command
22447      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22448      */
22449     execCmd : function(cmd, value){
22450         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22451         this.syncValue();
22452     },
22453  
22454  
22455    
22456     /**
22457      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22458      * to insert tRoo.
22459      * @param {String} text | dom node.. 
22460      */
22461     insertAtCursor : function(text)
22462     {
22463         
22464         if(!this.activated){
22465             return;
22466         }
22467         /*
22468         if(Roo.isIE){
22469             this.win.focus();
22470             var r = this.doc.selection.createRange();
22471             if(r){
22472                 r.collapse(true);
22473                 r.pasteHTML(text);
22474                 this.syncValue();
22475                 this.deferFocus();
22476             
22477             }
22478             return;
22479         }
22480         */
22481         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22482             this.win.focus();
22483             
22484             
22485             // from jquery ui (MIT licenced)
22486             var range, node;
22487             var win = this.win;
22488             
22489             if (win.getSelection && win.getSelection().getRangeAt) {
22490                 range = win.getSelection().getRangeAt(0);
22491                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22492                 range.insertNode(node);
22493             } else if (win.document.selection && win.document.selection.createRange) {
22494                 // no firefox support
22495                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22496                 win.document.selection.createRange().pasteHTML(txt);
22497             } else {
22498                 // no firefox support
22499                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22500                 this.execCmd('InsertHTML', txt);
22501             } 
22502             
22503             this.syncValue();
22504             
22505             this.deferFocus();
22506         }
22507     },
22508  // private
22509     mozKeyPress : function(e){
22510         if(e.ctrlKey){
22511             var c = e.getCharCode(), cmd;
22512           
22513             if(c > 0){
22514                 c = String.fromCharCode(c).toLowerCase();
22515                 switch(c){
22516                     case 'b':
22517                         cmd = 'bold';
22518                         break;
22519                     case 'i':
22520                         cmd = 'italic';
22521                         break;
22522                     
22523                     case 'u':
22524                         cmd = 'underline';
22525                         break;
22526                     
22527                     case 'v':
22528                         this.cleanUpPaste.defer(100, this);
22529                         return;
22530                         
22531                 }
22532                 if(cmd){
22533                     this.win.focus();
22534                     this.execCmd(cmd);
22535                     this.deferFocus();
22536                     e.preventDefault();
22537                 }
22538                 
22539             }
22540         }
22541     },
22542
22543     // private
22544     fixKeys : function(){ // load time branching for fastest keydown performance
22545         if(Roo.isIE){
22546             return function(e){
22547                 var k = e.getKey(), r;
22548                 if(k == e.TAB){
22549                     e.stopEvent();
22550                     r = this.doc.selection.createRange();
22551                     if(r){
22552                         r.collapse(true);
22553                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22554                         this.deferFocus();
22555                     }
22556                     return;
22557                 }
22558                 
22559                 if(k == e.ENTER){
22560                     r = this.doc.selection.createRange();
22561                     if(r){
22562                         var target = r.parentElement();
22563                         if(!target || target.tagName.toLowerCase() != 'li'){
22564                             e.stopEvent();
22565                             r.pasteHTML('<br />');
22566                             r.collapse(false);
22567                             r.select();
22568                         }
22569                     }
22570                 }
22571                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22572                     this.cleanUpPaste.defer(100, this);
22573                     return;
22574                 }
22575                 
22576                 
22577             };
22578         }else if(Roo.isOpera){
22579             return function(e){
22580                 var k = e.getKey();
22581                 if(k == e.TAB){
22582                     e.stopEvent();
22583                     this.win.focus();
22584                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22585                     this.deferFocus();
22586                 }
22587                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22588                     this.cleanUpPaste.defer(100, this);
22589                     return;
22590                 }
22591                 
22592             };
22593         }else if(Roo.isSafari){
22594             return function(e){
22595                 var k = e.getKey();
22596                 
22597                 if(k == e.TAB){
22598                     e.stopEvent();
22599                     this.execCmd('InsertText','\t');
22600                     this.deferFocus();
22601                     return;
22602                 }
22603                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22604                     this.cleanUpPaste.defer(100, this);
22605                     return;
22606                 }
22607                 
22608              };
22609         }
22610     }(),
22611     
22612     getAllAncestors: function()
22613     {
22614         var p = this.getSelectedNode();
22615         var a = [];
22616         if (!p) {
22617             a.push(p); // push blank onto stack..
22618             p = this.getParentElement();
22619         }
22620         
22621         
22622         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22623             a.push(p);
22624             p = p.parentNode;
22625         }
22626         a.push(this.doc.body);
22627         return a;
22628     },
22629     lastSel : false,
22630     lastSelNode : false,
22631     
22632     
22633     getSelection : function() 
22634     {
22635         this.assignDocWin();
22636         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22637     },
22638     
22639     getSelectedNode: function() 
22640     {
22641         // this may only work on Gecko!!!
22642         
22643         // should we cache this!!!!
22644         
22645         
22646         
22647          
22648         var range = this.createRange(this.getSelection()).cloneRange();
22649         
22650         if (Roo.isIE) {
22651             var parent = range.parentElement();
22652             while (true) {
22653                 var testRange = range.duplicate();
22654                 testRange.moveToElementText(parent);
22655                 if (testRange.inRange(range)) {
22656                     break;
22657                 }
22658                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22659                     break;
22660                 }
22661                 parent = parent.parentElement;
22662             }
22663             return parent;
22664         }
22665         
22666         // is ancestor a text element.
22667         var ac =  range.commonAncestorContainer;
22668         if (ac.nodeType == 3) {
22669             ac = ac.parentNode;
22670         }
22671         
22672         var ar = ac.childNodes;
22673          
22674         var nodes = [];
22675         var other_nodes = [];
22676         var has_other_nodes = false;
22677         for (var i=0;i<ar.length;i++) {
22678             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22679                 continue;
22680             }
22681             // fullly contained node.
22682             
22683             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22684                 nodes.push(ar[i]);
22685                 continue;
22686             }
22687             
22688             // probably selected..
22689             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22690                 other_nodes.push(ar[i]);
22691                 continue;
22692             }
22693             // outer..
22694             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22695                 continue;
22696             }
22697             
22698             
22699             has_other_nodes = true;
22700         }
22701         if (!nodes.length && other_nodes.length) {
22702             nodes= other_nodes;
22703         }
22704         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22705             return false;
22706         }
22707         
22708         return nodes[0];
22709     },
22710     createRange: function(sel)
22711     {
22712         // this has strange effects when using with 
22713         // top toolbar - not sure if it's a great idea.
22714         //this.editor.contentWindow.focus();
22715         if (typeof sel != "undefined") {
22716             try {
22717                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22718             } catch(e) {
22719                 return this.doc.createRange();
22720             }
22721         } else {
22722             return this.doc.createRange();
22723         }
22724     },
22725     getParentElement: function()
22726     {
22727         
22728         this.assignDocWin();
22729         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22730         
22731         var range = this.createRange(sel);
22732          
22733         try {
22734             var p = range.commonAncestorContainer;
22735             while (p.nodeType == 3) { // text node
22736                 p = p.parentNode;
22737             }
22738             return p;
22739         } catch (e) {
22740             return null;
22741         }
22742     
22743     },
22744     /***
22745      *
22746      * Range intersection.. the hard stuff...
22747      *  '-1' = before
22748      *  '0' = hits..
22749      *  '1' = after.
22750      *         [ -- selected range --- ]
22751      *   [fail]                        [fail]
22752      *
22753      *    basically..
22754      *      if end is before start or  hits it. fail.
22755      *      if start is after end or hits it fail.
22756      *
22757      *   if either hits (but other is outside. - then it's not 
22758      *   
22759      *    
22760      **/
22761     
22762     
22763     // @see http://www.thismuchiknow.co.uk/?p=64.
22764     rangeIntersectsNode : function(range, node)
22765     {
22766         var nodeRange = node.ownerDocument.createRange();
22767         try {
22768             nodeRange.selectNode(node);
22769         } catch (e) {
22770             nodeRange.selectNodeContents(node);
22771         }
22772     
22773         var rangeStartRange = range.cloneRange();
22774         rangeStartRange.collapse(true);
22775     
22776         var rangeEndRange = range.cloneRange();
22777         rangeEndRange.collapse(false);
22778     
22779         var nodeStartRange = nodeRange.cloneRange();
22780         nodeStartRange.collapse(true);
22781     
22782         var nodeEndRange = nodeRange.cloneRange();
22783         nodeEndRange.collapse(false);
22784     
22785         return rangeStartRange.compareBoundaryPoints(
22786                  Range.START_TO_START, nodeEndRange) == -1 &&
22787                rangeEndRange.compareBoundaryPoints(
22788                  Range.START_TO_START, nodeStartRange) == 1;
22789         
22790          
22791     },
22792     rangeCompareNode : function(range, node)
22793     {
22794         var nodeRange = node.ownerDocument.createRange();
22795         try {
22796             nodeRange.selectNode(node);
22797         } catch (e) {
22798             nodeRange.selectNodeContents(node);
22799         }
22800         
22801         
22802         range.collapse(true);
22803     
22804         nodeRange.collapse(true);
22805      
22806         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22807         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22808          
22809         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22810         
22811         var nodeIsBefore   =  ss == 1;
22812         var nodeIsAfter    = ee == -1;
22813         
22814         if (nodeIsBefore && nodeIsAfter) {
22815             return 0; // outer
22816         }
22817         if (!nodeIsBefore && nodeIsAfter) {
22818             return 1; //right trailed.
22819         }
22820         
22821         if (nodeIsBefore && !nodeIsAfter) {
22822             return 2;  // left trailed.
22823         }
22824         // fully contined.
22825         return 3;
22826     },
22827
22828     // private? - in a new class?
22829     cleanUpPaste :  function()
22830     {
22831         // cleans up the whole document..
22832         Roo.log('cleanuppaste');
22833         
22834         this.cleanUpChildren(this.doc.body);
22835         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22836         if (clean != this.doc.body.innerHTML) {
22837             this.doc.body.innerHTML = clean;
22838         }
22839         
22840     },
22841     
22842     cleanWordChars : function(input) {// change the chars to hex code
22843         var he = Roo.HtmlEditorCore;
22844         
22845         var output = input;
22846         Roo.each(he.swapCodes, function(sw) { 
22847             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22848             
22849             output = output.replace(swapper, sw[1]);
22850         });
22851         
22852         return output;
22853     },
22854     
22855     
22856     cleanUpChildren : function (n)
22857     {
22858         if (!n.childNodes.length) {
22859             return;
22860         }
22861         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22862            this.cleanUpChild(n.childNodes[i]);
22863         }
22864     },
22865     
22866     
22867         
22868     
22869     cleanUpChild : function (node)
22870     {
22871         var ed = this;
22872         //console.log(node);
22873         if (node.nodeName == "#text") {
22874             // clean up silly Windows -- stuff?
22875             return; 
22876         }
22877         if (node.nodeName == "#comment") {
22878             node.parentNode.removeChild(node);
22879             // clean up silly Windows -- stuff?
22880             return; 
22881         }
22882         var lcname = node.tagName.toLowerCase();
22883         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22884         // whitelist of tags..
22885         
22886         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22887             // remove node.
22888             node.parentNode.removeChild(node);
22889             return;
22890             
22891         }
22892         
22893         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22894         
22895         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22896         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22897         
22898         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22899         //    remove_keep_children = true;
22900         //}
22901         
22902         if (remove_keep_children) {
22903             this.cleanUpChildren(node);
22904             // inserts everything just before this node...
22905             while (node.childNodes.length) {
22906                 var cn = node.childNodes[0];
22907                 node.removeChild(cn);
22908                 node.parentNode.insertBefore(cn, node);
22909             }
22910             node.parentNode.removeChild(node);
22911             return;
22912         }
22913         
22914         if (!node.attributes || !node.attributes.length) {
22915             this.cleanUpChildren(node);
22916             return;
22917         }
22918         
22919         function cleanAttr(n,v)
22920         {
22921             
22922             if (v.match(/^\./) || v.match(/^\//)) {
22923                 return;
22924             }
22925             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22926                 return;
22927             }
22928             if (v.match(/^#/)) {
22929                 return;
22930             }
22931 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22932             node.removeAttribute(n);
22933             
22934         }
22935         
22936         var cwhite = this.cwhite;
22937         var cblack = this.cblack;
22938             
22939         function cleanStyle(n,v)
22940         {
22941             if (v.match(/expression/)) { //XSS?? should we even bother..
22942                 node.removeAttribute(n);
22943                 return;
22944             }
22945             
22946             var parts = v.split(/;/);
22947             var clean = [];
22948             
22949             Roo.each(parts, function(p) {
22950                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22951                 if (!p.length) {
22952                     return true;
22953                 }
22954                 var l = p.split(':').shift().replace(/\s+/g,'');
22955                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22956                 
22957                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22958 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22959                     //node.removeAttribute(n);
22960                     return true;
22961                 }
22962                 //Roo.log()
22963                 // only allow 'c whitelisted system attributes'
22964                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22965 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22966                     //node.removeAttribute(n);
22967                     return true;
22968                 }
22969                 
22970                 
22971                  
22972                 
22973                 clean.push(p);
22974                 return true;
22975             });
22976             if (clean.length) { 
22977                 node.setAttribute(n, clean.join(';'));
22978             } else {
22979                 node.removeAttribute(n);
22980             }
22981             
22982         }
22983         
22984         
22985         for (var i = node.attributes.length-1; i > -1 ; i--) {
22986             var a = node.attributes[i];
22987             //console.log(a);
22988             
22989             if (a.name.toLowerCase().substr(0,2)=='on')  {
22990                 node.removeAttribute(a.name);
22991                 continue;
22992             }
22993             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22994                 node.removeAttribute(a.name);
22995                 continue;
22996             }
22997             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22998                 cleanAttr(a.name,a.value); // fixme..
22999                 continue;
23000             }
23001             if (a.name == 'style') {
23002                 cleanStyle(a.name,a.value);
23003                 continue;
23004             }
23005             /// clean up MS crap..
23006             // tecnically this should be a list of valid class'es..
23007             
23008             
23009             if (a.name == 'class') {
23010                 if (a.value.match(/^Mso/)) {
23011                     node.className = '';
23012                 }
23013                 
23014                 if (a.value.match(/^body$/)) {
23015                     node.className = '';
23016                 }
23017                 continue;
23018             }
23019             
23020             // style cleanup!?
23021             // class cleanup?
23022             
23023         }
23024         
23025         
23026         this.cleanUpChildren(node);
23027         
23028         
23029     },
23030     
23031     /**
23032      * Clean up MS wordisms...
23033      */
23034     cleanWord : function(node)
23035     {
23036         
23037         
23038         if (!node) {
23039             this.cleanWord(this.doc.body);
23040             return;
23041         }
23042         if (node.nodeName == "#text") {
23043             // clean up silly Windows -- stuff?
23044             return; 
23045         }
23046         if (node.nodeName == "#comment") {
23047             node.parentNode.removeChild(node);
23048             // clean up silly Windows -- stuff?
23049             return; 
23050         }
23051         
23052         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23053             node.parentNode.removeChild(node);
23054             return;
23055         }
23056         
23057         // remove - but keep children..
23058         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23059             while (node.childNodes.length) {
23060                 var cn = node.childNodes[0];
23061                 node.removeChild(cn);
23062                 node.parentNode.insertBefore(cn, node);
23063             }
23064             node.parentNode.removeChild(node);
23065             this.iterateChildren(node, this.cleanWord);
23066             return;
23067         }
23068         // clean styles
23069         if (node.className.length) {
23070             
23071             var cn = node.className.split(/\W+/);
23072             var cna = [];
23073             Roo.each(cn, function(cls) {
23074                 if (cls.match(/Mso[a-zA-Z]+/)) {
23075                     return;
23076                 }
23077                 cna.push(cls);
23078             });
23079             node.className = cna.length ? cna.join(' ') : '';
23080             if (!cna.length) {
23081                 node.removeAttribute("class");
23082             }
23083         }
23084         
23085         if (node.hasAttribute("lang")) {
23086             node.removeAttribute("lang");
23087         }
23088         
23089         if (node.hasAttribute("style")) {
23090             
23091             var styles = node.getAttribute("style").split(";");
23092             var nstyle = [];
23093             Roo.each(styles, function(s) {
23094                 if (!s.match(/:/)) {
23095                     return;
23096                 }
23097                 var kv = s.split(":");
23098                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23099                     return;
23100                 }
23101                 // what ever is left... we allow.
23102                 nstyle.push(s);
23103             });
23104             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23105             if (!nstyle.length) {
23106                 node.removeAttribute('style');
23107             }
23108         }
23109         this.iterateChildren(node, this.cleanWord);
23110         
23111         
23112         
23113     },
23114     /**
23115      * iterateChildren of a Node, calling fn each time, using this as the scole..
23116      * @param {DomNode} node node to iterate children of.
23117      * @param {Function} fn method of this class to call on each item.
23118      */
23119     iterateChildren : function(node, fn)
23120     {
23121         if (!node.childNodes.length) {
23122                 return;
23123         }
23124         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23125            fn.call(this, node.childNodes[i])
23126         }
23127     },
23128     
23129     
23130     /**
23131      * cleanTableWidths.
23132      *
23133      * Quite often pasting from word etc.. results in tables with column and widths.
23134      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23135      *
23136      */
23137     cleanTableWidths : function(node)
23138     {
23139          
23140          
23141         if (!node) {
23142             this.cleanTableWidths(this.doc.body);
23143             return;
23144         }
23145         
23146         // ignore list...
23147         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23148             return; 
23149         }
23150         Roo.log(node.tagName);
23151         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23152             this.iterateChildren(node, this.cleanTableWidths);
23153             return;
23154         }
23155         if (node.hasAttribute('width')) {
23156             node.removeAttribute('width');
23157         }
23158         
23159          
23160         if (node.hasAttribute("style")) {
23161             // pretty basic...
23162             
23163             var styles = node.getAttribute("style").split(";");
23164             var nstyle = [];
23165             Roo.each(styles, function(s) {
23166                 if (!s.match(/:/)) {
23167                     return;
23168                 }
23169                 var kv = s.split(":");
23170                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23171                     return;
23172                 }
23173                 // what ever is left... we allow.
23174                 nstyle.push(s);
23175             });
23176             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23177             if (!nstyle.length) {
23178                 node.removeAttribute('style');
23179             }
23180         }
23181         
23182         this.iterateChildren(node, this.cleanTableWidths);
23183         
23184         
23185     },
23186     
23187     
23188     
23189     
23190     domToHTML : function(currentElement, depth, nopadtext) {
23191         
23192         depth = depth || 0;
23193         nopadtext = nopadtext || false;
23194     
23195         if (!currentElement) {
23196             return this.domToHTML(this.doc.body);
23197         }
23198         
23199         //Roo.log(currentElement);
23200         var j;
23201         var allText = false;
23202         var nodeName = currentElement.nodeName;
23203         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23204         
23205         if  (nodeName == '#text') {
23206             
23207             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23208         }
23209         
23210         
23211         var ret = '';
23212         if (nodeName != 'BODY') {
23213              
23214             var i = 0;
23215             // Prints the node tagName, such as <A>, <IMG>, etc
23216             if (tagName) {
23217                 var attr = [];
23218                 for(i = 0; i < currentElement.attributes.length;i++) {
23219                     // quoting?
23220                     var aname = currentElement.attributes.item(i).name;
23221                     if (!currentElement.attributes.item(i).value.length) {
23222                         continue;
23223                     }
23224                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23225                 }
23226                 
23227                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23228             } 
23229             else {
23230                 
23231                 // eack
23232             }
23233         } else {
23234             tagName = false;
23235         }
23236         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23237             return ret;
23238         }
23239         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23240             nopadtext = true;
23241         }
23242         
23243         
23244         // Traverse the tree
23245         i = 0;
23246         var currentElementChild = currentElement.childNodes.item(i);
23247         var allText = true;
23248         var innerHTML  = '';
23249         lastnode = '';
23250         while (currentElementChild) {
23251             // Formatting code (indent the tree so it looks nice on the screen)
23252             var nopad = nopadtext;
23253             if (lastnode == 'SPAN') {
23254                 nopad  = true;
23255             }
23256             // text
23257             if  (currentElementChild.nodeName == '#text') {
23258                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23259                 toadd = nopadtext ? toadd : toadd.trim();
23260                 if (!nopad && toadd.length > 80) {
23261                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23262                 }
23263                 innerHTML  += toadd;
23264                 
23265                 i++;
23266                 currentElementChild = currentElement.childNodes.item(i);
23267                 lastNode = '';
23268                 continue;
23269             }
23270             allText = false;
23271             
23272             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23273                 
23274             // Recursively traverse the tree structure of the child node
23275             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23276             lastnode = currentElementChild.nodeName;
23277             i++;
23278             currentElementChild=currentElement.childNodes.item(i);
23279         }
23280         
23281         ret += innerHTML;
23282         
23283         if (!allText) {
23284                 // The remaining code is mostly for formatting the tree
23285             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23286         }
23287         
23288         
23289         if (tagName) {
23290             ret+= "</"+tagName+">";
23291         }
23292         return ret;
23293         
23294     },
23295         
23296     applyBlacklists : function()
23297     {
23298         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23299         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23300         
23301         this.white = [];
23302         this.black = [];
23303         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23304             if (b.indexOf(tag) > -1) {
23305                 return;
23306             }
23307             this.white.push(tag);
23308             
23309         }, this);
23310         
23311         Roo.each(w, function(tag) {
23312             if (b.indexOf(tag) > -1) {
23313                 return;
23314             }
23315             if (this.white.indexOf(tag) > -1) {
23316                 return;
23317             }
23318             this.white.push(tag);
23319             
23320         }, this);
23321         
23322         
23323         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23324             if (w.indexOf(tag) > -1) {
23325                 return;
23326             }
23327             this.black.push(tag);
23328             
23329         }, this);
23330         
23331         Roo.each(b, function(tag) {
23332             if (w.indexOf(tag) > -1) {
23333                 return;
23334             }
23335             if (this.black.indexOf(tag) > -1) {
23336                 return;
23337             }
23338             this.black.push(tag);
23339             
23340         }, this);
23341         
23342         
23343         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23344         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23345         
23346         this.cwhite = [];
23347         this.cblack = [];
23348         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23349             if (b.indexOf(tag) > -1) {
23350                 return;
23351             }
23352             this.cwhite.push(tag);
23353             
23354         }, this);
23355         
23356         Roo.each(w, function(tag) {
23357             if (b.indexOf(tag) > -1) {
23358                 return;
23359             }
23360             if (this.cwhite.indexOf(tag) > -1) {
23361                 return;
23362             }
23363             this.cwhite.push(tag);
23364             
23365         }, this);
23366         
23367         
23368         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23369             if (w.indexOf(tag) > -1) {
23370                 return;
23371             }
23372             this.cblack.push(tag);
23373             
23374         }, this);
23375         
23376         Roo.each(b, function(tag) {
23377             if (w.indexOf(tag) > -1) {
23378                 return;
23379             }
23380             if (this.cblack.indexOf(tag) > -1) {
23381                 return;
23382             }
23383             this.cblack.push(tag);
23384             
23385         }, this);
23386     },
23387     
23388     setStylesheets : function(stylesheets)
23389     {
23390         if(typeof(stylesheets) == 'string'){
23391             Roo.get(this.iframe.contentDocument.head).createChild({
23392                 tag : 'link',
23393                 rel : 'stylesheet',
23394                 type : 'text/css',
23395                 href : stylesheets
23396             });
23397             
23398             return;
23399         }
23400         var _this = this;
23401      
23402         Roo.each(stylesheets, function(s) {
23403             if(!s.length){
23404                 return;
23405             }
23406             
23407             Roo.get(_this.iframe.contentDocument.head).createChild({
23408                 tag : 'link',
23409                 rel : 'stylesheet',
23410                 type : 'text/css',
23411                 href : s
23412             });
23413         });
23414
23415         
23416     },
23417     
23418     removeStylesheets : function()
23419     {
23420         var _this = this;
23421         
23422         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23423             s.remove();
23424         });
23425     },
23426     
23427     setStyle : function(style)
23428     {
23429         Roo.get(this.iframe.contentDocument.head).createChild({
23430             tag : 'style',
23431             type : 'text/css',
23432             html : style
23433         });
23434
23435         return;
23436     }
23437     
23438     // hide stuff that is not compatible
23439     /**
23440      * @event blur
23441      * @hide
23442      */
23443     /**
23444      * @event change
23445      * @hide
23446      */
23447     /**
23448      * @event focus
23449      * @hide
23450      */
23451     /**
23452      * @event specialkey
23453      * @hide
23454      */
23455     /**
23456      * @cfg {String} fieldClass @hide
23457      */
23458     /**
23459      * @cfg {String} focusClass @hide
23460      */
23461     /**
23462      * @cfg {String} autoCreate @hide
23463      */
23464     /**
23465      * @cfg {String} inputType @hide
23466      */
23467     /**
23468      * @cfg {String} invalidClass @hide
23469      */
23470     /**
23471      * @cfg {String} invalidText @hide
23472      */
23473     /**
23474      * @cfg {String} msgFx @hide
23475      */
23476     /**
23477      * @cfg {String} validateOnBlur @hide
23478      */
23479 });
23480
23481 Roo.HtmlEditorCore.white = [
23482         'area', 'br', 'img', 'input', 'hr', 'wbr',
23483         
23484        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23485        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23486        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23487        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23488        'table',   'ul',         'xmp', 
23489        
23490        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23491       'thead',   'tr', 
23492      
23493       'dir', 'menu', 'ol', 'ul', 'dl',
23494        
23495       'embed',  'object'
23496 ];
23497
23498
23499 Roo.HtmlEditorCore.black = [
23500     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23501         'applet', // 
23502         'base',   'basefont', 'bgsound', 'blink',  'body', 
23503         'frame',  'frameset', 'head',    'html',   'ilayer', 
23504         'iframe', 'layer',  'link',     'meta',    'object',   
23505         'script', 'style' ,'title',  'xml' // clean later..
23506 ];
23507 Roo.HtmlEditorCore.clean = [
23508     'script', 'style', 'title', 'xml'
23509 ];
23510 Roo.HtmlEditorCore.remove = [
23511     'font'
23512 ];
23513 // attributes..
23514
23515 Roo.HtmlEditorCore.ablack = [
23516     'on'
23517 ];
23518     
23519 Roo.HtmlEditorCore.aclean = [ 
23520     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23521 ];
23522
23523 // protocols..
23524 Roo.HtmlEditorCore.pwhite= [
23525         'http',  'https',  'mailto'
23526 ];
23527
23528 // white listed style attributes.
23529 Roo.HtmlEditorCore.cwhite= [
23530       //  'text-align', /// default is to allow most things..
23531       
23532          
23533 //        'font-size'//??
23534 ];
23535
23536 // black listed style attributes.
23537 Roo.HtmlEditorCore.cblack= [
23538       //  'font-size' -- this can be set by the project 
23539 ];
23540
23541
23542 Roo.HtmlEditorCore.swapCodes   =[ 
23543     [    8211, "--" ], 
23544     [    8212, "--" ], 
23545     [    8216,  "'" ],  
23546     [    8217, "'" ],  
23547     [    8220, '"' ],  
23548     [    8221, '"' ],  
23549     [    8226, "*" ],  
23550     [    8230, "..." ]
23551 ]; 
23552
23553     /*
23554  * - LGPL
23555  *
23556  * HtmlEditor
23557  * 
23558  */
23559
23560 /**
23561  * @class Roo.bootstrap.HtmlEditor
23562  * @extends Roo.bootstrap.TextArea
23563  * Bootstrap HtmlEditor class
23564
23565  * @constructor
23566  * Create a new HtmlEditor
23567  * @param {Object} config The config object
23568  */
23569
23570 Roo.bootstrap.HtmlEditor = function(config){
23571     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23572     if (!this.toolbars) {
23573         this.toolbars = [];
23574     }
23575     
23576     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23577     this.addEvents({
23578             /**
23579              * @event initialize
23580              * Fires when the editor is fully initialized (including the iframe)
23581              * @param {HtmlEditor} this
23582              */
23583             initialize: true,
23584             /**
23585              * @event activate
23586              * Fires when the editor is first receives the focus. Any insertion must wait
23587              * until after this event.
23588              * @param {HtmlEditor} this
23589              */
23590             activate: true,
23591              /**
23592              * @event beforesync
23593              * Fires before the textarea is updated with content from the editor iframe. Return false
23594              * to cancel the sync.
23595              * @param {HtmlEditor} this
23596              * @param {String} html
23597              */
23598             beforesync: true,
23599              /**
23600              * @event beforepush
23601              * Fires before the iframe editor is updated with content from the textarea. Return false
23602              * to cancel the push.
23603              * @param {HtmlEditor} this
23604              * @param {String} html
23605              */
23606             beforepush: true,
23607              /**
23608              * @event sync
23609              * Fires when the textarea is updated with content from the editor iframe.
23610              * @param {HtmlEditor} this
23611              * @param {String} html
23612              */
23613             sync: true,
23614              /**
23615              * @event push
23616              * Fires when the iframe editor is updated with content from the textarea.
23617              * @param {HtmlEditor} this
23618              * @param {String} html
23619              */
23620             push: true,
23621              /**
23622              * @event editmodechange
23623              * Fires when the editor switches edit modes
23624              * @param {HtmlEditor} this
23625              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23626              */
23627             editmodechange: true,
23628             /**
23629              * @event editorevent
23630              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23631              * @param {HtmlEditor} this
23632              */
23633             editorevent: true,
23634             /**
23635              * @event firstfocus
23636              * Fires when on first focus - needed by toolbars..
23637              * @param {HtmlEditor} this
23638              */
23639             firstfocus: true,
23640             /**
23641              * @event autosave
23642              * Auto save the htmlEditor value as a file into Events
23643              * @param {HtmlEditor} this
23644              */
23645             autosave: true,
23646             /**
23647              * @event savedpreview
23648              * preview the saved version of htmlEditor
23649              * @param {HtmlEditor} this
23650              */
23651             savedpreview: true
23652         });
23653 };
23654
23655
23656 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23657     
23658     
23659       /**
23660      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23661      */
23662     toolbars : false,
23663     
23664      /**
23665     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23666     */
23667     btns : [],
23668    
23669      /**
23670      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23671      *                        Roo.resizable.
23672      */
23673     resizable : false,
23674      /**
23675      * @cfg {Number} height (in pixels)
23676      */   
23677     height: 300,
23678    /**
23679      * @cfg {Number} width (in pixels)
23680      */   
23681     width: false,
23682     
23683     /**
23684      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23685      * 
23686      */
23687     stylesheets: false,
23688     
23689     // id of frame..
23690     frameId: false,
23691     
23692     // private properties
23693     validationEvent : false,
23694     deferHeight: true,
23695     initialized : false,
23696     activated : false,
23697     
23698     onFocus : Roo.emptyFn,
23699     iframePad:3,
23700     hideMode:'offsets',
23701     
23702     tbContainer : false,
23703     
23704     bodyCls : '',
23705     
23706     toolbarContainer :function() {
23707         return this.wrap.select('.x-html-editor-tb',true).first();
23708     },
23709
23710     /**
23711      * Protected method that will not generally be called directly. It
23712      * is called when the editor creates its toolbar. Override this method if you need to
23713      * add custom toolbar buttons.
23714      * @param {HtmlEditor} editor
23715      */
23716     createToolbar : function(){
23717         Roo.log('renewing');
23718         Roo.log("create toolbars");
23719         
23720         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23721         this.toolbars[0].render(this.toolbarContainer());
23722         
23723         return;
23724         
23725 //        if (!editor.toolbars || !editor.toolbars.length) {
23726 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23727 //        }
23728 //        
23729 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23730 //            editor.toolbars[i] = Roo.factory(
23731 //                    typeof(editor.toolbars[i]) == 'string' ?
23732 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23733 //                Roo.bootstrap.HtmlEditor);
23734 //            editor.toolbars[i].init(editor);
23735 //        }
23736     },
23737
23738      
23739     // private
23740     onRender : function(ct, position)
23741     {
23742        // Roo.log("Call onRender: " + this.xtype);
23743         var _t = this;
23744         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23745       
23746         this.wrap = this.inputEl().wrap({
23747             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23748         });
23749         
23750         this.editorcore.onRender(ct, position);
23751          
23752         if (this.resizable) {
23753             this.resizeEl = new Roo.Resizable(this.wrap, {
23754                 pinned : true,
23755                 wrap: true,
23756                 dynamic : true,
23757                 minHeight : this.height,
23758                 height: this.height,
23759                 handles : this.resizable,
23760                 width: this.width,
23761                 listeners : {
23762                     resize : function(r, w, h) {
23763                         _t.onResize(w,h); // -something
23764                     }
23765                 }
23766             });
23767             
23768         }
23769         this.createToolbar(this);
23770        
23771         
23772         if(!this.width && this.resizable){
23773             this.setSize(this.wrap.getSize());
23774         }
23775         if (this.resizeEl) {
23776             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23777             // should trigger onReize..
23778         }
23779         
23780     },
23781
23782     // private
23783     onResize : function(w, h)
23784     {
23785         Roo.log('resize: ' +w + ',' + h );
23786         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23787         var ew = false;
23788         var eh = false;
23789         
23790         if(this.inputEl() ){
23791             if(typeof w == 'number'){
23792                 var aw = w - this.wrap.getFrameWidth('lr');
23793                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23794                 ew = aw;
23795             }
23796             if(typeof h == 'number'){
23797                  var tbh = -11;  // fixme it needs to tool bar size!
23798                 for (var i =0; i < this.toolbars.length;i++) {
23799                     // fixme - ask toolbars for heights?
23800                     tbh += this.toolbars[i].el.getHeight();
23801                     //if (this.toolbars[i].footer) {
23802                     //    tbh += this.toolbars[i].footer.el.getHeight();
23803                     //}
23804                 }
23805               
23806                 
23807                 
23808                 
23809                 
23810                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23811                 ah -= 5; // knock a few pixes off for look..
23812                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23813                 var eh = ah;
23814             }
23815         }
23816         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23817         this.editorcore.onResize(ew,eh);
23818         
23819     },
23820
23821     /**
23822      * Toggles the editor between standard and source edit mode.
23823      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23824      */
23825     toggleSourceEdit : function(sourceEditMode)
23826     {
23827         this.editorcore.toggleSourceEdit(sourceEditMode);
23828         
23829         if(this.editorcore.sourceEditMode){
23830             Roo.log('editor - showing textarea');
23831             
23832 //            Roo.log('in');
23833 //            Roo.log(this.syncValue());
23834             this.syncValue();
23835             this.inputEl().removeClass(['hide', 'x-hidden']);
23836             this.inputEl().dom.removeAttribute('tabIndex');
23837             this.inputEl().focus();
23838         }else{
23839             Roo.log('editor - hiding textarea');
23840 //            Roo.log('out')
23841 //            Roo.log(this.pushValue()); 
23842             this.pushValue();
23843             
23844             this.inputEl().addClass(['hide', 'x-hidden']);
23845             this.inputEl().dom.setAttribute('tabIndex', -1);
23846             //this.deferFocus();
23847         }
23848          
23849         if(this.resizable){
23850             this.setSize(this.wrap.getSize());
23851         }
23852         
23853         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23854     },
23855  
23856     // private (for BoxComponent)
23857     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23858
23859     // private (for BoxComponent)
23860     getResizeEl : function(){
23861         return this.wrap;
23862     },
23863
23864     // private (for BoxComponent)
23865     getPositionEl : function(){
23866         return this.wrap;
23867     },
23868
23869     // private
23870     initEvents : function(){
23871         this.originalValue = this.getValue();
23872     },
23873
23874 //    /**
23875 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23876 //     * @method
23877 //     */
23878 //    markInvalid : Roo.emptyFn,
23879 //    /**
23880 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23881 //     * @method
23882 //     */
23883 //    clearInvalid : Roo.emptyFn,
23884
23885     setValue : function(v){
23886         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23887         this.editorcore.pushValue();
23888     },
23889
23890      
23891     // private
23892     deferFocus : function(){
23893         this.focus.defer(10, this);
23894     },
23895
23896     // doc'ed in Field
23897     focus : function(){
23898         this.editorcore.focus();
23899         
23900     },
23901       
23902
23903     // private
23904     onDestroy : function(){
23905         
23906         
23907         
23908         if(this.rendered){
23909             
23910             for (var i =0; i < this.toolbars.length;i++) {
23911                 // fixme - ask toolbars for heights?
23912                 this.toolbars[i].onDestroy();
23913             }
23914             
23915             this.wrap.dom.innerHTML = '';
23916             this.wrap.remove();
23917         }
23918     },
23919
23920     // private
23921     onFirstFocus : function(){
23922         //Roo.log("onFirstFocus");
23923         this.editorcore.onFirstFocus();
23924          for (var i =0; i < this.toolbars.length;i++) {
23925             this.toolbars[i].onFirstFocus();
23926         }
23927         
23928     },
23929     
23930     // private
23931     syncValue : function()
23932     {   
23933         this.editorcore.syncValue();
23934     },
23935     
23936     pushValue : function()
23937     {   
23938         this.editorcore.pushValue();
23939     }
23940      
23941     
23942     // hide stuff that is not compatible
23943     /**
23944      * @event blur
23945      * @hide
23946      */
23947     /**
23948      * @event change
23949      * @hide
23950      */
23951     /**
23952      * @event focus
23953      * @hide
23954      */
23955     /**
23956      * @event specialkey
23957      * @hide
23958      */
23959     /**
23960      * @cfg {String} fieldClass @hide
23961      */
23962     /**
23963      * @cfg {String} focusClass @hide
23964      */
23965     /**
23966      * @cfg {String} autoCreate @hide
23967      */
23968     /**
23969      * @cfg {String} inputType @hide
23970      */
23971      
23972     /**
23973      * @cfg {String} invalidText @hide
23974      */
23975     /**
23976      * @cfg {String} msgFx @hide
23977      */
23978     /**
23979      * @cfg {String} validateOnBlur @hide
23980      */
23981 });
23982  
23983     
23984    
23985    
23986    
23987       
23988 Roo.namespace('Roo.bootstrap.htmleditor');
23989 /**
23990  * @class Roo.bootstrap.HtmlEditorToolbar1
23991  * Basic Toolbar
23992  * 
23993  * Usage:
23994  *
23995  new Roo.bootstrap.HtmlEditor({
23996     ....
23997     toolbars : [
23998         new Roo.bootstrap.HtmlEditorToolbar1({
23999             disable : { fonts: 1 , format: 1, ..., ... , ...],
24000             btns : [ .... ]
24001         })
24002     }
24003      
24004  * 
24005  * @cfg {Object} disable List of elements to disable..
24006  * @cfg {Array} btns List of additional buttons.
24007  * 
24008  * 
24009  * NEEDS Extra CSS? 
24010  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24011  */
24012  
24013 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24014 {
24015     
24016     Roo.apply(this, config);
24017     
24018     // default disabled, based on 'good practice'..
24019     this.disable = this.disable || {};
24020     Roo.applyIf(this.disable, {
24021         fontSize : true,
24022         colors : true,
24023         specialElements : true
24024     });
24025     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24026     
24027     this.editor = config.editor;
24028     this.editorcore = config.editor.editorcore;
24029     
24030     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24031     
24032     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24033     // dont call parent... till later.
24034 }
24035 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24036      
24037     bar : true,
24038     
24039     editor : false,
24040     editorcore : false,
24041     
24042     
24043     formats : [
24044         "p" ,  
24045         "h1","h2","h3","h4","h5","h6", 
24046         "pre", "code", 
24047         "abbr", "acronym", "address", "cite", "samp", "var",
24048         'div','span'
24049     ],
24050     
24051     onRender : function(ct, position)
24052     {
24053        // Roo.log("Call onRender: " + this.xtype);
24054         
24055        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24056        Roo.log(this.el);
24057        this.el.dom.style.marginBottom = '0';
24058        var _this = this;
24059        var editorcore = this.editorcore;
24060        var editor= this.editor;
24061        
24062        var children = [];
24063        var btn = function(id,cmd , toggle, handler, html){
24064        
24065             var  event = toggle ? 'toggle' : 'click';
24066        
24067             var a = {
24068                 size : 'sm',
24069                 xtype: 'Button',
24070                 xns: Roo.bootstrap,
24071                 //glyphicon : id,
24072                 fa: id,
24073                 cmd : id || cmd,
24074                 enableToggle:toggle !== false,
24075                 html : html || '',
24076                 pressed : toggle ? false : null,
24077                 listeners : {}
24078             };
24079             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24080                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24081             };
24082             children.push(a);
24083             return a;
24084        }
24085        
24086     //    var cb_box = function...
24087         
24088         var style = {
24089                 xtype: 'Button',
24090                 size : 'sm',
24091                 xns: Roo.bootstrap,
24092                 fa : 'font',
24093                 //html : 'submit'
24094                 menu : {
24095                     xtype: 'Menu',
24096                     xns: Roo.bootstrap,
24097                     items:  []
24098                 }
24099         };
24100         Roo.each(this.formats, function(f) {
24101             style.menu.items.push({
24102                 xtype :'MenuItem',
24103                 xns: Roo.bootstrap,
24104                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24105                 tagname : f,
24106                 listeners : {
24107                     click : function()
24108                     {
24109                         editorcore.insertTag(this.tagname);
24110                         editor.focus();
24111                     }
24112                 }
24113                 
24114             });
24115         });
24116         children.push(style);   
24117         
24118         btn('bold',false,true);
24119         btn('italic',false,true);
24120         btn('align-left', 'justifyleft',true);
24121         btn('align-center', 'justifycenter',true);
24122         btn('align-right' , 'justifyright',true);
24123         btn('link', false, false, function(btn) {
24124             //Roo.log("create link?");
24125             var url = prompt(this.createLinkText, this.defaultLinkValue);
24126             if(url && url != 'http:/'+'/'){
24127                 this.editorcore.relayCmd('createlink', url);
24128             }
24129         }),
24130         btn('list','insertunorderedlist',true);
24131         btn('pencil', false,true, function(btn){
24132                 Roo.log(this);
24133                 this.toggleSourceEdit(btn.pressed);
24134         });
24135         
24136         if (this.editor.btns.length > 0) {
24137             for (var i = 0; i<this.editor.btns.length; i++) {
24138                 children.push(this.editor.btns[i]);
24139             }
24140         }
24141         
24142         /*
24143         var cog = {
24144                 xtype: 'Button',
24145                 size : 'sm',
24146                 xns: Roo.bootstrap,
24147                 glyphicon : 'cog',
24148                 //html : 'submit'
24149                 menu : {
24150                     xtype: 'Menu',
24151                     xns: Roo.bootstrap,
24152                     items:  []
24153                 }
24154         };
24155         
24156         cog.menu.items.push({
24157             xtype :'MenuItem',
24158             xns: Roo.bootstrap,
24159             html : Clean styles,
24160             tagname : f,
24161             listeners : {
24162                 click : function()
24163                 {
24164                     editorcore.insertTag(this.tagname);
24165                     editor.focus();
24166                 }
24167             }
24168             
24169         });
24170        */
24171         
24172          
24173        this.xtype = 'NavSimplebar';
24174         
24175         for(var i=0;i< children.length;i++) {
24176             
24177             this.buttons.add(this.addxtypeChild(children[i]));
24178             
24179         }
24180         
24181         editor.on('editorevent', this.updateToolbar, this);
24182     },
24183     onBtnClick : function(id)
24184     {
24185        this.editorcore.relayCmd(id);
24186        this.editorcore.focus();
24187     },
24188     
24189     /**
24190      * Protected method that will not generally be called directly. It triggers
24191      * a toolbar update by reading the markup state of the current selection in the editor.
24192      */
24193     updateToolbar: function(){
24194
24195         if(!this.editorcore.activated){
24196             this.editor.onFirstFocus(); // is this neeed?
24197             return;
24198         }
24199
24200         var btns = this.buttons; 
24201         var doc = this.editorcore.doc;
24202         btns.get('bold').setActive(doc.queryCommandState('bold'));
24203         btns.get('italic').setActive(doc.queryCommandState('italic'));
24204         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24205         
24206         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24207         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24208         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24209         
24210         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24211         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24212          /*
24213         
24214         var ans = this.editorcore.getAllAncestors();
24215         if (this.formatCombo) {
24216             
24217             
24218             var store = this.formatCombo.store;
24219             this.formatCombo.setValue("");
24220             for (var i =0; i < ans.length;i++) {
24221                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24222                     // select it..
24223                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24224                     break;
24225                 }
24226             }
24227         }
24228         
24229         
24230         
24231         // hides menus... - so this cant be on a menu...
24232         Roo.bootstrap.MenuMgr.hideAll();
24233         */
24234         Roo.bootstrap.MenuMgr.hideAll();
24235         //this.editorsyncValue();
24236     },
24237     onFirstFocus: function() {
24238         this.buttons.each(function(item){
24239            item.enable();
24240         });
24241     },
24242     toggleSourceEdit : function(sourceEditMode){
24243         
24244           
24245         if(sourceEditMode){
24246             Roo.log("disabling buttons");
24247            this.buttons.each( function(item){
24248                 if(item.cmd != 'pencil'){
24249                     item.disable();
24250                 }
24251             });
24252           
24253         }else{
24254             Roo.log("enabling buttons");
24255             if(this.editorcore.initialized){
24256                 this.buttons.each( function(item){
24257                     item.enable();
24258                 });
24259             }
24260             
24261         }
24262         Roo.log("calling toggole on editor");
24263         // tell the editor that it's been pressed..
24264         this.editor.toggleSourceEdit(sourceEditMode);
24265        
24266     }
24267 });
24268
24269
24270
24271
24272
24273 /**
24274  * @class Roo.bootstrap.Table.AbstractSelectionModel
24275  * @extends Roo.util.Observable
24276  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24277  * implemented by descendant classes.  This class should not be directly instantiated.
24278  * @constructor
24279  */
24280 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24281     this.locked = false;
24282     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24283 };
24284
24285
24286 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24287     /** @ignore Called by the grid automatically. Do not call directly. */
24288     init : function(grid){
24289         this.grid = grid;
24290         this.initEvents();
24291     },
24292
24293     /**
24294      * Locks the selections.
24295      */
24296     lock : function(){
24297         this.locked = true;
24298     },
24299
24300     /**
24301      * Unlocks the selections.
24302      */
24303     unlock : function(){
24304         this.locked = false;
24305     },
24306
24307     /**
24308      * Returns true if the selections are locked.
24309      * @return {Boolean}
24310      */
24311     isLocked : function(){
24312         return this.locked;
24313     }
24314 });
24315 /**
24316  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24317  * @class Roo.bootstrap.Table.RowSelectionModel
24318  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24319  * It supports multiple selections and keyboard selection/navigation. 
24320  * @constructor
24321  * @param {Object} config
24322  */
24323
24324 Roo.bootstrap.Table.RowSelectionModel = function(config){
24325     Roo.apply(this, config);
24326     this.selections = new Roo.util.MixedCollection(false, function(o){
24327         return o.id;
24328     });
24329
24330     this.last = false;
24331     this.lastActive = false;
24332
24333     this.addEvents({
24334         /**
24335              * @event selectionchange
24336              * Fires when the selection changes
24337              * @param {SelectionModel} this
24338              */
24339             "selectionchange" : true,
24340         /**
24341              * @event afterselectionchange
24342              * Fires after the selection changes (eg. by key press or clicking)
24343              * @param {SelectionModel} this
24344              */
24345             "afterselectionchange" : true,
24346         /**
24347              * @event beforerowselect
24348              * Fires when a row is selected being selected, return false to cancel.
24349              * @param {SelectionModel} this
24350              * @param {Number} rowIndex The selected index
24351              * @param {Boolean} keepExisting False if other selections will be cleared
24352              */
24353             "beforerowselect" : true,
24354         /**
24355              * @event rowselect
24356              * Fires when a row is selected.
24357              * @param {SelectionModel} this
24358              * @param {Number} rowIndex The selected index
24359              * @param {Roo.data.Record} r The record
24360              */
24361             "rowselect" : true,
24362         /**
24363              * @event rowdeselect
24364              * Fires when a row is deselected.
24365              * @param {SelectionModel} this
24366              * @param {Number} rowIndex The selected index
24367              */
24368         "rowdeselect" : true
24369     });
24370     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24371     this.locked = false;
24372  };
24373
24374 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24375     /**
24376      * @cfg {Boolean} singleSelect
24377      * True to allow selection of only one row at a time (defaults to false)
24378      */
24379     singleSelect : false,
24380
24381     // private
24382     initEvents : function()
24383     {
24384
24385         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24386         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24387         //}else{ // allow click to work like normal
24388          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24389         //}
24390         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24391         this.grid.on("rowclick", this.handleMouseDown, this);
24392         
24393         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24394             "up" : function(e){
24395                 if(!e.shiftKey){
24396                     this.selectPrevious(e.shiftKey);
24397                 }else if(this.last !== false && this.lastActive !== false){
24398                     var last = this.last;
24399                     this.selectRange(this.last,  this.lastActive-1);
24400                     this.grid.getView().focusRow(this.lastActive);
24401                     if(last !== false){
24402                         this.last = last;
24403                     }
24404                 }else{
24405                     this.selectFirstRow();
24406                 }
24407                 this.fireEvent("afterselectionchange", this);
24408             },
24409             "down" : function(e){
24410                 if(!e.shiftKey){
24411                     this.selectNext(e.shiftKey);
24412                 }else if(this.last !== false && this.lastActive !== false){
24413                     var last = this.last;
24414                     this.selectRange(this.last,  this.lastActive+1);
24415                     this.grid.getView().focusRow(this.lastActive);
24416                     if(last !== false){
24417                         this.last = last;
24418                     }
24419                 }else{
24420                     this.selectFirstRow();
24421                 }
24422                 this.fireEvent("afterselectionchange", this);
24423             },
24424             scope: this
24425         });
24426         this.grid.store.on('load', function(){
24427             this.selections.clear();
24428         },this);
24429         /*
24430         var view = this.grid.view;
24431         view.on("refresh", this.onRefresh, this);
24432         view.on("rowupdated", this.onRowUpdated, this);
24433         view.on("rowremoved", this.onRemove, this);
24434         */
24435     },
24436
24437     // private
24438     onRefresh : function()
24439     {
24440         var ds = this.grid.store, i, v = this.grid.view;
24441         var s = this.selections;
24442         s.each(function(r){
24443             if((i = ds.indexOfId(r.id)) != -1){
24444                 v.onRowSelect(i);
24445             }else{
24446                 s.remove(r);
24447             }
24448         });
24449     },
24450
24451     // private
24452     onRemove : function(v, index, r){
24453         this.selections.remove(r);
24454     },
24455
24456     // private
24457     onRowUpdated : function(v, index, r){
24458         if(this.isSelected(r)){
24459             v.onRowSelect(index);
24460         }
24461     },
24462
24463     /**
24464      * Select records.
24465      * @param {Array} records The records to select
24466      * @param {Boolean} keepExisting (optional) True to keep existing selections
24467      */
24468     selectRecords : function(records, keepExisting)
24469     {
24470         if(!keepExisting){
24471             this.clearSelections();
24472         }
24473             var ds = this.grid.store;
24474         for(var i = 0, len = records.length; i < len; i++){
24475             this.selectRow(ds.indexOf(records[i]), true);
24476         }
24477     },
24478
24479     /**
24480      * Gets the number of selected rows.
24481      * @return {Number}
24482      */
24483     getCount : function(){
24484         return this.selections.length;
24485     },
24486
24487     /**
24488      * Selects the first row in the grid.
24489      */
24490     selectFirstRow : function(){
24491         this.selectRow(0);
24492     },
24493
24494     /**
24495      * Select the last row.
24496      * @param {Boolean} keepExisting (optional) True to keep existing selections
24497      */
24498     selectLastRow : function(keepExisting){
24499         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24500         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24501     },
24502
24503     /**
24504      * Selects the row immediately following the last selected row.
24505      * @param {Boolean} keepExisting (optional) True to keep existing selections
24506      */
24507     selectNext : function(keepExisting)
24508     {
24509             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24510             this.selectRow(this.last+1, keepExisting);
24511             this.grid.getView().focusRow(this.last);
24512         }
24513     },
24514
24515     /**
24516      * Selects the row that precedes the last selected row.
24517      * @param {Boolean} keepExisting (optional) True to keep existing selections
24518      */
24519     selectPrevious : function(keepExisting){
24520         if(this.last){
24521             this.selectRow(this.last-1, keepExisting);
24522             this.grid.getView().focusRow(this.last);
24523         }
24524     },
24525
24526     /**
24527      * Returns the selected records
24528      * @return {Array} Array of selected records
24529      */
24530     getSelections : function(){
24531         return [].concat(this.selections.items);
24532     },
24533
24534     /**
24535      * Returns the first selected record.
24536      * @return {Record}
24537      */
24538     getSelected : function(){
24539         return this.selections.itemAt(0);
24540     },
24541
24542
24543     /**
24544      * Clears all selections.
24545      */
24546     clearSelections : function(fast)
24547     {
24548         if(this.locked) {
24549             return;
24550         }
24551         if(fast !== true){
24552                 var ds = this.grid.store;
24553             var s = this.selections;
24554             s.each(function(r){
24555                 this.deselectRow(ds.indexOfId(r.id));
24556             }, this);
24557             s.clear();
24558         }else{
24559             this.selections.clear();
24560         }
24561         this.last = false;
24562     },
24563
24564
24565     /**
24566      * Selects all rows.
24567      */
24568     selectAll : function(){
24569         if(this.locked) {
24570             return;
24571         }
24572         this.selections.clear();
24573         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24574             this.selectRow(i, true);
24575         }
24576     },
24577
24578     /**
24579      * Returns True if there is a selection.
24580      * @return {Boolean}
24581      */
24582     hasSelection : function(){
24583         return this.selections.length > 0;
24584     },
24585
24586     /**
24587      * Returns True if the specified row is selected.
24588      * @param {Number/Record} record The record or index of the record to check
24589      * @return {Boolean}
24590      */
24591     isSelected : function(index){
24592             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24593         return (r && this.selections.key(r.id) ? true : false);
24594     },
24595
24596     /**
24597      * Returns True if the specified record id is selected.
24598      * @param {String} id The id of record to check
24599      * @return {Boolean}
24600      */
24601     isIdSelected : function(id){
24602         return (this.selections.key(id) ? true : false);
24603     },
24604
24605
24606     // private
24607     handleMouseDBClick : function(e, t){
24608         
24609     },
24610     // private
24611     handleMouseDown : function(e, t)
24612     {
24613             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24614         if(this.isLocked() || rowIndex < 0 ){
24615             return;
24616         };
24617         if(e.shiftKey && this.last !== false){
24618             var last = this.last;
24619             this.selectRange(last, rowIndex, e.ctrlKey);
24620             this.last = last; // reset the last
24621             t.focus();
24622     
24623         }else{
24624             var isSelected = this.isSelected(rowIndex);
24625             //Roo.log("select row:" + rowIndex);
24626             if(isSelected){
24627                 this.deselectRow(rowIndex);
24628             } else {
24629                         this.selectRow(rowIndex, true);
24630             }
24631     
24632             /*
24633                 if(e.button !== 0 && isSelected){
24634                 alert('rowIndex 2: ' + rowIndex);
24635                     view.focusRow(rowIndex);
24636                 }else if(e.ctrlKey && isSelected){
24637                     this.deselectRow(rowIndex);
24638                 }else if(!isSelected){
24639                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24640                     view.focusRow(rowIndex);
24641                 }
24642             */
24643         }
24644         this.fireEvent("afterselectionchange", this);
24645     },
24646     // private
24647     handleDragableRowClick :  function(grid, rowIndex, e) 
24648     {
24649         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24650             this.selectRow(rowIndex, false);
24651             grid.view.focusRow(rowIndex);
24652              this.fireEvent("afterselectionchange", this);
24653         }
24654     },
24655     
24656     /**
24657      * Selects multiple rows.
24658      * @param {Array} rows Array of the indexes of the row to select
24659      * @param {Boolean} keepExisting (optional) True to keep existing selections
24660      */
24661     selectRows : function(rows, keepExisting){
24662         if(!keepExisting){
24663             this.clearSelections();
24664         }
24665         for(var i = 0, len = rows.length; i < len; i++){
24666             this.selectRow(rows[i], true);
24667         }
24668     },
24669
24670     /**
24671      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24672      * @param {Number} startRow The index of the first row in the range
24673      * @param {Number} endRow The index of the last row in the range
24674      * @param {Boolean} keepExisting (optional) True to retain existing selections
24675      */
24676     selectRange : function(startRow, endRow, keepExisting){
24677         if(this.locked) {
24678             return;
24679         }
24680         if(!keepExisting){
24681             this.clearSelections();
24682         }
24683         if(startRow <= endRow){
24684             for(var i = startRow; i <= endRow; i++){
24685                 this.selectRow(i, true);
24686             }
24687         }else{
24688             for(var i = startRow; i >= endRow; i--){
24689                 this.selectRow(i, true);
24690             }
24691         }
24692     },
24693
24694     /**
24695      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24696      * @param {Number} startRow The index of the first row in the range
24697      * @param {Number} endRow The index of the last row in the range
24698      */
24699     deselectRange : function(startRow, endRow, preventViewNotify){
24700         if(this.locked) {
24701             return;
24702         }
24703         for(var i = startRow; i <= endRow; i++){
24704             this.deselectRow(i, preventViewNotify);
24705         }
24706     },
24707
24708     /**
24709      * Selects a row.
24710      * @param {Number} row The index of the row to select
24711      * @param {Boolean} keepExisting (optional) True to keep existing selections
24712      */
24713     selectRow : function(index, keepExisting, preventViewNotify)
24714     {
24715             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24716             return;
24717         }
24718         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24719             if(!keepExisting || this.singleSelect){
24720                 this.clearSelections();
24721             }
24722             
24723             var r = this.grid.store.getAt(index);
24724             //console.log('selectRow - record id :' + r.id);
24725             
24726             this.selections.add(r);
24727             this.last = this.lastActive = index;
24728             if(!preventViewNotify){
24729                 var proxy = new Roo.Element(
24730                                 this.grid.getRowDom(index)
24731                 );
24732                 proxy.addClass('bg-info info');
24733             }
24734             this.fireEvent("rowselect", this, index, r);
24735             this.fireEvent("selectionchange", this);
24736         }
24737     },
24738
24739     /**
24740      * Deselects a row.
24741      * @param {Number} row The index of the row to deselect
24742      */
24743     deselectRow : function(index, preventViewNotify)
24744     {
24745         if(this.locked) {
24746             return;
24747         }
24748         if(this.last == index){
24749             this.last = false;
24750         }
24751         if(this.lastActive == index){
24752             this.lastActive = false;
24753         }
24754         
24755         var r = this.grid.store.getAt(index);
24756         if (!r) {
24757             return;
24758         }
24759         
24760         this.selections.remove(r);
24761         //.console.log('deselectRow - record id :' + r.id);
24762         if(!preventViewNotify){
24763         
24764             var proxy = new Roo.Element(
24765                 this.grid.getRowDom(index)
24766             );
24767             proxy.removeClass('bg-info info');
24768         }
24769         this.fireEvent("rowdeselect", this, index);
24770         this.fireEvent("selectionchange", this);
24771     },
24772
24773     // private
24774     restoreLast : function(){
24775         if(this._last){
24776             this.last = this._last;
24777         }
24778     },
24779
24780     // private
24781     acceptsNav : function(row, col, cm){
24782         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24783     },
24784
24785     // private
24786     onEditorKey : function(field, e){
24787         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24788         if(k == e.TAB){
24789             e.stopEvent();
24790             ed.completeEdit();
24791             if(e.shiftKey){
24792                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24793             }else{
24794                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24795             }
24796         }else if(k == e.ENTER && !e.ctrlKey){
24797             e.stopEvent();
24798             ed.completeEdit();
24799             if(e.shiftKey){
24800                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24801             }else{
24802                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24803             }
24804         }else if(k == e.ESC){
24805             ed.cancelEdit();
24806         }
24807         if(newCell){
24808             g.startEditing(newCell[0], newCell[1]);
24809         }
24810     }
24811 });
24812 /*
24813  * Based on:
24814  * Ext JS Library 1.1.1
24815  * Copyright(c) 2006-2007, Ext JS, LLC.
24816  *
24817  * Originally Released Under LGPL - original licence link has changed is not relivant.
24818  *
24819  * Fork - LGPL
24820  * <script type="text/javascript">
24821  */
24822  
24823 /**
24824  * @class Roo.bootstrap.PagingToolbar
24825  * @extends Roo.bootstrap.NavSimplebar
24826  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24827  * @constructor
24828  * Create a new PagingToolbar
24829  * @param {Object} config The config object
24830  * @param {Roo.data.Store} store
24831  */
24832 Roo.bootstrap.PagingToolbar = function(config)
24833 {
24834     // old args format still supported... - xtype is prefered..
24835         // created from xtype...
24836     
24837     this.ds = config.dataSource;
24838     
24839     if (config.store && !this.ds) {
24840         this.store= Roo.factory(config.store, Roo.data);
24841         this.ds = this.store;
24842         this.ds.xmodule = this.xmodule || false;
24843     }
24844     
24845     this.toolbarItems = [];
24846     if (config.items) {
24847         this.toolbarItems = config.items;
24848     }
24849     
24850     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24851     
24852     this.cursor = 0;
24853     
24854     if (this.ds) { 
24855         this.bind(this.ds);
24856     }
24857     
24858     if (Roo.bootstrap.version == 4) {
24859         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24860     } else {
24861         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24862     }
24863     
24864 };
24865
24866 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24867     /**
24868      * @cfg {Roo.data.Store} dataSource
24869      * The underlying data store providing the paged data
24870      */
24871     /**
24872      * @cfg {String/HTMLElement/Element} container
24873      * container The id or element that will contain the toolbar
24874      */
24875     /**
24876      * @cfg {Boolean} displayInfo
24877      * True to display the displayMsg (defaults to false)
24878      */
24879     /**
24880      * @cfg {Number} pageSize
24881      * The number of records to display per page (defaults to 20)
24882      */
24883     pageSize: 20,
24884     /**
24885      * @cfg {String} displayMsg
24886      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24887      */
24888     displayMsg : 'Displaying {0} - {1} of {2}',
24889     /**
24890      * @cfg {String} emptyMsg
24891      * The message to display when no records are found (defaults to "No data to display")
24892      */
24893     emptyMsg : 'No data to display',
24894     /**
24895      * Customizable piece of the default paging text (defaults to "Page")
24896      * @type String
24897      */
24898     beforePageText : "Page",
24899     /**
24900      * Customizable piece of the default paging text (defaults to "of %0")
24901      * @type String
24902      */
24903     afterPageText : "of {0}",
24904     /**
24905      * Customizable piece of the default paging text (defaults to "First Page")
24906      * @type String
24907      */
24908     firstText : "First Page",
24909     /**
24910      * Customizable piece of the default paging text (defaults to "Previous Page")
24911      * @type String
24912      */
24913     prevText : "Previous Page",
24914     /**
24915      * Customizable piece of the default paging text (defaults to "Next Page")
24916      * @type String
24917      */
24918     nextText : "Next Page",
24919     /**
24920      * Customizable piece of the default paging text (defaults to "Last Page")
24921      * @type String
24922      */
24923     lastText : "Last Page",
24924     /**
24925      * Customizable piece of the default paging text (defaults to "Refresh")
24926      * @type String
24927      */
24928     refreshText : "Refresh",
24929
24930     buttons : false,
24931     // private
24932     onRender : function(ct, position) 
24933     {
24934         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24935         this.navgroup.parentId = this.id;
24936         this.navgroup.onRender(this.el, null);
24937         // add the buttons to the navgroup
24938         
24939         if(this.displayInfo){
24940             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24941             this.displayEl = this.el.select('.x-paging-info', true).first();
24942 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24943 //            this.displayEl = navel.el.select('span',true).first();
24944         }
24945         
24946         var _this = this;
24947         
24948         if(this.buttons){
24949             Roo.each(_this.buttons, function(e){ // this might need to use render????
24950                Roo.factory(e).render(_this.el);
24951             });
24952         }
24953             
24954         Roo.each(_this.toolbarItems, function(e) {
24955             _this.navgroup.addItem(e);
24956         });
24957         
24958         
24959         this.first = this.navgroup.addItem({
24960             tooltip: this.firstText,
24961             cls: "prev btn-outline-secondary",
24962             html : ' <i class="fa fa-step-backward"></i>',
24963             disabled: true,
24964             preventDefault: true,
24965             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24966         });
24967         
24968         this.prev =  this.navgroup.addItem({
24969             tooltip: this.prevText,
24970             cls: "prev btn-outline-secondary",
24971             html : ' <i class="fa fa-backward"></i>',
24972             disabled: true,
24973             preventDefault: true,
24974             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24975         });
24976     //this.addSeparator();
24977         
24978         
24979         var field = this.navgroup.addItem( {
24980             tagtype : 'span',
24981             cls : 'x-paging-position  btn-outline-secondary',
24982              disabled: true,
24983             html : this.beforePageText  +
24984                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24985                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24986          } ); //?? escaped?
24987         
24988         this.field = field.el.select('input', true).first();
24989         this.field.on("keydown", this.onPagingKeydown, this);
24990         this.field.on("focus", function(){this.dom.select();});
24991     
24992     
24993         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24994         //this.field.setHeight(18);
24995         //this.addSeparator();
24996         this.next = this.navgroup.addItem({
24997             tooltip: this.nextText,
24998             cls: "next btn-outline-secondary",
24999             html : ' <i class="fa fa-forward"></i>',
25000             disabled: true,
25001             preventDefault: true,
25002             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25003         });
25004         this.last = this.navgroup.addItem({
25005             tooltip: this.lastText,
25006             html : ' <i class="fa fa-step-forward"></i>',
25007             cls: "next btn-outline-secondary",
25008             disabled: true,
25009             preventDefault: true,
25010             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25011         });
25012     //this.addSeparator();
25013         this.loading = this.navgroup.addItem({
25014             tooltip: this.refreshText,
25015             cls: "btn-outline-secondary",
25016             html : ' <i class="fa fa-refresh"></i>',
25017             preventDefault: true,
25018             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25019         });
25020         
25021     },
25022
25023     // private
25024     updateInfo : function(){
25025         if(this.displayEl){
25026             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25027             var msg = count == 0 ?
25028                 this.emptyMsg :
25029                 String.format(
25030                     this.displayMsg,
25031                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25032                 );
25033             this.displayEl.update(msg);
25034         }
25035     },
25036
25037     // private
25038     onLoad : function(ds, r, o)
25039     {
25040         this.cursor = o.params.start ? o.params.start : 0;
25041         
25042         var d = this.getPageData(),
25043             ap = d.activePage,
25044             ps = d.pages;
25045         
25046         
25047         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25048         this.field.dom.value = ap;
25049         this.first.setDisabled(ap == 1);
25050         this.prev.setDisabled(ap == 1);
25051         this.next.setDisabled(ap == ps);
25052         this.last.setDisabled(ap == ps);
25053         this.loading.enable();
25054         this.updateInfo();
25055     },
25056
25057     // private
25058     getPageData : function(){
25059         var total = this.ds.getTotalCount();
25060         return {
25061             total : total,
25062             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25063             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25064         };
25065     },
25066
25067     // private
25068     onLoadError : function(){
25069         this.loading.enable();
25070     },
25071
25072     // private
25073     onPagingKeydown : function(e){
25074         var k = e.getKey();
25075         var d = this.getPageData();
25076         if(k == e.RETURN){
25077             var v = this.field.dom.value, pageNum;
25078             if(!v || isNaN(pageNum = parseInt(v, 10))){
25079                 this.field.dom.value = d.activePage;
25080                 return;
25081             }
25082             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25083             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25084             e.stopEvent();
25085         }
25086         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))
25087         {
25088           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25089           this.field.dom.value = pageNum;
25090           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25091           e.stopEvent();
25092         }
25093         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25094         {
25095           var v = this.field.dom.value, pageNum; 
25096           var increment = (e.shiftKey) ? 10 : 1;
25097           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25098                 increment *= -1;
25099           }
25100           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25101             this.field.dom.value = d.activePage;
25102             return;
25103           }
25104           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25105           {
25106             this.field.dom.value = parseInt(v, 10) + increment;
25107             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25108             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25109           }
25110           e.stopEvent();
25111         }
25112     },
25113
25114     // private
25115     beforeLoad : function(){
25116         if(this.loading){
25117             this.loading.disable();
25118         }
25119     },
25120
25121     // private
25122     onClick : function(which){
25123         
25124         var ds = this.ds;
25125         if (!ds) {
25126             return;
25127         }
25128         
25129         switch(which){
25130             case "first":
25131                 ds.load({params:{start: 0, limit: this.pageSize}});
25132             break;
25133             case "prev":
25134                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25135             break;
25136             case "next":
25137                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25138             break;
25139             case "last":
25140                 var total = ds.getTotalCount();
25141                 var extra = total % this.pageSize;
25142                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25143                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25144             break;
25145             case "refresh":
25146                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25147             break;
25148         }
25149     },
25150
25151     /**
25152      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25153      * @param {Roo.data.Store} store The data store to unbind
25154      */
25155     unbind : function(ds){
25156         ds.un("beforeload", this.beforeLoad, this);
25157         ds.un("load", this.onLoad, this);
25158         ds.un("loadexception", this.onLoadError, this);
25159         ds.un("remove", this.updateInfo, this);
25160         ds.un("add", this.updateInfo, this);
25161         this.ds = undefined;
25162     },
25163
25164     /**
25165      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25166      * @param {Roo.data.Store} store The data store to bind
25167      */
25168     bind : function(ds){
25169         ds.on("beforeload", this.beforeLoad, this);
25170         ds.on("load", this.onLoad, this);
25171         ds.on("loadexception", this.onLoadError, this);
25172         ds.on("remove", this.updateInfo, this);
25173         ds.on("add", this.updateInfo, this);
25174         this.ds = ds;
25175     }
25176 });/*
25177  * - LGPL
25178  *
25179  * element
25180  * 
25181  */
25182
25183 /**
25184  * @class Roo.bootstrap.MessageBar
25185  * @extends Roo.bootstrap.Component
25186  * Bootstrap MessageBar class
25187  * @cfg {String} html contents of the MessageBar
25188  * @cfg {String} weight (info | success | warning | danger) default info
25189  * @cfg {String} beforeClass insert the bar before the given class
25190  * @cfg {Boolean} closable (true | false) default false
25191  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25192  * 
25193  * @constructor
25194  * Create a new Element
25195  * @param {Object} config The config object
25196  */
25197
25198 Roo.bootstrap.MessageBar = function(config){
25199     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25200 };
25201
25202 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25203     
25204     html: '',
25205     weight: 'info',
25206     closable: false,
25207     fixed: false,
25208     beforeClass: 'bootstrap-sticky-wrap',
25209     
25210     getAutoCreate : function(){
25211         
25212         var cfg = {
25213             tag: 'div',
25214             cls: 'alert alert-dismissable alert-' + this.weight,
25215             cn: [
25216                 {
25217                     tag: 'span',
25218                     cls: 'message',
25219                     html: this.html || ''
25220                 }
25221             ]
25222         };
25223         
25224         if(this.fixed){
25225             cfg.cls += ' alert-messages-fixed';
25226         }
25227         
25228         if(this.closable){
25229             cfg.cn.push({
25230                 tag: 'button',
25231                 cls: 'close',
25232                 html: 'x'
25233             });
25234         }
25235         
25236         return cfg;
25237     },
25238     
25239     onRender : function(ct, position)
25240     {
25241         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25242         
25243         if(!this.el){
25244             var cfg = Roo.apply({},  this.getAutoCreate());
25245             cfg.id = Roo.id();
25246             
25247             if (this.cls) {
25248                 cfg.cls += ' ' + this.cls;
25249             }
25250             if (this.style) {
25251                 cfg.style = this.style;
25252             }
25253             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25254             
25255             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25256         }
25257         
25258         this.el.select('>button.close').on('click', this.hide, this);
25259         
25260     },
25261     
25262     show : function()
25263     {
25264         if (!this.rendered) {
25265             this.render();
25266         }
25267         
25268         this.el.show();
25269         
25270         this.fireEvent('show', this);
25271         
25272     },
25273     
25274     hide : function()
25275     {
25276         if (!this.rendered) {
25277             this.render();
25278         }
25279         
25280         this.el.hide();
25281         
25282         this.fireEvent('hide', this);
25283     },
25284     
25285     update : function()
25286     {
25287 //        var e = this.el.dom.firstChild;
25288 //        
25289 //        if(this.closable){
25290 //            e = e.nextSibling;
25291 //        }
25292 //        
25293 //        e.data = this.html || '';
25294
25295         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25296     }
25297    
25298 });
25299
25300  
25301
25302      /*
25303  * - LGPL
25304  *
25305  * Graph
25306  * 
25307  */
25308
25309
25310 /**
25311  * @class Roo.bootstrap.Graph
25312  * @extends Roo.bootstrap.Component
25313  * Bootstrap Graph class
25314 > Prameters
25315  -sm {number} sm 4
25316  -md {number} md 5
25317  @cfg {String} graphtype  bar | vbar | pie
25318  @cfg {number} g_x coodinator | centre x (pie)
25319  @cfg {number} g_y coodinator | centre y (pie)
25320  @cfg {number} g_r radius (pie)
25321  @cfg {number} g_height height of the chart (respected by all elements in the set)
25322  @cfg {number} g_width width of the chart (respected by all elements in the set)
25323  @cfg {Object} title The title of the chart
25324     
25325  -{Array}  values
25326  -opts (object) options for the chart 
25327      o {
25328      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25329      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25330      o vgutter (number)
25331      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.
25332      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25333      o to
25334      o stretch (boolean)
25335      o }
25336  -opts (object) options for the pie
25337      o{
25338      o cut
25339      o startAngle (number)
25340      o endAngle (number)
25341      } 
25342  *
25343  * @constructor
25344  * Create a new Input
25345  * @param {Object} config The config object
25346  */
25347
25348 Roo.bootstrap.Graph = function(config){
25349     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25350     
25351     this.addEvents({
25352         // img events
25353         /**
25354          * @event click
25355          * The img click event for the img.
25356          * @param {Roo.EventObject} e
25357          */
25358         "click" : true
25359     });
25360 };
25361
25362 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25363     
25364     sm: 4,
25365     md: 5,
25366     graphtype: 'bar',
25367     g_height: 250,
25368     g_width: 400,
25369     g_x: 50,
25370     g_y: 50,
25371     g_r: 30,
25372     opts:{
25373         //g_colors: this.colors,
25374         g_type: 'soft',
25375         g_gutter: '20%'
25376
25377     },
25378     title : false,
25379
25380     getAutoCreate : function(){
25381         
25382         var cfg = {
25383             tag: 'div',
25384             html : null
25385         };
25386         
25387         
25388         return  cfg;
25389     },
25390
25391     onRender : function(ct,position){
25392         
25393         
25394         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25395         
25396         if (typeof(Raphael) == 'undefined') {
25397             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25398             return;
25399         }
25400         
25401         this.raphael = Raphael(this.el.dom);
25402         
25403                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25404                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25405                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25406                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25407                 /*
25408                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25409                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25410                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25411                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25412                 
25413                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25414                 r.barchart(330, 10, 300, 220, data1);
25415                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25416                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25417                 */
25418                 
25419                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25420                 // r.barchart(30, 30, 560, 250,  xdata, {
25421                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25422                 //     axis : "0 0 1 1",
25423                 //     axisxlabels :  xdata
25424                 //     //yvalues : cols,
25425                    
25426                 // });
25427 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25428 //        
25429 //        this.load(null,xdata,{
25430 //                axis : "0 0 1 1",
25431 //                axisxlabels :  xdata
25432 //                });
25433
25434     },
25435
25436     load : function(graphtype,xdata,opts)
25437     {
25438         this.raphael.clear();
25439         if(!graphtype) {
25440             graphtype = this.graphtype;
25441         }
25442         if(!opts){
25443             opts = this.opts;
25444         }
25445         var r = this.raphael,
25446             fin = function () {
25447                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25448             },
25449             fout = function () {
25450                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25451             },
25452             pfin = function() {
25453                 this.sector.stop();
25454                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25455
25456                 if (this.label) {
25457                     this.label[0].stop();
25458                     this.label[0].attr({ r: 7.5 });
25459                     this.label[1].attr({ "font-weight": 800 });
25460                 }
25461             },
25462             pfout = function() {
25463                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25464
25465                 if (this.label) {
25466                     this.label[0].animate({ r: 5 }, 500, "bounce");
25467                     this.label[1].attr({ "font-weight": 400 });
25468                 }
25469             };
25470
25471         switch(graphtype){
25472             case 'bar':
25473                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25474                 break;
25475             case 'hbar':
25476                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25477                 break;
25478             case 'pie':
25479 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25480 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25481 //            
25482                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25483                 
25484                 break;
25485
25486         }
25487         
25488         if(this.title){
25489             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25490         }
25491         
25492     },
25493     
25494     setTitle: function(o)
25495     {
25496         this.title = o;
25497     },
25498     
25499     initEvents: function() {
25500         
25501         if(!this.href){
25502             this.el.on('click', this.onClick, this);
25503         }
25504     },
25505     
25506     onClick : function(e)
25507     {
25508         Roo.log('img onclick');
25509         this.fireEvent('click', this, e);
25510     }
25511    
25512 });
25513
25514  
25515 /*
25516  * - LGPL
25517  *
25518  * numberBox
25519  * 
25520  */
25521 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25522
25523 /**
25524  * @class Roo.bootstrap.dash.NumberBox
25525  * @extends Roo.bootstrap.Component
25526  * Bootstrap NumberBox class
25527  * @cfg {String} headline Box headline
25528  * @cfg {String} content Box content
25529  * @cfg {String} icon Box icon
25530  * @cfg {String} footer Footer text
25531  * @cfg {String} fhref Footer href
25532  * 
25533  * @constructor
25534  * Create a new NumberBox
25535  * @param {Object} config The config object
25536  */
25537
25538
25539 Roo.bootstrap.dash.NumberBox = function(config){
25540     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25541     
25542 };
25543
25544 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25545     
25546     headline : '',
25547     content : '',
25548     icon : '',
25549     footer : '',
25550     fhref : '',
25551     ficon : '',
25552     
25553     getAutoCreate : function(){
25554         
25555         var cfg = {
25556             tag : 'div',
25557             cls : 'small-box ',
25558             cn : [
25559                 {
25560                     tag : 'div',
25561                     cls : 'inner',
25562                     cn :[
25563                         {
25564                             tag : 'h3',
25565                             cls : 'roo-headline',
25566                             html : this.headline
25567                         },
25568                         {
25569                             tag : 'p',
25570                             cls : 'roo-content',
25571                             html : this.content
25572                         }
25573                     ]
25574                 }
25575             ]
25576         };
25577         
25578         if(this.icon){
25579             cfg.cn.push({
25580                 tag : 'div',
25581                 cls : 'icon',
25582                 cn :[
25583                     {
25584                         tag : 'i',
25585                         cls : 'ion ' + this.icon
25586                     }
25587                 ]
25588             });
25589         }
25590         
25591         if(this.footer){
25592             var footer = {
25593                 tag : 'a',
25594                 cls : 'small-box-footer',
25595                 href : this.fhref || '#',
25596                 html : this.footer
25597             };
25598             
25599             cfg.cn.push(footer);
25600             
25601         }
25602         
25603         return  cfg;
25604     },
25605
25606     onRender : function(ct,position){
25607         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25608
25609
25610        
25611                 
25612     },
25613
25614     setHeadline: function (value)
25615     {
25616         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25617     },
25618     
25619     setFooter: function (value, href)
25620     {
25621         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25622         
25623         if(href){
25624             this.el.select('a.small-box-footer',true).first().attr('href', href);
25625         }
25626         
25627     },
25628
25629     setContent: function (value)
25630     {
25631         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25632     },
25633
25634     initEvents: function() 
25635     {   
25636         
25637     }
25638     
25639 });
25640
25641  
25642 /*
25643  * - LGPL
25644  *
25645  * TabBox
25646  * 
25647  */
25648 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25649
25650 /**
25651  * @class Roo.bootstrap.dash.TabBox
25652  * @extends Roo.bootstrap.Component
25653  * Bootstrap TabBox class
25654  * @cfg {String} title Title of the TabBox
25655  * @cfg {String} icon Icon of the TabBox
25656  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25657  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25658  * 
25659  * @constructor
25660  * Create a new TabBox
25661  * @param {Object} config The config object
25662  */
25663
25664
25665 Roo.bootstrap.dash.TabBox = function(config){
25666     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25667     this.addEvents({
25668         // raw events
25669         /**
25670          * @event addpane
25671          * When a pane is added
25672          * @param {Roo.bootstrap.dash.TabPane} pane
25673          */
25674         "addpane" : true,
25675         /**
25676          * @event activatepane
25677          * When a pane is activated
25678          * @param {Roo.bootstrap.dash.TabPane} pane
25679          */
25680         "activatepane" : true
25681         
25682          
25683     });
25684     
25685     this.panes = [];
25686 };
25687
25688 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25689
25690     title : '',
25691     icon : false,
25692     showtabs : true,
25693     tabScrollable : false,
25694     
25695     getChildContainer : function()
25696     {
25697         return this.el.select('.tab-content', true).first();
25698     },
25699     
25700     getAutoCreate : function(){
25701         
25702         var header = {
25703             tag: 'li',
25704             cls: 'pull-left header',
25705             html: this.title,
25706             cn : []
25707         };
25708         
25709         if(this.icon){
25710             header.cn.push({
25711                 tag: 'i',
25712                 cls: 'fa ' + this.icon
25713             });
25714         }
25715         
25716         var h = {
25717             tag: 'ul',
25718             cls: 'nav nav-tabs pull-right',
25719             cn: [
25720                 header
25721             ]
25722         };
25723         
25724         if(this.tabScrollable){
25725             h = {
25726                 tag: 'div',
25727                 cls: 'tab-header',
25728                 cn: [
25729                     {
25730                         tag: 'ul',
25731                         cls: 'nav nav-tabs pull-right',
25732                         cn: [
25733                             header
25734                         ]
25735                     }
25736                 ]
25737             };
25738         }
25739         
25740         var cfg = {
25741             tag: 'div',
25742             cls: 'nav-tabs-custom',
25743             cn: [
25744                 h,
25745                 {
25746                     tag: 'div',
25747                     cls: 'tab-content no-padding',
25748                     cn: []
25749                 }
25750             ]
25751         };
25752
25753         return  cfg;
25754     },
25755     initEvents : function()
25756     {
25757         //Roo.log('add add pane handler');
25758         this.on('addpane', this.onAddPane, this);
25759     },
25760      /**
25761      * Updates the box title
25762      * @param {String} html to set the title to.
25763      */
25764     setTitle : function(value)
25765     {
25766         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25767     },
25768     onAddPane : function(pane)
25769     {
25770         this.panes.push(pane);
25771         //Roo.log('addpane');
25772         //Roo.log(pane);
25773         // tabs are rendere left to right..
25774         if(!this.showtabs){
25775             return;
25776         }
25777         
25778         var ctr = this.el.select('.nav-tabs', true).first();
25779          
25780          
25781         var existing = ctr.select('.nav-tab',true);
25782         var qty = existing.getCount();;
25783         
25784         
25785         var tab = ctr.createChild({
25786             tag : 'li',
25787             cls : 'nav-tab' + (qty ? '' : ' active'),
25788             cn : [
25789                 {
25790                     tag : 'a',
25791                     href:'#',
25792                     html : pane.title
25793                 }
25794             ]
25795         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25796         pane.tab = tab;
25797         
25798         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25799         if (!qty) {
25800             pane.el.addClass('active');
25801         }
25802         
25803                 
25804     },
25805     onTabClick : function(ev,un,ob,pane)
25806     {
25807         //Roo.log('tab - prev default');
25808         ev.preventDefault();
25809         
25810         
25811         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25812         pane.tab.addClass('active');
25813         //Roo.log(pane.title);
25814         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25815         // technically we should have a deactivate event.. but maybe add later.
25816         // and it should not de-activate the selected tab...
25817         this.fireEvent('activatepane', pane);
25818         pane.el.addClass('active');
25819         pane.fireEvent('activate');
25820         
25821         
25822     },
25823     
25824     getActivePane : function()
25825     {
25826         var r = false;
25827         Roo.each(this.panes, function(p) {
25828             if(p.el.hasClass('active')){
25829                 r = p;
25830                 return false;
25831             }
25832             
25833             return;
25834         });
25835         
25836         return r;
25837     }
25838     
25839     
25840 });
25841
25842  
25843 /*
25844  * - LGPL
25845  *
25846  * Tab pane
25847  * 
25848  */
25849 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25850 /**
25851  * @class Roo.bootstrap.TabPane
25852  * @extends Roo.bootstrap.Component
25853  * Bootstrap TabPane class
25854  * @cfg {Boolean} active (false | true) Default false
25855  * @cfg {String} title title of panel
25856
25857  * 
25858  * @constructor
25859  * Create a new TabPane
25860  * @param {Object} config The config object
25861  */
25862
25863 Roo.bootstrap.dash.TabPane = function(config){
25864     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25865     
25866     this.addEvents({
25867         // raw events
25868         /**
25869          * @event activate
25870          * When a pane is activated
25871          * @param {Roo.bootstrap.dash.TabPane} pane
25872          */
25873         "activate" : true
25874          
25875     });
25876 };
25877
25878 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25879     
25880     active : false,
25881     title : '',
25882     
25883     // the tabBox that this is attached to.
25884     tab : false,
25885      
25886     getAutoCreate : function() 
25887     {
25888         var cfg = {
25889             tag: 'div',
25890             cls: 'tab-pane'
25891         };
25892         
25893         if(this.active){
25894             cfg.cls += ' active';
25895         }
25896         
25897         return cfg;
25898     },
25899     initEvents  : function()
25900     {
25901         //Roo.log('trigger add pane handler');
25902         this.parent().fireEvent('addpane', this)
25903     },
25904     
25905      /**
25906      * Updates the tab title 
25907      * @param {String} html to set the title to.
25908      */
25909     setTitle: function(str)
25910     {
25911         if (!this.tab) {
25912             return;
25913         }
25914         this.title = str;
25915         this.tab.select('a', true).first().dom.innerHTML = str;
25916         
25917     }
25918     
25919     
25920     
25921 });
25922
25923  
25924
25925
25926  /*
25927  * - LGPL
25928  *
25929  * menu
25930  * 
25931  */
25932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25933
25934 /**
25935  * @class Roo.bootstrap.menu.Menu
25936  * @extends Roo.bootstrap.Component
25937  * Bootstrap Menu class - container for Menu
25938  * @cfg {String} html Text of the menu
25939  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25940  * @cfg {String} icon Font awesome icon
25941  * @cfg {String} pos Menu align to (top | bottom) default bottom
25942  * 
25943  * 
25944  * @constructor
25945  * Create a new Menu
25946  * @param {Object} config The config object
25947  */
25948
25949
25950 Roo.bootstrap.menu.Menu = function(config){
25951     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25952     
25953     this.addEvents({
25954         /**
25955          * @event beforeshow
25956          * Fires before this menu is displayed
25957          * @param {Roo.bootstrap.menu.Menu} this
25958          */
25959         beforeshow : true,
25960         /**
25961          * @event beforehide
25962          * Fires before this menu is hidden
25963          * @param {Roo.bootstrap.menu.Menu} this
25964          */
25965         beforehide : true,
25966         /**
25967          * @event show
25968          * Fires after this menu is displayed
25969          * @param {Roo.bootstrap.menu.Menu} this
25970          */
25971         show : true,
25972         /**
25973          * @event hide
25974          * Fires after this menu is hidden
25975          * @param {Roo.bootstrap.menu.Menu} this
25976          */
25977         hide : true,
25978         /**
25979          * @event click
25980          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25981          * @param {Roo.bootstrap.menu.Menu} this
25982          * @param {Roo.EventObject} e
25983          */
25984         click : true
25985     });
25986     
25987 };
25988
25989 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25990     
25991     submenu : false,
25992     html : '',
25993     weight : 'default',
25994     icon : false,
25995     pos : 'bottom',
25996     
25997     
25998     getChildContainer : function() {
25999         if(this.isSubMenu){
26000             return this.el;
26001         }
26002         
26003         return this.el.select('ul.dropdown-menu', true).first();  
26004     },
26005     
26006     getAutoCreate : function()
26007     {
26008         var text = [
26009             {
26010                 tag : 'span',
26011                 cls : 'roo-menu-text',
26012                 html : this.html
26013             }
26014         ];
26015         
26016         if(this.icon){
26017             text.unshift({
26018                 tag : 'i',
26019                 cls : 'fa ' + this.icon
26020             })
26021         }
26022         
26023         
26024         var cfg = {
26025             tag : 'div',
26026             cls : 'btn-group',
26027             cn : [
26028                 {
26029                     tag : 'button',
26030                     cls : 'dropdown-button btn btn-' + this.weight,
26031                     cn : text
26032                 },
26033                 {
26034                     tag : 'button',
26035                     cls : 'dropdown-toggle btn btn-' + this.weight,
26036                     cn : [
26037                         {
26038                             tag : 'span',
26039                             cls : 'caret'
26040                         }
26041                     ]
26042                 },
26043                 {
26044                     tag : 'ul',
26045                     cls : 'dropdown-menu'
26046                 }
26047             ]
26048             
26049         };
26050         
26051         if(this.pos == 'top'){
26052             cfg.cls += ' dropup';
26053         }
26054         
26055         if(this.isSubMenu){
26056             cfg = {
26057                 tag : 'ul',
26058                 cls : 'dropdown-menu'
26059             }
26060         }
26061         
26062         return cfg;
26063     },
26064     
26065     onRender : function(ct, position)
26066     {
26067         this.isSubMenu = ct.hasClass('dropdown-submenu');
26068         
26069         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26070     },
26071     
26072     initEvents : function() 
26073     {
26074         if(this.isSubMenu){
26075             return;
26076         }
26077         
26078         this.hidden = true;
26079         
26080         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26081         this.triggerEl.on('click', this.onTriggerPress, this);
26082         
26083         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26084         this.buttonEl.on('click', this.onClick, this);
26085         
26086     },
26087     
26088     list : function()
26089     {
26090         if(this.isSubMenu){
26091             return this.el;
26092         }
26093         
26094         return this.el.select('ul.dropdown-menu', true).first();
26095     },
26096     
26097     onClick : function(e)
26098     {
26099         this.fireEvent("click", this, e);
26100     },
26101     
26102     onTriggerPress  : function(e)
26103     {   
26104         if (this.isVisible()) {
26105             this.hide();
26106         } else {
26107             this.show();
26108         }
26109     },
26110     
26111     isVisible : function(){
26112         return !this.hidden;
26113     },
26114     
26115     show : function()
26116     {
26117         this.fireEvent("beforeshow", this);
26118         
26119         this.hidden = false;
26120         this.el.addClass('open');
26121         
26122         Roo.get(document).on("mouseup", this.onMouseUp, this);
26123         
26124         this.fireEvent("show", this);
26125         
26126         
26127     },
26128     
26129     hide : function()
26130     {
26131         this.fireEvent("beforehide", this);
26132         
26133         this.hidden = true;
26134         this.el.removeClass('open');
26135         
26136         Roo.get(document).un("mouseup", this.onMouseUp);
26137         
26138         this.fireEvent("hide", this);
26139     },
26140     
26141     onMouseUp : function()
26142     {
26143         this.hide();
26144     }
26145     
26146 });
26147
26148  
26149  /*
26150  * - LGPL
26151  *
26152  * menu item
26153  * 
26154  */
26155 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26156
26157 /**
26158  * @class Roo.bootstrap.menu.Item
26159  * @extends Roo.bootstrap.Component
26160  * Bootstrap MenuItem class
26161  * @cfg {Boolean} submenu (true | false) default false
26162  * @cfg {String} html text of the item
26163  * @cfg {String} href the link
26164  * @cfg {Boolean} disable (true | false) default false
26165  * @cfg {Boolean} preventDefault (true | false) default true
26166  * @cfg {String} icon Font awesome icon
26167  * @cfg {String} pos Submenu align to (left | right) default right 
26168  * 
26169  * 
26170  * @constructor
26171  * Create a new Item
26172  * @param {Object} config The config object
26173  */
26174
26175
26176 Roo.bootstrap.menu.Item = function(config){
26177     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26178     this.addEvents({
26179         /**
26180          * @event mouseover
26181          * Fires when the mouse is hovering over this menu
26182          * @param {Roo.bootstrap.menu.Item} this
26183          * @param {Roo.EventObject} e
26184          */
26185         mouseover : true,
26186         /**
26187          * @event mouseout
26188          * Fires when the mouse exits this menu
26189          * @param {Roo.bootstrap.menu.Item} this
26190          * @param {Roo.EventObject} e
26191          */
26192         mouseout : true,
26193         // raw events
26194         /**
26195          * @event click
26196          * The raw click event for the entire grid.
26197          * @param {Roo.EventObject} e
26198          */
26199         click : true
26200     });
26201 };
26202
26203 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26204     
26205     submenu : false,
26206     href : '',
26207     html : '',
26208     preventDefault: true,
26209     disable : false,
26210     icon : false,
26211     pos : 'right',
26212     
26213     getAutoCreate : function()
26214     {
26215         var text = [
26216             {
26217                 tag : 'span',
26218                 cls : 'roo-menu-item-text',
26219                 html : this.html
26220             }
26221         ];
26222         
26223         if(this.icon){
26224             text.unshift({
26225                 tag : 'i',
26226                 cls : 'fa ' + this.icon
26227             })
26228         }
26229         
26230         var cfg = {
26231             tag : 'li',
26232             cn : [
26233                 {
26234                     tag : 'a',
26235                     href : this.href || '#',
26236                     cn : text
26237                 }
26238             ]
26239         };
26240         
26241         if(this.disable){
26242             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26243         }
26244         
26245         if(this.submenu){
26246             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26247             
26248             if(this.pos == 'left'){
26249                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26250             }
26251         }
26252         
26253         return cfg;
26254     },
26255     
26256     initEvents : function() 
26257     {
26258         this.el.on('mouseover', this.onMouseOver, this);
26259         this.el.on('mouseout', this.onMouseOut, this);
26260         
26261         this.el.select('a', true).first().on('click', this.onClick, this);
26262         
26263     },
26264     
26265     onClick : function(e)
26266     {
26267         if(this.preventDefault){
26268             e.preventDefault();
26269         }
26270         
26271         this.fireEvent("click", this, e);
26272     },
26273     
26274     onMouseOver : function(e)
26275     {
26276         if(this.submenu && this.pos == 'left'){
26277             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26278         }
26279         
26280         this.fireEvent("mouseover", this, e);
26281     },
26282     
26283     onMouseOut : function(e)
26284     {
26285         this.fireEvent("mouseout", this, e);
26286     }
26287 });
26288
26289  
26290
26291  /*
26292  * - LGPL
26293  *
26294  * menu separator
26295  * 
26296  */
26297 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26298
26299 /**
26300  * @class Roo.bootstrap.menu.Separator
26301  * @extends Roo.bootstrap.Component
26302  * Bootstrap Separator class
26303  * 
26304  * @constructor
26305  * Create a new Separator
26306  * @param {Object} config The config object
26307  */
26308
26309
26310 Roo.bootstrap.menu.Separator = function(config){
26311     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26312 };
26313
26314 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26315     
26316     getAutoCreate : function(){
26317         var cfg = {
26318             tag : 'li',
26319             cls: 'divider'
26320         };
26321         
26322         return cfg;
26323     }
26324    
26325 });
26326
26327  
26328
26329  /*
26330  * - LGPL
26331  *
26332  * Tooltip
26333  * 
26334  */
26335
26336 /**
26337  * @class Roo.bootstrap.Tooltip
26338  * Bootstrap Tooltip class
26339  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26340  * to determine which dom element triggers the tooltip.
26341  * 
26342  * It needs to add support for additional attributes like tooltip-position
26343  * 
26344  * @constructor
26345  * Create a new Toolti
26346  * @param {Object} config The config object
26347  */
26348
26349 Roo.bootstrap.Tooltip = function(config){
26350     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26351     
26352     this.alignment = Roo.bootstrap.Tooltip.alignment;
26353     
26354     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26355         this.alignment = config.alignment;
26356     }
26357     
26358 };
26359
26360 Roo.apply(Roo.bootstrap.Tooltip, {
26361     /**
26362      * @function init initialize tooltip monitoring.
26363      * @static
26364      */
26365     currentEl : false,
26366     currentTip : false,
26367     currentRegion : false,
26368     
26369     //  init : delay?
26370     
26371     init : function()
26372     {
26373         Roo.get(document).on('mouseover', this.enter ,this);
26374         Roo.get(document).on('mouseout', this.leave, this);
26375          
26376         
26377         this.currentTip = new Roo.bootstrap.Tooltip();
26378     },
26379     
26380     enter : function(ev)
26381     {
26382         var dom = ev.getTarget();
26383         
26384         //Roo.log(['enter',dom]);
26385         var el = Roo.fly(dom);
26386         if (this.currentEl) {
26387             //Roo.log(dom);
26388             //Roo.log(this.currentEl);
26389             //Roo.log(this.currentEl.contains(dom));
26390             if (this.currentEl == el) {
26391                 return;
26392             }
26393             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26394                 return;
26395             }
26396
26397         }
26398         
26399         if (this.currentTip.el) {
26400             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26401         }    
26402         //Roo.log(ev);
26403         
26404         if(!el || el.dom == document){
26405             return;
26406         }
26407         
26408         var bindEl = el;
26409         
26410         // you can not look for children, as if el is the body.. then everythign is the child..
26411         if (!el.attr('tooltip')) { //
26412             if (!el.select("[tooltip]").elements.length) {
26413                 return;
26414             }
26415             // is the mouse over this child...?
26416             bindEl = el.select("[tooltip]").first();
26417             var xy = ev.getXY();
26418             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26419                 //Roo.log("not in region.");
26420                 return;
26421             }
26422             //Roo.log("child element over..");
26423             
26424         }
26425         this.currentEl = bindEl;
26426         this.currentTip.bind(bindEl);
26427         this.currentRegion = Roo.lib.Region.getRegion(dom);
26428         this.currentTip.enter();
26429         
26430     },
26431     leave : function(ev)
26432     {
26433         var dom = ev.getTarget();
26434         //Roo.log(['leave',dom]);
26435         if (!this.currentEl) {
26436             return;
26437         }
26438         
26439         
26440         if (dom != this.currentEl.dom) {
26441             return;
26442         }
26443         var xy = ev.getXY();
26444         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26445             return;
26446         }
26447         // only activate leave if mouse cursor is outside... bounding box..
26448         
26449         
26450         
26451         
26452         if (this.currentTip) {
26453             this.currentTip.leave();
26454         }
26455         //Roo.log('clear currentEl');
26456         this.currentEl = false;
26457         
26458         
26459     },
26460     alignment : {
26461         'left' : ['r-l', [-2,0], 'right'],
26462         'right' : ['l-r', [2,0], 'left'],
26463         'bottom' : ['t-b', [0,2], 'top'],
26464         'top' : [ 'b-t', [0,-2], 'bottom']
26465     }
26466     
26467 });
26468
26469
26470 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26471     
26472     
26473     bindEl : false,
26474     
26475     delay : null, // can be { show : 300 , hide: 500}
26476     
26477     timeout : null,
26478     
26479     hoverState : null, //???
26480     
26481     placement : 'bottom', 
26482     
26483     alignment : false,
26484     
26485     getAutoCreate : function(){
26486     
26487         var cfg = {
26488            cls : 'tooltip',
26489            role : 'tooltip',
26490            cn : [
26491                 {
26492                     cls : 'tooltip-arrow'
26493                 },
26494                 {
26495                     cls : 'tooltip-inner'
26496                 }
26497            ]
26498         };
26499         
26500         return cfg;
26501     },
26502     bind : function(el)
26503     {
26504         this.bindEl = el;
26505     },
26506       
26507     
26508     enter : function () {
26509        
26510         if (this.timeout != null) {
26511             clearTimeout(this.timeout);
26512         }
26513         
26514         this.hoverState = 'in';
26515          //Roo.log("enter - show");
26516         if (!this.delay || !this.delay.show) {
26517             this.show();
26518             return;
26519         }
26520         var _t = this;
26521         this.timeout = setTimeout(function () {
26522             if (_t.hoverState == 'in') {
26523                 _t.show();
26524             }
26525         }, this.delay.show);
26526     },
26527     leave : function()
26528     {
26529         clearTimeout(this.timeout);
26530     
26531         this.hoverState = 'out';
26532          if (!this.delay || !this.delay.hide) {
26533             this.hide();
26534             return;
26535         }
26536        
26537         var _t = this;
26538         this.timeout = setTimeout(function () {
26539             //Roo.log("leave - timeout");
26540             
26541             if (_t.hoverState == 'out') {
26542                 _t.hide();
26543                 Roo.bootstrap.Tooltip.currentEl = false;
26544             }
26545         }, delay);
26546     },
26547     
26548     show : function (msg)
26549     {
26550         if (!this.el) {
26551             this.render(document.body);
26552         }
26553         // set content.
26554         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26555         
26556         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26557         
26558         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26559         
26560         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26561         
26562         var placement = typeof this.placement == 'function' ?
26563             this.placement.call(this, this.el, on_el) :
26564             this.placement;
26565             
26566         var autoToken = /\s?auto?\s?/i;
26567         var autoPlace = autoToken.test(placement);
26568         if (autoPlace) {
26569             placement = placement.replace(autoToken, '') || 'top';
26570         }
26571         
26572         //this.el.detach()
26573         //this.el.setXY([0,0]);
26574         this.el.show();
26575         //this.el.dom.style.display='block';
26576         
26577         //this.el.appendTo(on_el);
26578         
26579         var p = this.getPosition();
26580         var box = this.el.getBox();
26581         
26582         if (autoPlace) {
26583             // fixme..
26584         }
26585         
26586         var align = this.alignment[placement];
26587         
26588         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26589         
26590         if(placement == 'top' || placement == 'bottom'){
26591             if(xy[0] < 0){
26592                 placement = 'right';
26593             }
26594             
26595             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26596                 placement = 'left';
26597             }
26598             
26599             var scroll = Roo.select('body', true).first().getScroll();
26600             
26601             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26602                 placement = 'top';
26603             }
26604             
26605             align = this.alignment[placement];
26606         }
26607         
26608         this.el.alignTo(this.bindEl, align[0],align[1]);
26609         //var arrow = this.el.select('.arrow',true).first();
26610         //arrow.set(align[2], 
26611         
26612         this.el.addClass(placement);
26613         
26614         this.el.addClass('in fade');
26615         
26616         this.hoverState = null;
26617         
26618         if (this.el.hasClass('fade')) {
26619             // fade it?
26620         }
26621         
26622     },
26623     hide : function()
26624     {
26625          
26626         if (!this.el) {
26627             return;
26628         }
26629         //this.el.setXY([0,0]);
26630         this.el.removeClass('in');
26631         //this.el.hide();
26632         
26633     }
26634     
26635 });
26636  
26637
26638  /*
26639  * - LGPL
26640  *
26641  * Location Picker
26642  * 
26643  */
26644
26645 /**
26646  * @class Roo.bootstrap.LocationPicker
26647  * @extends Roo.bootstrap.Component
26648  * Bootstrap LocationPicker class
26649  * @cfg {Number} latitude Position when init default 0
26650  * @cfg {Number} longitude Position when init default 0
26651  * @cfg {Number} zoom default 15
26652  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26653  * @cfg {Boolean} mapTypeControl default false
26654  * @cfg {Boolean} disableDoubleClickZoom default false
26655  * @cfg {Boolean} scrollwheel default true
26656  * @cfg {Boolean} streetViewControl default false
26657  * @cfg {Number} radius default 0
26658  * @cfg {String} locationName
26659  * @cfg {Boolean} draggable default true
26660  * @cfg {Boolean} enableAutocomplete default false
26661  * @cfg {Boolean} enableReverseGeocode default true
26662  * @cfg {String} markerTitle
26663  * 
26664  * @constructor
26665  * Create a new LocationPicker
26666  * @param {Object} config The config object
26667  */
26668
26669
26670 Roo.bootstrap.LocationPicker = function(config){
26671     
26672     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26673     
26674     this.addEvents({
26675         /**
26676          * @event initial
26677          * Fires when the picker initialized.
26678          * @param {Roo.bootstrap.LocationPicker} this
26679          * @param {Google Location} location
26680          */
26681         initial : true,
26682         /**
26683          * @event positionchanged
26684          * Fires when the picker position changed.
26685          * @param {Roo.bootstrap.LocationPicker} this
26686          * @param {Google Location} location
26687          */
26688         positionchanged : true,
26689         /**
26690          * @event resize
26691          * Fires when the map resize.
26692          * @param {Roo.bootstrap.LocationPicker} this
26693          */
26694         resize : true,
26695         /**
26696          * @event show
26697          * Fires when the map show.
26698          * @param {Roo.bootstrap.LocationPicker} this
26699          */
26700         show : true,
26701         /**
26702          * @event hide
26703          * Fires when the map hide.
26704          * @param {Roo.bootstrap.LocationPicker} this
26705          */
26706         hide : true,
26707         /**
26708          * @event mapClick
26709          * Fires when click the map.
26710          * @param {Roo.bootstrap.LocationPicker} this
26711          * @param {Map event} e
26712          */
26713         mapClick : true,
26714         /**
26715          * @event mapRightClick
26716          * Fires when right click the map.
26717          * @param {Roo.bootstrap.LocationPicker} this
26718          * @param {Map event} e
26719          */
26720         mapRightClick : true,
26721         /**
26722          * @event markerClick
26723          * Fires when click the marker.
26724          * @param {Roo.bootstrap.LocationPicker} this
26725          * @param {Map event} e
26726          */
26727         markerClick : true,
26728         /**
26729          * @event markerRightClick
26730          * Fires when right click the marker.
26731          * @param {Roo.bootstrap.LocationPicker} this
26732          * @param {Map event} e
26733          */
26734         markerRightClick : true,
26735         /**
26736          * @event OverlayViewDraw
26737          * Fires when OverlayView Draw
26738          * @param {Roo.bootstrap.LocationPicker} this
26739          */
26740         OverlayViewDraw : true,
26741         /**
26742          * @event OverlayViewOnAdd
26743          * Fires when OverlayView Draw
26744          * @param {Roo.bootstrap.LocationPicker} this
26745          */
26746         OverlayViewOnAdd : true,
26747         /**
26748          * @event OverlayViewOnRemove
26749          * Fires when OverlayView Draw
26750          * @param {Roo.bootstrap.LocationPicker} this
26751          */
26752         OverlayViewOnRemove : true,
26753         /**
26754          * @event OverlayViewShow
26755          * Fires when OverlayView Draw
26756          * @param {Roo.bootstrap.LocationPicker} this
26757          * @param {Pixel} cpx
26758          */
26759         OverlayViewShow : true,
26760         /**
26761          * @event OverlayViewHide
26762          * Fires when OverlayView Draw
26763          * @param {Roo.bootstrap.LocationPicker} this
26764          */
26765         OverlayViewHide : true,
26766         /**
26767          * @event loadexception
26768          * Fires when load google lib failed.
26769          * @param {Roo.bootstrap.LocationPicker} this
26770          */
26771         loadexception : true
26772     });
26773         
26774 };
26775
26776 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26777     
26778     gMapContext: false,
26779     
26780     latitude: 0,
26781     longitude: 0,
26782     zoom: 15,
26783     mapTypeId: false,
26784     mapTypeControl: false,
26785     disableDoubleClickZoom: false,
26786     scrollwheel: true,
26787     streetViewControl: false,
26788     radius: 0,
26789     locationName: '',
26790     draggable: true,
26791     enableAutocomplete: false,
26792     enableReverseGeocode: true,
26793     markerTitle: '',
26794     
26795     getAutoCreate: function()
26796     {
26797
26798         var cfg = {
26799             tag: 'div',
26800             cls: 'roo-location-picker'
26801         };
26802         
26803         return cfg
26804     },
26805     
26806     initEvents: function(ct, position)
26807     {       
26808         if(!this.el.getWidth() || this.isApplied()){
26809             return;
26810         }
26811         
26812         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26813         
26814         this.initial();
26815     },
26816     
26817     initial: function()
26818     {
26819         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26820             this.fireEvent('loadexception', this);
26821             return;
26822         }
26823         
26824         if(!this.mapTypeId){
26825             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26826         }
26827         
26828         this.gMapContext = this.GMapContext();
26829         
26830         this.initOverlayView();
26831         
26832         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26833         
26834         var _this = this;
26835                 
26836         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26837             _this.setPosition(_this.gMapContext.marker.position);
26838         });
26839         
26840         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26841             _this.fireEvent('mapClick', this, event);
26842             
26843         });
26844
26845         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26846             _this.fireEvent('mapRightClick', this, event);
26847             
26848         });
26849         
26850         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26851             _this.fireEvent('markerClick', this, event);
26852             
26853         });
26854
26855         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26856             _this.fireEvent('markerRightClick', this, event);
26857             
26858         });
26859         
26860         this.setPosition(this.gMapContext.location);
26861         
26862         this.fireEvent('initial', this, this.gMapContext.location);
26863     },
26864     
26865     initOverlayView: function()
26866     {
26867         var _this = this;
26868         
26869         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26870             
26871             draw: function()
26872             {
26873                 _this.fireEvent('OverlayViewDraw', _this);
26874             },
26875             
26876             onAdd: function()
26877             {
26878                 _this.fireEvent('OverlayViewOnAdd', _this);
26879             },
26880             
26881             onRemove: function()
26882             {
26883                 _this.fireEvent('OverlayViewOnRemove', _this);
26884             },
26885             
26886             show: function(cpx)
26887             {
26888                 _this.fireEvent('OverlayViewShow', _this, cpx);
26889             },
26890             
26891             hide: function()
26892             {
26893                 _this.fireEvent('OverlayViewHide', _this);
26894             }
26895             
26896         });
26897     },
26898     
26899     fromLatLngToContainerPixel: function(event)
26900     {
26901         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26902     },
26903     
26904     isApplied: function() 
26905     {
26906         return this.getGmapContext() == false ? false : true;
26907     },
26908     
26909     getGmapContext: function() 
26910     {
26911         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26912     },
26913     
26914     GMapContext: function() 
26915     {
26916         var position = new google.maps.LatLng(this.latitude, this.longitude);
26917         
26918         var _map = new google.maps.Map(this.el.dom, {
26919             center: position,
26920             zoom: this.zoom,
26921             mapTypeId: this.mapTypeId,
26922             mapTypeControl: this.mapTypeControl,
26923             disableDoubleClickZoom: this.disableDoubleClickZoom,
26924             scrollwheel: this.scrollwheel,
26925             streetViewControl: this.streetViewControl,
26926             locationName: this.locationName,
26927             draggable: this.draggable,
26928             enableAutocomplete: this.enableAutocomplete,
26929             enableReverseGeocode: this.enableReverseGeocode
26930         });
26931         
26932         var _marker = new google.maps.Marker({
26933             position: position,
26934             map: _map,
26935             title: this.markerTitle,
26936             draggable: this.draggable
26937         });
26938         
26939         return {
26940             map: _map,
26941             marker: _marker,
26942             circle: null,
26943             location: position,
26944             radius: this.radius,
26945             locationName: this.locationName,
26946             addressComponents: {
26947                 formatted_address: null,
26948                 addressLine1: null,
26949                 addressLine2: null,
26950                 streetName: null,
26951                 streetNumber: null,
26952                 city: null,
26953                 district: null,
26954                 state: null,
26955                 stateOrProvince: null
26956             },
26957             settings: this,
26958             domContainer: this.el.dom,
26959             geodecoder: new google.maps.Geocoder()
26960         };
26961     },
26962     
26963     drawCircle: function(center, radius, options) 
26964     {
26965         if (this.gMapContext.circle != null) {
26966             this.gMapContext.circle.setMap(null);
26967         }
26968         if (radius > 0) {
26969             radius *= 1;
26970             options = Roo.apply({}, options, {
26971                 strokeColor: "#0000FF",
26972                 strokeOpacity: .35,
26973                 strokeWeight: 2,
26974                 fillColor: "#0000FF",
26975                 fillOpacity: .2
26976             });
26977             
26978             options.map = this.gMapContext.map;
26979             options.radius = radius;
26980             options.center = center;
26981             this.gMapContext.circle = new google.maps.Circle(options);
26982             return this.gMapContext.circle;
26983         }
26984         
26985         return null;
26986     },
26987     
26988     setPosition: function(location) 
26989     {
26990         this.gMapContext.location = location;
26991         this.gMapContext.marker.setPosition(location);
26992         this.gMapContext.map.panTo(location);
26993         this.drawCircle(location, this.gMapContext.radius, {});
26994         
26995         var _this = this;
26996         
26997         if (this.gMapContext.settings.enableReverseGeocode) {
26998             this.gMapContext.geodecoder.geocode({
26999                 latLng: this.gMapContext.location
27000             }, function(results, status) {
27001                 
27002                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27003                     _this.gMapContext.locationName = results[0].formatted_address;
27004                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27005                     
27006                     _this.fireEvent('positionchanged', this, location);
27007                 }
27008             });
27009             
27010             return;
27011         }
27012         
27013         this.fireEvent('positionchanged', this, location);
27014     },
27015     
27016     resize: function()
27017     {
27018         google.maps.event.trigger(this.gMapContext.map, "resize");
27019         
27020         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27021         
27022         this.fireEvent('resize', this);
27023     },
27024     
27025     setPositionByLatLng: function(latitude, longitude)
27026     {
27027         this.setPosition(new google.maps.LatLng(latitude, longitude));
27028     },
27029     
27030     getCurrentPosition: function() 
27031     {
27032         return {
27033             latitude: this.gMapContext.location.lat(),
27034             longitude: this.gMapContext.location.lng()
27035         };
27036     },
27037     
27038     getAddressName: function() 
27039     {
27040         return this.gMapContext.locationName;
27041     },
27042     
27043     getAddressComponents: function() 
27044     {
27045         return this.gMapContext.addressComponents;
27046     },
27047     
27048     address_component_from_google_geocode: function(address_components) 
27049     {
27050         var result = {};
27051         
27052         for (var i = 0; i < address_components.length; i++) {
27053             var component = address_components[i];
27054             if (component.types.indexOf("postal_code") >= 0) {
27055                 result.postalCode = component.short_name;
27056             } else if (component.types.indexOf("street_number") >= 0) {
27057                 result.streetNumber = component.short_name;
27058             } else if (component.types.indexOf("route") >= 0) {
27059                 result.streetName = component.short_name;
27060             } else if (component.types.indexOf("neighborhood") >= 0) {
27061                 result.city = component.short_name;
27062             } else if (component.types.indexOf("locality") >= 0) {
27063                 result.city = component.short_name;
27064             } else if (component.types.indexOf("sublocality") >= 0) {
27065                 result.district = component.short_name;
27066             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27067                 result.stateOrProvince = component.short_name;
27068             } else if (component.types.indexOf("country") >= 0) {
27069                 result.country = component.short_name;
27070             }
27071         }
27072         
27073         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27074         result.addressLine2 = "";
27075         return result;
27076     },
27077     
27078     setZoomLevel: function(zoom)
27079     {
27080         this.gMapContext.map.setZoom(zoom);
27081     },
27082     
27083     show: function()
27084     {
27085         if(!this.el){
27086             return;
27087         }
27088         
27089         this.el.show();
27090         
27091         this.resize();
27092         
27093         this.fireEvent('show', this);
27094     },
27095     
27096     hide: function()
27097     {
27098         if(!this.el){
27099             return;
27100         }
27101         
27102         this.el.hide();
27103         
27104         this.fireEvent('hide', this);
27105     }
27106     
27107 });
27108
27109 Roo.apply(Roo.bootstrap.LocationPicker, {
27110     
27111     OverlayView : function(map, options)
27112     {
27113         options = options || {};
27114         
27115         this.setMap(map);
27116     }
27117     
27118     
27119 });/*
27120  * - LGPL
27121  *
27122  * Alert
27123  * 
27124  */
27125
27126 /**
27127  * @class Roo.bootstrap.Alert
27128  * @extends Roo.bootstrap.Component
27129  * Bootstrap Alert class
27130  * @cfg {String} title The title of alert
27131  * @cfg {String} html The content of alert
27132  * @cfg {String} weight (  success | info | warning | danger )
27133  * @cfg {String} faicon font-awesomeicon
27134  * 
27135  * @constructor
27136  * Create a new alert
27137  * @param {Object} config The config object
27138  */
27139
27140
27141 Roo.bootstrap.Alert = function(config){
27142     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27143     
27144 };
27145
27146 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27147     
27148     title: '',
27149     html: '',
27150     weight: false,
27151     faicon: false,
27152     
27153     getAutoCreate : function()
27154     {
27155         
27156         var cfg = {
27157             tag : 'div',
27158             cls : 'alert',
27159             cn : [
27160                 {
27161                     tag : 'i',
27162                     cls : 'roo-alert-icon'
27163                     
27164                 },
27165                 {
27166                     tag : 'b',
27167                     cls : 'roo-alert-title',
27168                     html : this.title
27169                 },
27170                 {
27171                     tag : 'span',
27172                     cls : 'roo-alert-text',
27173                     html : this.html
27174                 }
27175             ]
27176         };
27177         
27178         if(this.faicon){
27179             cfg.cn[0].cls += ' fa ' + this.faicon;
27180         }
27181         
27182         if(this.weight){
27183             cfg.cls += ' alert-' + this.weight;
27184         }
27185         
27186         return cfg;
27187     },
27188     
27189     initEvents: function() 
27190     {
27191         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27192     },
27193     
27194     setTitle : function(str)
27195     {
27196         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27197     },
27198     
27199     setText : function(str)
27200     {
27201         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27202     },
27203     
27204     setWeight : function(weight)
27205     {
27206         if(this.weight){
27207             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27208         }
27209         
27210         this.weight = weight;
27211         
27212         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27213     },
27214     
27215     setIcon : function(icon)
27216     {
27217         if(this.faicon){
27218             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27219         }
27220         
27221         this.faicon = icon;
27222         
27223         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27224     },
27225     
27226     hide: function() 
27227     {
27228         this.el.hide();   
27229     },
27230     
27231     show: function() 
27232     {  
27233         this.el.show();   
27234     }
27235     
27236 });
27237
27238  
27239 /*
27240 * Licence: LGPL
27241 */
27242
27243 /**
27244  * @class Roo.bootstrap.UploadCropbox
27245  * @extends Roo.bootstrap.Component
27246  * Bootstrap UploadCropbox class
27247  * @cfg {String} emptyText show when image has been loaded
27248  * @cfg {String} rotateNotify show when image too small to rotate
27249  * @cfg {Number} errorTimeout default 3000
27250  * @cfg {Number} minWidth default 300
27251  * @cfg {Number} minHeight default 300
27252  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27253  * @cfg {Boolean} isDocument (true|false) default false
27254  * @cfg {String} url action url
27255  * @cfg {String} paramName default 'imageUpload'
27256  * @cfg {String} method default POST
27257  * @cfg {Boolean} loadMask (true|false) default true
27258  * @cfg {Boolean} loadingText default 'Loading...'
27259  * 
27260  * @constructor
27261  * Create a new UploadCropbox
27262  * @param {Object} config The config object
27263  */
27264
27265 Roo.bootstrap.UploadCropbox = function(config){
27266     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27267     
27268     this.addEvents({
27269         /**
27270          * @event beforeselectfile
27271          * Fire before select file
27272          * @param {Roo.bootstrap.UploadCropbox} this
27273          */
27274         "beforeselectfile" : true,
27275         /**
27276          * @event initial
27277          * Fire after initEvent
27278          * @param {Roo.bootstrap.UploadCropbox} this
27279          */
27280         "initial" : true,
27281         /**
27282          * @event crop
27283          * Fire after initEvent
27284          * @param {Roo.bootstrap.UploadCropbox} this
27285          * @param {String} data
27286          */
27287         "crop" : true,
27288         /**
27289          * @event prepare
27290          * Fire when preparing the file data
27291          * @param {Roo.bootstrap.UploadCropbox} this
27292          * @param {Object} file
27293          */
27294         "prepare" : true,
27295         /**
27296          * @event exception
27297          * Fire when get exception
27298          * @param {Roo.bootstrap.UploadCropbox} this
27299          * @param {XMLHttpRequest} xhr
27300          */
27301         "exception" : true,
27302         /**
27303          * @event beforeloadcanvas
27304          * Fire before load the canvas
27305          * @param {Roo.bootstrap.UploadCropbox} this
27306          * @param {String} src
27307          */
27308         "beforeloadcanvas" : true,
27309         /**
27310          * @event trash
27311          * Fire when trash image
27312          * @param {Roo.bootstrap.UploadCropbox} this
27313          */
27314         "trash" : true,
27315         /**
27316          * @event download
27317          * Fire when download the image
27318          * @param {Roo.bootstrap.UploadCropbox} this
27319          */
27320         "download" : true,
27321         /**
27322          * @event footerbuttonclick
27323          * Fire when footerbuttonclick
27324          * @param {Roo.bootstrap.UploadCropbox} this
27325          * @param {String} type
27326          */
27327         "footerbuttonclick" : true,
27328         /**
27329          * @event resize
27330          * Fire when resize
27331          * @param {Roo.bootstrap.UploadCropbox} this
27332          */
27333         "resize" : true,
27334         /**
27335          * @event rotate
27336          * Fire when rotate the image
27337          * @param {Roo.bootstrap.UploadCropbox} this
27338          * @param {String} pos
27339          */
27340         "rotate" : true,
27341         /**
27342          * @event inspect
27343          * Fire when inspect the file
27344          * @param {Roo.bootstrap.UploadCropbox} this
27345          * @param {Object} file
27346          */
27347         "inspect" : true,
27348         /**
27349          * @event upload
27350          * Fire when xhr upload the file
27351          * @param {Roo.bootstrap.UploadCropbox} this
27352          * @param {Object} data
27353          */
27354         "upload" : true,
27355         /**
27356          * @event arrange
27357          * Fire when arrange the file data
27358          * @param {Roo.bootstrap.UploadCropbox} this
27359          * @param {Object} formData
27360          */
27361         "arrange" : true
27362     });
27363     
27364     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27365 };
27366
27367 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27368     
27369     emptyText : 'Click to upload image',
27370     rotateNotify : 'Image is too small to rotate',
27371     errorTimeout : 3000,
27372     scale : 0,
27373     baseScale : 1,
27374     rotate : 0,
27375     dragable : false,
27376     pinching : false,
27377     mouseX : 0,
27378     mouseY : 0,
27379     cropData : false,
27380     minWidth : 300,
27381     minHeight : 300,
27382     file : false,
27383     exif : {},
27384     baseRotate : 1,
27385     cropType : 'image/jpeg',
27386     buttons : false,
27387     canvasLoaded : false,
27388     isDocument : false,
27389     method : 'POST',
27390     paramName : 'imageUpload',
27391     loadMask : true,
27392     loadingText : 'Loading...',
27393     maskEl : false,
27394     
27395     getAutoCreate : function()
27396     {
27397         var cfg = {
27398             tag : 'div',
27399             cls : 'roo-upload-cropbox',
27400             cn : [
27401                 {
27402                     tag : 'input',
27403                     cls : 'roo-upload-cropbox-selector',
27404                     type : 'file'
27405                 },
27406                 {
27407                     tag : 'div',
27408                     cls : 'roo-upload-cropbox-body',
27409                     style : 'cursor:pointer',
27410                     cn : [
27411                         {
27412                             tag : 'div',
27413                             cls : 'roo-upload-cropbox-preview'
27414                         },
27415                         {
27416                             tag : 'div',
27417                             cls : 'roo-upload-cropbox-thumb'
27418                         },
27419                         {
27420                             tag : 'div',
27421                             cls : 'roo-upload-cropbox-empty-notify',
27422                             html : this.emptyText
27423                         },
27424                         {
27425                             tag : 'div',
27426                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27427                             html : this.rotateNotify
27428                         }
27429                     ]
27430                 },
27431                 {
27432                     tag : 'div',
27433                     cls : 'roo-upload-cropbox-footer',
27434                     cn : {
27435                         tag : 'div',
27436                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27437                         cn : []
27438                     }
27439                 }
27440             ]
27441         };
27442         
27443         return cfg;
27444     },
27445     
27446     onRender : function(ct, position)
27447     {
27448         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27449         
27450         if (this.buttons.length) {
27451             
27452             Roo.each(this.buttons, function(bb) {
27453                 
27454                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27455                 
27456                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27457                 
27458             }, this);
27459         }
27460         
27461         if(this.loadMask){
27462             this.maskEl = this.el;
27463         }
27464     },
27465     
27466     initEvents : function()
27467     {
27468         this.urlAPI = (window.createObjectURL && window) || 
27469                                 (window.URL && URL.revokeObjectURL && URL) || 
27470                                 (window.webkitURL && webkitURL);
27471                         
27472         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27473         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27474         
27475         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27476         this.selectorEl.hide();
27477         
27478         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27479         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27480         
27481         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27482         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27483         this.thumbEl.hide();
27484         
27485         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27486         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27487         
27488         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27489         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27490         this.errorEl.hide();
27491         
27492         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27493         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27494         this.footerEl.hide();
27495         
27496         this.setThumbBoxSize();
27497         
27498         this.bind();
27499         
27500         this.resize();
27501         
27502         this.fireEvent('initial', this);
27503     },
27504
27505     bind : function()
27506     {
27507         var _this = this;
27508         
27509         window.addEventListener("resize", function() { _this.resize(); } );
27510         
27511         this.bodyEl.on('click', this.beforeSelectFile, this);
27512         
27513         if(Roo.isTouch){
27514             this.bodyEl.on('touchstart', this.onTouchStart, this);
27515             this.bodyEl.on('touchmove', this.onTouchMove, this);
27516             this.bodyEl.on('touchend', this.onTouchEnd, this);
27517         }
27518         
27519         if(!Roo.isTouch){
27520             this.bodyEl.on('mousedown', this.onMouseDown, this);
27521             this.bodyEl.on('mousemove', this.onMouseMove, this);
27522             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27523             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27524             Roo.get(document).on('mouseup', this.onMouseUp, this);
27525         }
27526         
27527         this.selectorEl.on('change', this.onFileSelected, this);
27528     },
27529     
27530     reset : function()
27531     {    
27532         this.scale = 0;
27533         this.baseScale = 1;
27534         this.rotate = 0;
27535         this.baseRotate = 1;
27536         this.dragable = false;
27537         this.pinching = false;
27538         this.mouseX = 0;
27539         this.mouseY = 0;
27540         this.cropData = false;
27541         this.notifyEl.dom.innerHTML = this.emptyText;
27542         
27543         this.selectorEl.dom.value = '';
27544         
27545     },
27546     
27547     resize : function()
27548     {
27549         if(this.fireEvent('resize', this) != false){
27550             this.setThumbBoxPosition();
27551             this.setCanvasPosition();
27552         }
27553     },
27554     
27555     onFooterButtonClick : function(e, el, o, type)
27556     {
27557         switch (type) {
27558             case 'rotate-left' :
27559                 this.onRotateLeft(e);
27560                 break;
27561             case 'rotate-right' :
27562                 this.onRotateRight(e);
27563                 break;
27564             case 'picture' :
27565                 this.beforeSelectFile(e);
27566                 break;
27567             case 'trash' :
27568                 this.trash(e);
27569                 break;
27570             case 'crop' :
27571                 this.crop(e);
27572                 break;
27573             case 'download' :
27574                 this.download(e);
27575                 break;
27576             default :
27577                 break;
27578         }
27579         
27580         this.fireEvent('footerbuttonclick', this, type);
27581     },
27582     
27583     beforeSelectFile : function(e)
27584     {
27585         e.preventDefault();
27586         
27587         if(this.fireEvent('beforeselectfile', this) != false){
27588             this.selectorEl.dom.click();
27589         }
27590     },
27591     
27592     onFileSelected : function(e)
27593     {
27594         e.preventDefault();
27595         
27596         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27597             return;
27598         }
27599         
27600         var file = this.selectorEl.dom.files[0];
27601         
27602         if(this.fireEvent('inspect', this, file) != false){
27603             this.prepare(file);
27604         }
27605         
27606     },
27607     
27608     trash : function(e)
27609     {
27610         this.fireEvent('trash', this);
27611     },
27612     
27613     download : function(e)
27614     {
27615         this.fireEvent('download', this);
27616     },
27617     
27618     loadCanvas : function(src)
27619     {   
27620         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27621             
27622             this.reset();
27623             
27624             this.imageEl = document.createElement('img');
27625             
27626             var _this = this;
27627             
27628             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27629             
27630             this.imageEl.src = src;
27631         }
27632     },
27633     
27634     onLoadCanvas : function()
27635     {   
27636         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27637         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27638         
27639         this.bodyEl.un('click', this.beforeSelectFile, this);
27640         
27641         this.notifyEl.hide();
27642         this.thumbEl.show();
27643         this.footerEl.show();
27644         
27645         this.baseRotateLevel();
27646         
27647         if(this.isDocument){
27648             this.setThumbBoxSize();
27649         }
27650         
27651         this.setThumbBoxPosition();
27652         
27653         this.baseScaleLevel();
27654         
27655         this.draw();
27656         
27657         this.resize();
27658         
27659         this.canvasLoaded = true;
27660         
27661         if(this.loadMask){
27662             this.maskEl.unmask();
27663         }
27664         
27665     },
27666     
27667     setCanvasPosition : function()
27668     {   
27669         if(!this.canvasEl){
27670             return;
27671         }
27672         
27673         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27674         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27675         
27676         this.previewEl.setLeft(pw);
27677         this.previewEl.setTop(ph);
27678         
27679     },
27680     
27681     onMouseDown : function(e)
27682     {   
27683         e.stopEvent();
27684         
27685         this.dragable = true;
27686         this.pinching = false;
27687         
27688         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27689             this.dragable = false;
27690             return;
27691         }
27692         
27693         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27694         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27695         
27696     },
27697     
27698     onMouseMove : function(e)
27699     {   
27700         e.stopEvent();
27701         
27702         if(!this.canvasLoaded){
27703             return;
27704         }
27705         
27706         if (!this.dragable){
27707             return;
27708         }
27709         
27710         var minX = Math.ceil(this.thumbEl.getLeft(true));
27711         var minY = Math.ceil(this.thumbEl.getTop(true));
27712         
27713         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27714         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27715         
27716         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27717         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27718         
27719         x = x - this.mouseX;
27720         y = y - this.mouseY;
27721         
27722         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27723         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27724         
27725         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27726         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27727         
27728         this.previewEl.setLeft(bgX);
27729         this.previewEl.setTop(bgY);
27730         
27731         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27732         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27733     },
27734     
27735     onMouseUp : function(e)
27736     {   
27737         e.stopEvent();
27738         
27739         this.dragable = false;
27740     },
27741     
27742     onMouseWheel : function(e)
27743     {   
27744         e.stopEvent();
27745         
27746         this.startScale = this.scale;
27747         
27748         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27749         
27750         if(!this.zoomable()){
27751             this.scale = this.startScale;
27752             return;
27753         }
27754         
27755         this.draw();
27756         
27757         return;
27758     },
27759     
27760     zoomable : function()
27761     {
27762         var minScale = this.thumbEl.getWidth() / this.minWidth;
27763         
27764         if(this.minWidth < this.minHeight){
27765             minScale = this.thumbEl.getHeight() / this.minHeight;
27766         }
27767         
27768         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27769         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27770         
27771         if(
27772                 this.isDocument &&
27773                 (this.rotate == 0 || this.rotate == 180) && 
27774                 (
27775                     width > this.imageEl.OriginWidth || 
27776                     height > this.imageEl.OriginHeight ||
27777                     (width < this.minWidth && height < this.minHeight)
27778                 )
27779         ){
27780             return false;
27781         }
27782         
27783         if(
27784                 this.isDocument &&
27785                 (this.rotate == 90 || this.rotate == 270) && 
27786                 (
27787                     width > this.imageEl.OriginWidth || 
27788                     height > this.imageEl.OriginHeight ||
27789                     (width < this.minHeight && height < this.minWidth)
27790                 )
27791         ){
27792             return false;
27793         }
27794         
27795         if(
27796                 !this.isDocument &&
27797                 (this.rotate == 0 || this.rotate == 180) && 
27798                 (
27799                     width < this.minWidth || 
27800                     width > this.imageEl.OriginWidth || 
27801                     height < this.minHeight || 
27802                     height > this.imageEl.OriginHeight
27803                 )
27804         ){
27805             return false;
27806         }
27807         
27808         if(
27809                 !this.isDocument &&
27810                 (this.rotate == 90 || this.rotate == 270) && 
27811                 (
27812                     width < this.minHeight || 
27813                     width > this.imageEl.OriginWidth || 
27814                     height < this.minWidth || 
27815                     height > this.imageEl.OriginHeight
27816                 )
27817         ){
27818             return false;
27819         }
27820         
27821         return true;
27822         
27823     },
27824     
27825     onRotateLeft : function(e)
27826     {   
27827         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27828             
27829             var minScale = this.thumbEl.getWidth() / this.minWidth;
27830             
27831             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27832             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27833             
27834             this.startScale = this.scale;
27835             
27836             while (this.getScaleLevel() < minScale){
27837             
27838                 this.scale = this.scale + 1;
27839                 
27840                 if(!this.zoomable()){
27841                     break;
27842                 }
27843                 
27844                 if(
27845                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27846                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27847                 ){
27848                     continue;
27849                 }
27850                 
27851                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27852
27853                 this.draw();
27854                 
27855                 return;
27856             }
27857             
27858             this.scale = this.startScale;
27859             
27860             this.onRotateFail();
27861             
27862             return false;
27863         }
27864         
27865         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27866
27867         if(this.isDocument){
27868             this.setThumbBoxSize();
27869             this.setThumbBoxPosition();
27870             this.setCanvasPosition();
27871         }
27872         
27873         this.draw();
27874         
27875         this.fireEvent('rotate', this, 'left');
27876         
27877     },
27878     
27879     onRotateRight : function(e)
27880     {
27881         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27882             
27883             var minScale = this.thumbEl.getWidth() / this.minWidth;
27884         
27885             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27886             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27887             
27888             this.startScale = this.scale;
27889             
27890             while (this.getScaleLevel() < minScale){
27891             
27892                 this.scale = this.scale + 1;
27893                 
27894                 if(!this.zoomable()){
27895                     break;
27896                 }
27897                 
27898                 if(
27899                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27900                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27901                 ){
27902                     continue;
27903                 }
27904                 
27905                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27906
27907                 this.draw();
27908                 
27909                 return;
27910             }
27911             
27912             this.scale = this.startScale;
27913             
27914             this.onRotateFail();
27915             
27916             return false;
27917         }
27918         
27919         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27920
27921         if(this.isDocument){
27922             this.setThumbBoxSize();
27923             this.setThumbBoxPosition();
27924             this.setCanvasPosition();
27925         }
27926         
27927         this.draw();
27928         
27929         this.fireEvent('rotate', this, 'right');
27930     },
27931     
27932     onRotateFail : function()
27933     {
27934         this.errorEl.show(true);
27935         
27936         var _this = this;
27937         
27938         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27939     },
27940     
27941     draw : function()
27942     {
27943         this.previewEl.dom.innerHTML = '';
27944         
27945         var canvasEl = document.createElement("canvas");
27946         
27947         var contextEl = canvasEl.getContext("2d");
27948         
27949         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27950         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27951         var center = this.imageEl.OriginWidth / 2;
27952         
27953         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27954             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27955             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27956             center = this.imageEl.OriginHeight / 2;
27957         }
27958         
27959         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27960         
27961         contextEl.translate(center, center);
27962         contextEl.rotate(this.rotate * Math.PI / 180);
27963
27964         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27965         
27966         this.canvasEl = document.createElement("canvas");
27967         
27968         this.contextEl = this.canvasEl.getContext("2d");
27969         
27970         switch (this.rotate) {
27971             case 0 :
27972                 
27973                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27974                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27975                 
27976                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27977                 
27978                 break;
27979             case 90 : 
27980                 
27981                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27982                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27983                 
27984                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27985                     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);
27986                     break;
27987                 }
27988                 
27989                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27990                 
27991                 break;
27992             case 180 :
27993                 
27994                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27995                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27996                 
27997                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27998                     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);
27999                     break;
28000                 }
28001                 
28002                 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);
28003                 
28004                 break;
28005             case 270 :
28006                 
28007                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28008                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28009         
28010                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28011                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28012                     break;
28013                 }
28014                 
28015                 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);
28016                 
28017                 break;
28018             default : 
28019                 break;
28020         }
28021         
28022         this.previewEl.appendChild(this.canvasEl);
28023         
28024         this.setCanvasPosition();
28025     },
28026     
28027     crop : function()
28028     {
28029         if(!this.canvasLoaded){
28030             return;
28031         }
28032         
28033         var imageCanvas = document.createElement("canvas");
28034         
28035         var imageContext = imageCanvas.getContext("2d");
28036         
28037         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28038         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28039         
28040         var center = imageCanvas.width / 2;
28041         
28042         imageContext.translate(center, center);
28043         
28044         imageContext.rotate(this.rotate * Math.PI / 180);
28045         
28046         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28047         
28048         var canvas = document.createElement("canvas");
28049         
28050         var context = canvas.getContext("2d");
28051                 
28052         canvas.width = this.minWidth;
28053         canvas.height = this.minHeight;
28054
28055         switch (this.rotate) {
28056             case 0 :
28057                 
28058                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28059                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28060                 
28061                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28062                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28063                 
28064                 var targetWidth = this.minWidth - 2 * x;
28065                 var targetHeight = this.minHeight - 2 * y;
28066                 
28067                 var scale = 1;
28068                 
28069                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28070                     scale = targetWidth / width;
28071                 }
28072                 
28073                 if(x > 0 && y == 0){
28074                     scale = targetHeight / height;
28075                 }
28076                 
28077                 if(x > 0 && y > 0){
28078                     scale = targetWidth / width;
28079                     
28080                     if(width < height){
28081                         scale = targetHeight / height;
28082                     }
28083                 }
28084                 
28085                 context.scale(scale, scale);
28086                 
28087                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28088                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28089
28090                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28091                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28092
28093                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28094                 
28095                 break;
28096             case 90 : 
28097                 
28098                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28099                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28100                 
28101                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28102                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28103                 
28104                 var targetWidth = this.minWidth - 2 * x;
28105                 var targetHeight = this.minHeight - 2 * y;
28106                 
28107                 var scale = 1;
28108                 
28109                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28110                     scale = targetWidth / width;
28111                 }
28112                 
28113                 if(x > 0 && y == 0){
28114                     scale = targetHeight / height;
28115                 }
28116                 
28117                 if(x > 0 && y > 0){
28118                     scale = targetWidth / width;
28119                     
28120                     if(width < height){
28121                         scale = targetHeight / height;
28122                     }
28123                 }
28124                 
28125                 context.scale(scale, scale);
28126                 
28127                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28128                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28129
28130                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28131                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28132                 
28133                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28134                 
28135                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28136                 
28137                 break;
28138             case 180 :
28139                 
28140                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28141                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28142                 
28143                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28144                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28145                 
28146                 var targetWidth = this.minWidth - 2 * x;
28147                 var targetHeight = this.minHeight - 2 * y;
28148                 
28149                 var scale = 1;
28150                 
28151                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28152                     scale = targetWidth / width;
28153                 }
28154                 
28155                 if(x > 0 && y == 0){
28156                     scale = targetHeight / height;
28157                 }
28158                 
28159                 if(x > 0 && y > 0){
28160                     scale = targetWidth / width;
28161                     
28162                     if(width < height){
28163                         scale = targetHeight / height;
28164                     }
28165                 }
28166                 
28167                 context.scale(scale, scale);
28168                 
28169                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28170                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28171
28172                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28173                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28174
28175                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28176                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28177                 
28178                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28179                 
28180                 break;
28181             case 270 :
28182                 
28183                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28184                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28185                 
28186                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28187                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28188                 
28189                 var targetWidth = this.minWidth - 2 * x;
28190                 var targetHeight = this.minHeight - 2 * y;
28191                 
28192                 var scale = 1;
28193                 
28194                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28195                     scale = targetWidth / width;
28196                 }
28197                 
28198                 if(x > 0 && y == 0){
28199                     scale = targetHeight / height;
28200                 }
28201                 
28202                 if(x > 0 && y > 0){
28203                     scale = targetWidth / width;
28204                     
28205                     if(width < height){
28206                         scale = targetHeight / height;
28207                     }
28208                 }
28209                 
28210                 context.scale(scale, scale);
28211                 
28212                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28213                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28214
28215                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28216                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28217                 
28218                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28219                 
28220                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28221                 
28222                 break;
28223             default : 
28224                 break;
28225         }
28226         
28227         this.cropData = canvas.toDataURL(this.cropType);
28228         
28229         if(this.fireEvent('crop', this, this.cropData) !== false){
28230             this.process(this.file, this.cropData);
28231         }
28232         
28233         return;
28234         
28235     },
28236     
28237     setThumbBoxSize : function()
28238     {
28239         var width, height;
28240         
28241         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28242             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28243             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28244             
28245             this.minWidth = width;
28246             this.minHeight = height;
28247             
28248             if(this.rotate == 90 || this.rotate == 270){
28249                 this.minWidth = height;
28250                 this.minHeight = width;
28251             }
28252         }
28253         
28254         height = 300;
28255         width = Math.ceil(this.minWidth * height / this.minHeight);
28256         
28257         if(this.minWidth > this.minHeight){
28258             width = 300;
28259             height = Math.ceil(this.minHeight * width / this.minWidth);
28260         }
28261         
28262         this.thumbEl.setStyle({
28263             width : width + 'px',
28264             height : height + 'px'
28265         });
28266
28267         return;
28268             
28269     },
28270     
28271     setThumbBoxPosition : function()
28272     {
28273         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28274         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28275         
28276         this.thumbEl.setLeft(x);
28277         this.thumbEl.setTop(y);
28278         
28279     },
28280     
28281     baseRotateLevel : function()
28282     {
28283         this.baseRotate = 1;
28284         
28285         if(
28286                 typeof(this.exif) != 'undefined' &&
28287                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28288                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28289         ){
28290             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28291         }
28292         
28293         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28294         
28295     },
28296     
28297     baseScaleLevel : function()
28298     {
28299         var width, height;
28300         
28301         if(this.isDocument){
28302             
28303             if(this.baseRotate == 6 || this.baseRotate == 8){
28304             
28305                 height = this.thumbEl.getHeight();
28306                 this.baseScale = height / this.imageEl.OriginWidth;
28307
28308                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28309                     width = this.thumbEl.getWidth();
28310                     this.baseScale = width / this.imageEl.OriginHeight;
28311                 }
28312
28313                 return;
28314             }
28315
28316             height = this.thumbEl.getHeight();
28317             this.baseScale = height / this.imageEl.OriginHeight;
28318
28319             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28320                 width = this.thumbEl.getWidth();
28321                 this.baseScale = width / this.imageEl.OriginWidth;
28322             }
28323
28324             return;
28325         }
28326         
28327         if(this.baseRotate == 6 || this.baseRotate == 8){
28328             
28329             width = this.thumbEl.getHeight();
28330             this.baseScale = width / this.imageEl.OriginHeight;
28331             
28332             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28333                 height = this.thumbEl.getWidth();
28334                 this.baseScale = height / this.imageEl.OriginHeight;
28335             }
28336             
28337             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28338                 height = this.thumbEl.getWidth();
28339                 this.baseScale = height / this.imageEl.OriginHeight;
28340                 
28341                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28342                     width = this.thumbEl.getHeight();
28343                     this.baseScale = width / this.imageEl.OriginWidth;
28344                 }
28345             }
28346             
28347             return;
28348         }
28349         
28350         width = this.thumbEl.getWidth();
28351         this.baseScale = width / this.imageEl.OriginWidth;
28352         
28353         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28354             height = this.thumbEl.getHeight();
28355             this.baseScale = height / this.imageEl.OriginHeight;
28356         }
28357         
28358         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28359             
28360             height = this.thumbEl.getHeight();
28361             this.baseScale = height / this.imageEl.OriginHeight;
28362             
28363             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28364                 width = this.thumbEl.getWidth();
28365                 this.baseScale = width / this.imageEl.OriginWidth;
28366             }
28367             
28368         }
28369         
28370         return;
28371     },
28372     
28373     getScaleLevel : function()
28374     {
28375         return this.baseScale * Math.pow(1.1, this.scale);
28376     },
28377     
28378     onTouchStart : function(e)
28379     {
28380         if(!this.canvasLoaded){
28381             this.beforeSelectFile(e);
28382             return;
28383         }
28384         
28385         var touches = e.browserEvent.touches;
28386         
28387         if(!touches){
28388             return;
28389         }
28390         
28391         if(touches.length == 1){
28392             this.onMouseDown(e);
28393             return;
28394         }
28395         
28396         if(touches.length != 2){
28397             return;
28398         }
28399         
28400         var coords = [];
28401         
28402         for(var i = 0, finger; finger = touches[i]; i++){
28403             coords.push(finger.pageX, finger.pageY);
28404         }
28405         
28406         var x = Math.pow(coords[0] - coords[2], 2);
28407         var y = Math.pow(coords[1] - coords[3], 2);
28408         
28409         this.startDistance = Math.sqrt(x + y);
28410         
28411         this.startScale = this.scale;
28412         
28413         this.pinching = true;
28414         this.dragable = false;
28415         
28416     },
28417     
28418     onTouchMove : function(e)
28419     {
28420         if(!this.pinching && !this.dragable){
28421             return;
28422         }
28423         
28424         var touches = e.browserEvent.touches;
28425         
28426         if(!touches){
28427             return;
28428         }
28429         
28430         if(this.dragable){
28431             this.onMouseMove(e);
28432             return;
28433         }
28434         
28435         var coords = [];
28436         
28437         for(var i = 0, finger; finger = touches[i]; i++){
28438             coords.push(finger.pageX, finger.pageY);
28439         }
28440         
28441         var x = Math.pow(coords[0] - coords[2], 2);
28442         var y = Math.pow(coords[1] - coords[3], 2);
28443         
28444         this.endDistance = Math.sqrt(x + y);
28445         
28446         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28447         
28448         if(!this.zoomable()){
28449             this.scale = this.startScale;
28450             return;
28451         }
28452         
28453         this.draw();
28454         
28455     },
28456     
28457     onTouchEnd : function(e)
28458     {
28459         this.pinching = false;
28460         this.dragable = false;
28461         
28462     },
28463     
28464     process : function(file, crop)
28465     {
28466         if(this.loadMask){
28467             this.maskEl.mask(this.loadingText);
28468         }
28469         
28470         this.xhr = new XMLHttpRequest();
28471         
28472         file.xhr = this.xhr;
28473
28474         this.xhr.open(this.method, this.url, true);
28475         
28476         var headers = {
28477             "Accept": "application/json",
28478             "Cache-Control": "no-cache",
28479             "X-Requested-With": "XMLHttpRequest"
28480         };
28481         
28482         for (var headerName in headers) {
28483             var headerValue = headers[headerName];
28484             if (headerValue) {
28485                 this.xhr.setRequestHeader(headerName, headerValue);
28486             }
28487         }
28488         
28489         var _this = this;
28490         
28491         this.xhr.onload = function()
28492         {
28493             _this.xhrOnLoad(_this.xhr);
28494         }
28495         
28496         this.xhr.onerror = function()
28497         {
28498             _this.xhrOnError(_this.xhr);
28499         }
28500         
28501         var formData = new FormData();
28502
28503         formData.append('returnHTML', 'NO');
28504         
28505         if(crop){
28506             formData.append('crop', crop);
28507         }
28508         
28509         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28510             formData.append(this.paramName, file, file.name);
28511         }
28512         
28513         if(typeof(file.filename) != 'undefined'){
28514             formData.append('filename', file.filename);
28515         }
28516         
28517         if(typeof(file.mimetype) != 'undefined'){
28518             formData.append('mimetype', file.mimetype);
28519         }
28520         
28521         if(this.fireEvent('arrange', this, formData) != false){
28522             this.xhr.send(formData);
28523         };
28524     },
28525     
28526     xhrOnLoad : function(xhr)
28527     {
28528         if(this.loadMask){
28529             this.maskEl.unmask();
28530         }
28531         
28532         if (xhr.readyState !== 4) {
28533             this.fireEvent('exception', this, xhr);
28534             return;
28535         }
28536
28537         var response = Roo.decode(xhr.responseText);
28538         
28539         if(!response.success){
28540             this.fireEvent('exception', this, xhr);
28541             return;
28542         }
28543         
28544         var response = Roo.decode(xhr.responseText);
28545         
28546         this.fireEvent('upload', this, response);
28547         
28548     },
28549     
28550     xhrOnError : function()
28551     {
28552         if(this.loadMask){
28553             this.maskEl.unmask();
28554         }
28555         
28556         Roo.log('xhr on error');
28557         
28558         var response = Roo.decode(xhr.responseText);
28559           
28560         Roo.log(response);
28561         
28562     },
28563     
28564     prepare : function(file)
28565     {   
28566         if(this.loadMask){
28567             this.maskEl.mask(this.loadingText);
28568         }
28569         
28570         this.file = false;
28571         this.exif = {};
28572         
28573         if(typeof(file) === 'string'){
28574             this.loadCanvas(file);
28575             return;
28576         }
28577         
28578         if(!file || !this.urlAPI){
28579             return;
28580         }
28581         
28582         this.file = file;
28583         this.cropType = file.type;
28584         
28585         var _this = this;
28586         
28587         if(this.fireEvent('prepare', this, this.file) != false){
28588             
28589             var reader = new FileReader();
28590             
28591             reader.onload = function (e) {
28592                 if (e.target.error) {
28593                     Roo.log(e.target.error);
28594                     return;
28595                 }
28596                 
28597                 var buffer = e.target.result,
28598                     dataView = new DataView(buffer),
28599                     offset = 2,
28600                     maxOffset = dataView.byteLength - 4,
28601                     markerBytes,
28602                     markerLength;
28603                 
28604                 if (dataView.getUint16(0) === 0xffd8) {
28605                     while (offset < maxOffset) {
28606                         markerBytes = dataView.getUint16(offset);
28607                         
28608                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28609                             markerLength = dataView.getUint16(offset + 2) + 2;
28610                             if (offset + markerLength > dataView.byteLength) {
28611                                 Roo.log('Invalid meta data: Invalid segment size.');
28612                                 break;
28613                             }
28614                             
28615                             if(markerBytes == 0xffe1){
28616                                 _this.parseExifData(
28617                                     dataView,
28618                                     offset,
28619                                     markerLength
28620                                 );
28621                             }
28622                             
28623                             offset += markerLength;
28624                             
28625                             continue;
28626                         }
28627                         
28628                         break;
28629                     }
28630                     
28631                 }
28632                 
28633                 var url = _this.urlAPI.createObjectURL(_this.file);
28634                 
28635                 _this.loadCanvas(url);
28636                 
28637                 return;
28638             }
28639             
28640             reader.readAsArrayBuffer(this.file);
28641             
28642         }
28643         
28644     },
28645     
28646     parseExifData : function(dataView, offset, length)
28647     {
28648         var tiffOffset = offset + 10,
28649             littleEndian,
28650             dirOffset;
28651     
28652         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28653             // No Exif data, might be XMP data instead
28654             return;
28655         }
28656         
28657         // Check for the ASCII code for "Exif" (0x45786966):
28658         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28659             // No Exif data, might be XMP data instead
28660             return;
28661         }
28662         if (tiffOffset + 8 > dataView.byteLength) {
28663             Roo.log('Invalid Exif data: Invalid segment size.');
28664             return;
28665         }
28666         // Check for the two null bytes:
28667         if (dataView.getUint16(offset + 8) !== 0x0000) {
28668             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28669             return;
28670         }
28671         // Check the byte alignment:
28672         switch (dataView.getUint16(tiffOffset)) {
28673         case 0x4949:
28674             littleEndian = true;
28675             break;
28676         case 0x4D4D:
28677             littleEndian = false;
28678             break;
28679         default:
28680             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28681             return;
28682         }
28683         // Check for the TIFF tag marker (0x002A):
28684         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28685             Roo.log('Invalid Exif data: Missing TIFF marker.');
28686             return;
28687         }
28688         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28689         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28690         
28691         this.parseExifTags(
28692             dataView,
28693             tiffOffset,
28694             tiffOffset + dirOffset,
28695             littleEndian
28696         );
28697     },
28698     
28699     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28700     {
28701         var tagsNumber,
28702             dirEndOffset,
28703             i;
28704         if (dirOffset + 6 > dataView.byteLength) {
28705             Roo.log('Invalid Exif data: Invalid directory offset.');
28706             return;
28707         }
28708         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28709         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28710         if (dirEndOffset + 4 > dataView.byteLength) {
28711             Roo.log('Invalid Exif data: Invalid directory size.');
28712             return;
28713         }
28714         for (i = 0; i < tagsNumber; i += 1) {
28715             this.parseExifTag(
28716                 dataView,
28717                 tiffOffset,
28718                 dirOffset + 2 + 12 * i, // tag offset
28719                 littleEndian
28720             );
28721         }
28722         // Return the offset to the next directory:
28723         return dataView.getUint32(dirEndOffset, littleEndian);
28724     },
28725     
28726     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28727     {
28728         var tag = dataView.getUint16(offset, littleEndian);
28729         
28730         this.exif[tag] = this.getExifValue(
28731             dataView,
28732             tiffOffset,
28733             offset,
28734             dataView.getUint16(offset + 2, littleEndian), // tag type
28735             dataView.getUint32(offset + 4, littleEndian), // tag length
28736             littleEndian
28737         );
28738     },
28739     
28740     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28741     {
28742         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28743             tagSize,
28744             dataOffset,
28745             values,
28746             i,
28747             str,
28748             c;
28749     
28750         if (!tagType) {
28751             Roo.log('Invalid Exif data: Invalid tag type.');
28752             return;
28753         }
28754         
28755         tagSize = tagType.size * length;
28756         // Determine if the value is contained in the dataOffset bytes,
28757         // or if the value at the dataOffset is a pointer to the actual data:
28758         dataOffset = tagSize > 4 ?
28759                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28760         if (dataOffset + tagSize > dataView.byteLength) {
28761             Roo.log('Invalid Exif data: Invalid data offset.');
28762             return;
28763         }
28764         if (length === 1) {
28765             return tagType.getValue(dataView, dataOffset, littleEndian);
28766         }
28767         values = [];
28768         for (i = 0; i < length; i += 1) {
28769             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28770         }
28771         
28772         if (tagType.ascii) {
28773             str = '';
28774             // Concatenate the chars:
28775             for (i = 0; i < values.length; i += 1) {
28776                 c = values[i];
28777                 // Ignore the terminating NULL byte(s):
28778                 if (c === '\u0000') {
28779                     break;
28780                 }
28781                 str += c;
28782             }
28783             return str;
28784         }
28785         return values;
28786     }
28787     
28788 });
28789
28790 Roo.apply(Roo.bootstrap.UploadCropbox, {
28791     tags : {
28792         'Orientation': 0x0112
28793     },
28794     
28795     Orientation: {
28796             1: 0, //'top-left',
28797 //            2: 'top-right',
28798             3: 180, //'bottom-right',
28799 //            4: 'bottom-left',
28800 //            5: 'left-top',
28801             6: 90, //'right-top',
28802 //            7: 'right-bottom',
28803             8: 270 //'left-bottom'
28804     },
28805     
28806     exifTagTypes : {
28807         // byte, 8-bit unsigned int:
28808         1: {
28809             getValue: function (dataView, dataOffset) {
28810                 return dataView.getUint8(dataOffset);
28811             },
28812             size: 1
28813         },
28814         // ascii, 8-bit byte:
28815         2: {
28816             getValue: function (dataView, dataOffset) {
28817                 return String.fromCharCode(dataView.getUint8(dataOffset));
28818             },
28819             size: 1,
28820             ascii: true
28821         },
28822         // short, 16 bit int:
28823         3: {
28824             getValue: function (dataView, dataOffset, littleEndian) {
28825                 return dataView.getUint16(dataOffset, littleEndian);
28826             },
28827             size: 2
28828         },
28829         // long, 32 bit int:
28830         4: {
28831             getValue: function (dataView, dataOffset, littleEndian) {
28832                 return dataView.getUint32(dataOffset, littleEndian);
28833             },
28834             size: 4
28835         },
28836         // rational = two long values, first is numerator, second is denominator:
28837         5: {
28838             getValue: function (dataView, dataOffset, littleEndian) {
28839                 return dataView.getUint32(dataOffset, littleEndian) /
28840                     dataView.getUint32(dataOffset + 4, littleEndian);
28841             },
28842             size: 8
28843         },
28844         // slong, 32 bit signed int:
28845         9: {
28846             getValue: function (dataView, dataOffset, littleEndian) {
28847                 return dataView.getInt32(dataOffset, littleEndian);
28848             },
28849             size: 4
28850         },
28851         // srational, two slongs, first is numerator, second is denominator:
28852         10: {
28853             getValue: function (dataView, dataOffset, littleEndian) {
28854                 return dataView.getInt32(dataOffset, littleEndian) /
28855                     dataView.getInt32(dataOffset + 4, littleEndian);
28856             },
28857             size: 8
28858         }
28859     },
28860     
28861     footer : {
28862         STANDARD : [
28863             {
28864                 tag : 'div',
28865                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28866                 action : 'rotate-left',
28867                 cn : [
28868                     {
28869                         tag : 'button',
28870                         cls : 'btn btn-default',
28871                         html : '<i class="fa fa-undo"></i>'
28872                     }
28873                 ]
28874             },
28875             {
28876                 tag : 'div',
28877                 cls : 'btn-group roo-upload-cropbox-picture',
28878                 action : 'picture',
28879                 cn : [
28880                     {
28881                         tag : 'button',
28882                         cls : 'btn btn-default',
28883                         html : '<i class="fa fa-picture-o"></i>'
28884                     }
28885                 ]
28886             },
28887             {
28888                 tag : 'div',
28889                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28890                 action : 'rotate-right',
28891                 cn : [
28892                     {
28893                         tag : 'button',
28894                         cls : 'btn btn-default',
28895                         html : '<i class="fa fa-repeat"></i>'
28896                     }
28897                 ]
28898             }
28899         ],
28900         DOCUMENT : [
28901             {
28902                 tag : 'div',
28903                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28904                 action : 'rotate-left',
28905                 cn : [
28906                     {
28907                         tag : 'button',
28908                         cls : 'btn btn-default',
28909                         html : '<i class="fa fa-undo"></i>'
28910                     }
28911                 ]
28912             },
28913             {
28914                 tag : 'div',
28915                 cls : 'btn-group roo-upload-cropbox-download',
28916                 action : 'download',
28917                 cn : [
28918                     {
28919                         tag : 'button',
28920                         cls : 'btn btn-default',
28921                         html : '<i class="fa fa-download"></i>'
28922                     }
28923                 ]
28924             },
28925             {
28926                 tag : 'div',
28927                 cls : 'btn-group roo-upload-cropbox-crop',
28928                 action : 'crop',
28929                 cn : [
28930                     {
28931                         tag : 'button',
28932                         cls : 'btn btn-default',
28933                         html : '<i class="fa fa-crop"></i>'
28934                     }
28935                 ]
28936             },
28937             {
28938                 tag : 'div',
28939                 cls : 'btn-group roo-upload-cropbox-trash',
28940                 action : 'trash',
28941                 cn : [
28942                     {
28943                         tag : 'button',
28944                         cls : 'btn btn-default',
28945                         html : '<i class="fa fa-trash"></i>'
28946                     }
28947                 ]
28948             },
28949             {
28950                 tag : 'div',
28951                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28952                 action : 'rotate-right',
28953                 cn : [
28954                     {
28955                         tag : 'button',
28956                         cls : 'btn btn-default',
28957                         html : '<i class="fa fa-repeat"></i>'
28958                     }
28959                 ]
28960             }
28961         ],
28962         ROTATOR : [
28963             {
28964                 tag : 'div',
28965                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28966                 action : 'rotate-left',
28967                 cn : [
28968                     {
28969                         tag : 'button',
28970                         cls : 'btn btn-default',
28971                         html : '<i class="fa fa-undo"></i>'
28972                     }
28973                 ]
28974             },
28975             {
28976                 tag : 'div',
28977                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28978                 action : 'rotate-right',
28979                 cn : [
28980                     {
28981                         tag : 'button',
28982                         cls : 'btn btn-default',
28983                         html : '<i class="fa fa-repeat"></i>'
28984                     }
28985                 ]
28986             }
28987         ]
28988     }
28989 });
28990
28991 /*
28992 * Licence: LGPL
28993 */
28994
28995 /**
28996  * @class Roo.bootstrap.DocumentManager
28997  * @extends Roo.bootstrap.Component
28998  * Bootstrap DocumentManager class
28999  * @cfg {String} paramName default 'imageUpload'
29000  * @cfg {String} toolTipName default 'filename'
29001  * @cfg {String} method default POST
29002  * @cfg {String} url action url
29003  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29004  * @cfg {Boolean} multiple multiple upload default true
29005  * @cfg {Number} thumbSize default 300
29006  * @cfg {String} fieldLabel
29007  * @cfg {Number} labelWidth default 4
29008  * @cfg {String} labelAlign (left|top) default left
29009  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29010 * @cfg {Number} labellg set the width of label (1-12)
29011  * @cfg {Number} labelmd set the width of label (1-12)
29012  * @cfg {Number} labelsm set the width of label (1-12)
29013  * @cfg {Number} labelxs set the width of label (1-12)
29014  * 
29015  * @constructor
29016  * Create a new DocumentManager
29017  * @param {Object} config The config object
29018  */
29019
29020 Roo.bootstrap.DocumentManager = function(config){
29021     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29022     
29023     this.files = [];
29024     this.delegates = [];
29025     
29026     this.addEvents({
29027         /**
29028          * @event initial
29029          * Fire when initial the DocumentManager
29030          * @param {Roo.bootstrap.DocumentManager} this
29031          */
29032         "initial" : true,
29033         /**
29034          * @event inspect
29035          * inspect selected file
29036          * @param {Roo.bootstrap.DocumentManager} this
29037          * @param {File} file
29038          */
29039         "inspect" : true,
29040         /**
29041          * @event exception
29042          * Fire when xhr load exception
29043          * @param {Roo.bootstrap.DocumentManager} this
29044          * @param {XMLHttpRequest} xhr
29045          */
29046         "exception" : true,
29047         /**
29048          * @event afterupload
29049          * Fire when xhr load exception
29050          * @param {Roo.bootstrap.DocumentManager} this
29051          * @param {XMLHttpRequest} xhr
29052          */
29053         "afterupload" : true,
29054         /**
29055          * @event prepare
29056          * prepare the form data
29057          * @param {Roo.bootstrap.DocumentManager} this
29058          * @param {Object} formData
29059          */
29060         "prepare" : true,
29061         /**
29062          * @event remove
29063          * Fire when remove the file
29064          * @param {Roo.bootstrap.DocumentManager} this
29065          * @param {Object} file
29066          */
29067         "remove" : true,
29068         /**
29069          * @event refresh
29070          * Fire after refresh the file
29071          * @param {Roo.bootstrap.DocumentManager} this
29072          */
29073         "refresh" : true,
29074         /**
29075          * @event click
29076          * Fire after click the image
29077          * @param {Roo.bootstrap.DocumentManager} this
29078          * @param {Object} file
29079          */
29080         "click" : true,
29081         /**
29082          * @event edit
29083          * Fire when upload a image and editable set to true
29084          * @param {Roo.bootstrap.DocumentManager} this
29085          * @param {Object} file
29086          */
29087         "edit" : true,
29088         /**
29089          * @event beforeselectfile
29090          * Fire before select file
29091          * @param {Roo.bootstrap.DocumentManager} this
29092          */
29093         "beforeselectfile" : true,
29094         /**
29095          * @event process
29096          * Fire before process file
29097          * @param {Roo.bootstrap.DocumentManager} this
29098          * @param {Object} file
29099          */
29100         "process" : true,
29101         /**
29102          * @event previewrendered
29103          * Fire when preview rendered
29104          * @param {Roo.bootstrap.DocumentManager} this
29105          * @param {Object} file
29106          */
29107         "previewrendered" : true,
29108         /**
29109          */
29110         "previewResize" : true
29111         
29112     });
29113 };
29114
29115 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29116     
29117     boxes : 0,
29118     inputName : '',
29119     thumbSize : 300,
29120     multiple : true,
29121     files : false,
29122     method : 'POST',
29123     url : '',
29124     paramName : 'imageUpload',
29125     toolTipName : 'filename',
29126     fieldLabel : '',
29127     labelWidth : 4,
29128     labelAlign : 'left',
29129     editable : true,
29130     delegates : false,
29131     xhr : false, 
29132     
29133     labellg : 0,
29134     labelmd : 0,
29135     labelsm : 0,
29136     labelxs : 0,
29137     
29138     getAutoCreate : function()
29139     {   
29140         var managerWidget = {
29141             tag : 'div',
29142             cls : 'roo-document-manager',
29143             cn : [
29144                 {
29145                     tag : 'input',
29146                     cls : 'roo-document-manager-selector',
29147                     type : 'file'
29148                 },
29149                 {
29150                     tag : 'div',
29151                     cls : 'roo-document-manager-uploader',
29152                     cn : [
29153                         {
29154                             tag : 'div',
29155                             cls : 'roo-document-manager-upload-btn',
29156                             html : '<i class="fa fa-plus"></i>'
29157                         }
29158                     ]
29159                     
29160                 }
29161             ]
29162         };
29163         
29164         var content = [
29165             {
29166                 tag : 'div',
29167                 cls : 'column col-md-12',
29168                 cn : managerWidget
29169             }
29170         ];
29171         
29172         if(this.fieldLabel.length){
29173             
29174             content = [
29175                 {
29176                     tag : 'div',
29177                     cls : 'column col-md-12',
29178                     html : this.fieldLabel
29179                 },
29180                 {
29181                     tag : 'div',
29182                     cls : 'column col-md-12',
29183                     cn : managerWidget
29184                 }
29185             ];
29186
29187             if(this.labelAlign == 'left'){
29188                 content = [
29189                     {
29190                         tag : 'div',
29191                         cls : 'column',
29192                         html : this.fieldLabel
29193                     },
29194                     {
29195                         tag : 'div',
29196                         cls : 'column',
29197                         cn : managerWidget
29198                     }
29199                 ];
29200                 
29201                 if(this.labelWidth > 12){
29202                     content[0].style = "width: " + this.labelWidth + 'px';
29203                 }
29204
29205                 if(this.labelWidth < 13 && this.labelmd == 0){
29206                     this.labelmd = this.labelWidth;
29207                 }
29208
29209                 if(this.labellg > 0){
29210                     content[0].cls += ' col-lg-' + this.labellg;
29211                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29212                 }
29213
29214                 if(this.labelmd > 0){
29215                     content[0].cls += ' col-md-' + this.labelmd;
29216                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29217                 }
29218
29219                 if(this.labelsm > 0){
29220                     content[0].cls += ' col-sm-' + this.labelsm;
29221                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29222                 }
29223
29224                 if(this.labelxs > 0){
29225                     content[0].cls += ' col-xs-' + this.labelxs;
29226                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29227                 }
29228                 
29229             }
29230         }
29231         
29232         var cfg = {
29233             tag : 'div',
29234             cls : 'row clearfix',
29235             cn : content
29236         };
29237         
29238         return cfg;
29239         
29240     },
29241     
29242     initEvents : function()
29243     {
29244         this.managerEl = this.el.select('.roo-document-manager', true).first();
29245         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29246         
29247         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29248         this.selectorEl.hide();
29249         
29250         if(this.multiple){
29251             this.selectorEl.attr('multiple', 'multiple');
29252         }
29253         
29254         this.selectorEl.on('change', this.onFileSelected, this);
29255         
29256         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29257         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29258         
29259         this.uploader.on('click', this.onUploaderClick, this);
29260         
29261         this.renderProgressDialog();
29262         
29263         var _this = this;
29264         
29265         window.addEventListener("resize", function() { _this.refresh(); } );
29266         
29267         this.fireEvent('initial', this);
29268     },
29269     
29270     renderProgressDialog : function()
29271     {
29272         var _this = this;
29273         
29274         this.progressDialog = new Roo.bootstrap.Modal({
29275             cls : 'roo-document-manager-progress-dialog',
29276             allow_close : false,
29277             animate : false,
29278             title : '',
29279             buttons : [
29280                 {
29281                     name  :'cancel',
29282                     weight : 'danger',
29283                     html : 'Cancel'
29284                 }
29285             ], 
29286             listeners : { 
29287                 btnclick : function() {
29288                     _this.uploadCancel();
29289                     this.hide();
29290                 }
29291             }
29292         });
29293          
29294         this.progressDialog.render(Roo.get(document.body));
29295          
29296         this.progress = new Roo.bootstrap.Progress({
29297             cls : 'roo-document-manager-progress',
29298             active : true,
29299             striped : true
29300         });
29301         
29302         this.progress.render(this.progressDialog.getChildContainer());
29303         
29304         this.progressBar = new Roo.bootstrap.ProgressBar({
29305             cls : 'roo-document-manager-progress-bar',
29306             aria_valuenow : 0,
29307             aria_valuemin : 0,
29308             aria_valuemax : 12,
29309             panel : 'success'
29310         });
29311         
29312         this.progressBar.render(this.progress.getChildContainer());
29313     },
29314     
29315     onUploaderClick : function(e)
29316     {
29317         e.preventDefault();
29318      
29319         if(this.fireEvent('beforeselectfile', this) != false){
29320             this.selectorEl.dom.click();
29321         }
29322         
29323     },
29324     
29325     onFileSelected : function(e)
29326     {
29327         e.preventDefault();
29328         
29329         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29330             return;
29331         }
29332         
29333         Roo.each(this.selectorEl.dom.files, function(file){
29334             if(this.fireEvent('inspect', this, file) != false){
29335                 this.files.push(file);
29336             }
29337         }, this);
29338         
29339         this.queue();
29340         
29341     },
29342     
29343     queue : function()
29344     {
29345         this.selectorEl.dom.value = '';
29346         
29347         if(!this.files || !this.files.length){
29348             return;
29349         }
29350         
29351         if(this.boxes > 0 && this.files.length > this.boxes){
29352             this.files = this.files.slice(0, this.boxes);
29353         }
29354         
29355         this.uploader.show();
29356         
29357         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29358             this.uploader.hide();
29359         }
29360         
29361         var _this = this;
29362         
29363         var files = [];
29364         
29365         var docs = [];
29366         
29367         Roo.each(this.files, function(file){
29368             
29369             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29370                 var f = this.renderPreview(file);
29371                 files.push(f);
29372                 return;
29373             }
29374             
29375             if(file.type.indexOf('image') != -1){
29376                 this.delegates.push(
29377                     (function(){
29378                         _this.process(file);
29379                     }).createDelegate(this)
29380                 );
29381         
29382                 return;
29383             }
29384             
29385             docs.push(
29386                 (function(){
29387                     _this.process(file);
29388                 }).createDelegate(this)
29389             );
29390             
29391         }, this);
29392         
29393         this.files = files;
29394         
29395         this.delegates = this.delegates.concat(docs);
29396         
29397         if(!this.delegates.length){
29398             this.refresh();
29399             return;
29400         }
29401         
29402         this.progressBar.aria_valuemax = this.delegates.length;
29403         
29404         this.arrange();
29405         
29406         return;
29407     },
29408     
29409     arrange : function()
29410     {
29411         if(!this.delegates.length){
29412             this.progressDialog.hide();
29413             this.refresh();
29414             return;
29415         }
29416         
29417         var delegate = this.delegates.shift();
29418         
29419         this.progressDialog.show();
29420         
29421         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29422         
29423         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29424         
29425         delegate();
29426     },
29427     
29428     refresh : function()
29429     {
29430         this.uploader.show();
29431         
29432         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29433             this.uploader.hide();
29434         }
29435         
29436         Roo.isTouch ? this.closable(false) : this.closable(true);
29437         
29438         this.fireEvent('refresh', this);
29439     },
29440     
29441     onRemove : function(e, el, o)
29442     {
29443         e.preventDefault();
29444         
29445         this.fireEvent('remove', this, o);
29446         
29447     },
29448     
29449     remove : function(o)
29450     {
29451         var files = [];
29452         
29453         Roo.each(this.files, function(file){
29454             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29455                 files.push(file);
29456                 return;
29457             }
29458
29459             o.target.remove();
29460
29461         }, this);
29462         
29463         this.files = files;
29464         
29465         this.refresh();
29466     },
29467     
29468     clear : function()
29469     {
29470         Roo.each(this.files, function(file){
29471             if(!file.target){
29472                 return;
29473             }
29474             
29475             file.target.remove();
29476
29477         }, this);
29478         
29479         this.files = [];
29480         
29481         this.refresh();
29482     },
29483     
29484     onClick : function(e, el, o)
29485     {
29486         e.preventDefault();
29487         
29488         this.fireEvent('click', this, o);
29489         
29490     },
29491     
29492     closable : function(closable)
29493     {
29494         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29495             
29496             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29497             
29498             if(closable){
29499                 el.show();
29500                 return;
29501             }
29502             
29503             el.hide();
29504             
29505         }, this);
29506     },
29507     
29508     xhrOnLoad : function(xhr)
29509     {
29510         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29511             el.remove();
29512         }, this);
29513         
29514         if (xhr.readyState !== 4) {
29515             this.arrange();
29516             this.fireEvent('exception', this, xhr);
29517             return;
29518         }
29519
29520         var response = Roo.decode(xhr.responseText);
29521         
29522         if(!response.success){
29523             this.arrange();
29524             this.fireEvent('exception', this, xhr);
29525             return;
29526         }
29527         
29528         var file = this.renderPreview(response.data);
29529         
29530         this.files.push(file);
29531         
29532         this.arrange();
29533         
29534         this.fireEvent('afterupload', this, xhr);
29535         
29536     },
29537     
29538     xhrOnError : function(xhr)
29539     {
29540         Roo.log('xhr on error');
29541         
29542         var response = Roo.decode(xhr.responseText);
29543           
29544         Roo.log(response);
29545         
29546         this.arrange();
29547     },
29548     
29549     process : function(file)
29550     {
29551         if(this.fireEvent('process', this, file) !== false){
29552             if(this.editable && file.type.indexOf('image') != -1){
29553                 this.fireEvent('edit', this, file);
29554                 return;
29555             }
29556
29557             this.uploadStart(file, false);
29558
29559             return;
29560         }
29561         
29562     },
29563     
29564     uploadStart : function(file, crop)
29565     {
29566         this.xhr = new XMLHttpRequest();
29567         
29568         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29569             this.arrange();
29570             return;
29571         }
29572         
29573         file.xhr = this.xhr;
29574             
29575         this.managerEl.createChild({
29576             tag : 'div',
29577             cls : 'roo-document-manager-loading',
29578             cn : [
29579                 {
29580                     tag : 'div',
29581                     tooltip : file.name,
29582                     cls : 'roo-document-manager-thumb',
29583                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29584                 }
29585             ]
29586
29587         });
29588
29589         this.xhr.open(this.method, this.url, true);
29590         
29591         var headers = {
29592             "Accept": "application/json",
29593             "Cache-Control": "no-cache",
29594             "X-Requested-With": "XMLHttpRequest"
29595         };
29596         
29597         for (var headerName in headers) {
29598             var headerValue = headers[headerName];
29599             if (headerValue) {
29600                 this.xhr.setRequestHeader(headerName, headerValue);
29601             }
29602         }
29603         
29604         var _this = this;
29605         
29606         this.xhr.onload = function()
29607         {
29608             _this.xhrOnLoad(_this.xhr);
29609         }
29610         
29611         this.xhr.onerror = function()
29612         {
29613             _this.xhrOnError(_this.xhr);
29614         }
29615         
29616         var formData = new FormData();
29617
29618         formData.append('returnHTML', 'NO');
29619         
29620         if(crop){
29621             formData.append('crop', crop);
29622         }
29623         
29624         formData.append(this.paramName, file, file.name);
29625         
29626         var options = {
29627             file : file, 
29628             manually : false
29629         };
29630         
29631         if(this.fireEvent('prepare', this, formData, options) != false){
29632             
29633             if(options.manually){
29634                 return;
29635             }
29636             
29637             this.xhr.send(formData);
29638             return;
29639         };
29640         
29641         this.uploadCancel();
29642     },
29643     
29644     uploadCancel : function()
29645     {
29646         if (this.xhr) {
29647             this.xhr.abort();
29648         }
29649         
29650         this.delegates = [];
29651         
29652         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29653             el.remove();
29654         }, this);
29655         
29656         this.arrange();
29657     },
29658     
29659     renderPreview : function(file)
29660     {
29661         if(typeof(file.target) != 'undefined' && file.target){
29662             return file;
29663         }
29664         
29665         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29666         
29667         var previewEl = this.managerEl.createChild({
29668             tag : 'div',
29669             cls : 'roo-document-manager-preview',
29670             cn : [
29671                 {
29672                     tag : 'div',
29673                     tooltip : file[this.toolTipName],
29674                     cls : 'roo-document-manager-thumb',
29675                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29676                 },
29677                 {
29678                     tag : 'button',
29679                     cls : 'close',
29680                     html : '<i class="fa fa-times-circle"></i>'
29681                 }
29682             ]
29683         });
29684
29685         var close = previewEl.select('button.close', true).first();
29686
29687         close.on('click', this.onRemove, this, file);
29688
29689         file.target = previewEl;
29690
29691         var image = previewEl.select('img', true).first();
29692         
29693         var _this = this;
29694         
29695         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29696         
29697         image.on('click', this.onClick, this, file);
29698         
29699         this.fireEvent('previewrendered', this, file);
29700         
29701         return file;
29702         
29703     },
29704     
29705     onPreviewLoad : function(file, image)
29706     {
29707         if(typeof(file.target) == 'undefined' || !file.target){
29708             return;
29709         }
29710         
29711         var width = image.dom.naturalWidth || image.dom.width;
29712         var height = image.dom.naturalHeight || image.dom.height;
29713         
29714         if(!this.previewResize) {
29715             return;
29716         }
29717         
29718         if(width > height){
29719             file.target.addClass('wide');
29720             return;
29721         }
29722         
29723         file.target.addClass('tall');
29724         return;
29725         
29726     },
29727     
29728     uploadFromSource : function(file, crop)
29729     {
29730         this.xhr = new XMLHttpRequest();
29731         
29732         this.managerEl.createChild({
29733             tag : 'div',
29734             cls : 'roo-document-manager-loading',
29735             cn : [
29736                 {
29737                     tag : 'div',
29738                     tooltip : file.name,
29739                     cls : 'roo-document-manager-thumb',
29740                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29741                 }
29742             ]
29743
29744         });
29745
29746         this.xhr.open(this.method, this.url, true);
29747         
29748         var headers = {
29749             "Accept": "application/json",
29750             "Cache-Control": "no-cache",
29751             "X-Requested-With": "XMLHttpRequest"
29752         };
29753         
29754         for (var headerName in headers) {
29755             var headerValue = headers[headerName];
29756             if (headerValue) {
29757                 this.xhr.setRequestHeader(headerName, headerValue);
29758             }
29759         }
29760         
29761         var _this = this;
29762         
29763         this.xhr.onload = function()
29764         {
29765             _this.xhrOnLoad(_this.xhr);
29766         }
29767         
29768         this.xhr.onerror = function()
29769         {
29770             _this.xhrOnError(_this.xhr);
29771         }
29772         
29773         var formData = new FormData();
29774
29775         formData.append('returnHTML', 'NO');
29776         
29777         formData.append('crop', crop);
29778         
29779         if(typeof(file.filename) != 'undefined'){
29780             formData.append('filename', file.filename);
29781         }
29782         
29783         if(typeof(file.mimetype) != 'undefined'){
29784             formData.append('mimetype', file.mimetype);
29785         }
29786         
29787         Roo.log(formData);
29788         
29789         if(this.fireEvent('prepare', this, formData) != false){
29790             this.xhr.send(formData);
29791         };
29792     }
29793 });
29794
29795 /*
29796 * Licence: LGPL
29797 */
29798
29799 /**
29800  * @class Roo.bootstrap.DocumentViewer
29801  * @extends Roo.bootstrap.Component
29802  * Bootstrap DocumentViewer class
29803  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29804  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29805  * 
29806  * @constructor
29807  * Create a new DocumentViewer
29808  * @param {Object} config The config object
29809  */
29810
29811 Roo.bootstrap.DocumentViewer = function(config){
29812     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29813     
29814     this.addEvents({
29815         /**
29816          * @event initial
29817          * Fire after initEvent
29818          * @param {Roo.bootstrap.DocumentViewer} this
29819          */
29820         "initial" : true,
29821         /**
29822          * @event click
29823          * Fire after click
29824          * @param {Roo.bootstrap.DocumentViewer} this
29825          */
29826         "click" : true,
29827         /**
29828          * @event download
29829          * Fire after download button
29830          * @param {Roo.bootstrap.DocumentViewer} this
29831          */
29832         "download" : true,
29833         /**
29834          * @event trash
29835          * Fire after trash button
29836          * @param {Roo.bootstrap.DocumentViewer} this
29837          */
29838         "trash" : true
29839         
29840     });
29841 };
29842
29843 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29844     
29845     showDownload : true,
29846     
29847     showTrash : true,
29848     
29849     getAutoCreate : function()
29850     {
29851         var cfg = {
29852             tag : 'div',
29853             cls : 'roo-document-viewer',
29854             cn : [
29855                 {
29856                     tag : 'div',
29857                     cls : 'roo-document-viewer-body',
29858                     cn : [
29859                         {
29860                             tag : 'div',
29861                             cls : 'roo-document-viewer-thumb',
29862                             cn : [
29863                                 {
29864                                     tag : 'img',
29865                                     cls : 'roo-document-viewer-image'
29866                                 }
29867                             ]
29868                         }
29869                     ]
29870                 },
29871                 {
29872                     tag : 'div',
29873                     cls : 'roo-document-viewer-footer',
29874                     cn : {
29875                         tag : 'div',
29876                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29877                         cn : [
29878                             {
29879                                 tag : 'div',
29880                                 cls : 'btn-group roo-document-viewer-download',
29881                                 cn : [
29882                                     {
29883                                         tag : 'button',
29884                                         cls : 'btn btn-default',
29885                                         html : '<i class="fa fa-download"></i>'
29886                                     }
29887                                 ]
29888                             },
29889                             {
29890                                 tag : 'div',
29891                                 cls : 'btn-group roo-document-viewer-trash',
29892                                 cn : [
29893                                     {
29894                                         tag : 'button',
29895                                         cls : 'btn btn-default',
29896                                         html : '<i class="fa fa-trash"></i>'
29897                                     }
29898                                 ]
29899                             }
29900                         ]
29901                     }
29902                 }
29903             ]
29904         };
29905         
29906         return cfg;
29907     },
29908     
29909     initEvents : function()
29910     {
29911         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29912         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29913         
29914         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29915         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29916         
29917         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29918         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29919         
29920         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29921         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29922         
29923         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29924         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29925         
29926         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29927         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29928         
29929         this.bodyEl.on('click', this.onClick, this);
29930         this.downloadBtn.on('click', this.onDownload, this);
29931         this.trashBtn.on('click', this.onTrash, this);
29932         
29933         this.downloadBtn.hide();
29934         this.trashBtn.hide();
29935         
29936         if(this.showDownload){
29937             this.downloadBtn.show();
29938         }
29939         
29940         if(this.showTrash){
29941             this.trashBtn.show();
29942         }
29943         
29944         if(!this.showDownload && !this.showTrash) {
29945             this.footerEl.hide();
29946         }
29947         
29948     },
29949     
29950     initial : function()
29951     {
29952         this.fireEvent('initial', this);
29953         
29954     },
29955     
29956     onClick : function(e)
29957     {
29958         e.preventDefault();
29959         
29960         this.fireEvent('click', this);
29961     },
29962     
29963     onDownload : function(e)
29964     {
29965         e.preventDefault();
29966         
29967         this.fireEvent('download', this);
29968     },
29969     
29970     onTrash : function(e)
29971     {
29972         e.preventDefault();
29973         
29974         this.fireEvent('trash', this);
29975     }
29976     
29977 });
29978 /*
29979  * - LGPL
29980  *
29981  * nav progress bar
29982  * 
29983  */
29984
29985 /**
29986  * @class Roo.bootstrap.NavProgressBar
29987  * @extends Roo.bootstrap.Component
29988  * Bootstrap NavProgressBar class
29989  * 
29990  * @constructor
29991  * Create a new nav progress bar
29992  * @param {Object} config The config object
29993  */
29994
29995 Roo.bootstrap.NavProgressBar = function(config){
29996     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29997
29998     this.bullets = this.bullets || [];
29999    
30000 //    Roo.bootstrap.NavProgressBar.register(this);
30001      this.addEvents({
30002         /**
30003              * @event changed
30004              * Fires when the active item changes
30005              * @param {Roo.bootstrap.NavProgressBar} this
30006              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30007              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30008          */
30009         'changed': true
30010      });
30011     
30012 };
30013
30014 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30015     
30016     bullets : [],
30017     barItems : [],
30018     
30019     getAutoCreate : function()
30020     {
30021         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30022         
30023         cfg = {
30024             tag : 'div',
30025             cls : 'roo-navigation-bar-group',
30026             cn : [
30027                 {
30028                     tag : 'div',
30029                     cls : 'roo-navigation-top-bar'
30030                 },
30031                 {
30032                     tag : 'div',
30033                     cls : 'roo-navigation-bullets-bar',
30034                     cn : [
30035                         {
30036                             tag : 'ul',
30037                             cls : 'roo-navigation-bar'
30038                         }
30039                     ]
30040                 },
30041                 
30042                 {
30043                     tag : 'div',
30044                     cls : 'roo-navigation-bottom-bar'
30045                 }
30046             ]
30047             
30048         };
30049         
30050         return cfg;
30051         
30052     },
30053     
30054     initEvents: function() 
30055     {
30056         
30057     },
30058     
30059     onRender : function(ct, position) 
30060     {
30061         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30062         
30063         if(this.bullets.length){
30064             Roo.each(this.bullets, function(b){
30065                this.addItem(b);
30066             }, this);
30067         }
30068         
30069         this.format();
30070         
30071     },
30072     
30073     addItem : function(cfg)
30074     {
30075         var item = new Roo.bootstrap.NavProgressItem(cfg);
30076         
30077         item.parentId = this.id;
30078         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30079         
30080         if(cfg.html){
30081             var top = new Roo.bootstrap.Element({
30082                 tag : 'div',
30083                 cls : 'roo-navigation-bar-text'
30084             });
30085             
30086             var bottom = new Roo.bootstrap.Element({
30087                 tag : 'div',
30088                 cls : 'roo-navigation-bar-text'
30089             });
30090             
30091             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30092             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30093             
30094             var topText = new Roo.bootstrap.Element({
30095                 tag : 'span',
30096                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30097             });
30098             
30099             var bottomText = new Roo.bootstrap.Element({
30100                 tag : 'span',
30101                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30102             });
30103             
30104             topText.onRender(top.el, null);
30105             bottomText.onRender(bottom.el, null);
30106             
30107             item.topEl = top;
30108             item.bottomEl = bottom;
30109         }
30110         
30111         this.barItems.push(item);
30112         
30113         return item;
30114     },
30115     
30116     getActive : function()
30117     {
30118         var active = false;
30119         
30120         Roo.each(this.barItems, function(v){
30121             
30122             if (!v.isActive()) {
30123                 return;
30124             }
30125             
30126             active = v;
30127             return false;
30128             
30129         });
30130         
30131         return active;
30132     },
30133     
30134     setActiveItem : function(item)
30135     {
30136         var prev = false;
30137         
30138         Roo.each(this.barItems, function(v){
30139             if (v.rid == item.rid) {
30140                 return ;
30141             }
30142             
30143             if (v.isActive()) {
30144                 v.setActive(false);
30145                 prev = v;
30146             }
30147         });
30148
30149         item.setActive(true);
30150         
30151         this.fireEvent('changed', this, item, prev);
30152     },
30153     
30154     getBarItem: function(rid)
30155     {
30156         var ret = false;
30157         
30158         Roo.each(this.barItems, function(e) {
30159             if (e.rid != rid) {
30160                 return;
30161             }
30162             
30163             ret =  e;
30164             return false;
30165         });
30166         
30167         return ret;
30168     },
30169     
30170     indexOfItem : function(item)
30171     {
30172         var index = false;
30173         
30174         Roo.each(this.barItems, function(v, i){
30175             
30176             if (v.rid != item.rid) {
30177                 return;
30178             }
30179             
30180             index = i;
30181             return false
30182         });
30183         
30184         return index;
30185     },
30186     
30187     setActiveNext : function()
30188     {
30189         var i = this.indexOfItem(this.getActive());
30190         
30191         if (i > this.barItems.length) {
30192             return;
30193         }
30194         
30195         this.setActiveItem(this.barItems[i+1]);
30196     },
30197     
30198     setActivePrev : function()
30199     {
30200         var i = this.indexOfItem(this.getActive());
30201         
30202         if (i  < 1) {
30203             return;
30204         }
30205         
30206         this.setActiveItem(this.barItems[i-1]);
30207     },
30208     
30209     format : function()
30210     {
30211         if(!this.barItems.length){
30212             return;
30213         }
30214      
30215         var width = 100 / this.barItems.length;
30216         
30217         Roo.each(this.barItems, function(i){
30218             i.el.setStyle('width', width + '%');
30219             i.topEl.el.setStyle('width', width + '%');
30220             i.bottomEl.el.setStyle('width', width + '%');
30221         }, this);
30222         
30223     }
30224     
30225 });
30226 /*
30227  * - LGPL
30228  *
30229  * Nav Progress Item
30230  * 
30231  */
30232
30233 /**
30234  * @class Roo.bootstrap.NavProgressItem
30235  * @extends Roo.bootstrap.Component
30236  * Bootstrap NavProgressItem class
30237  * @cfg {String} rid the reference id
30238  * @cfg {Boolean} active (true|false) Is item active default false
30239  * @cfg {Boolean} disabled (true|false) Is item active default false
30240  * @cfg {String} html
30241  * @cfg {String} position (top|bottom) text position default bottom
30242  * @cfg {String} icon show icon instead of number
30243  * 
30244  * @constructor
30245  * Create a new NavProgressItem
30246  * @param {Object} config The config object
30247  */
30248 Roo.bootstrap.NavProgressItem = function(config){
30249     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30250     this.addEvents({
30251         // raw events
30252         /**
30253          * @event click
30254          * The raw click event for the entire grid.
30255          * @param {Roo.bootstrap.NavProgressItem} this
30256          * @param {Roo.EventObject} e
30257          */
30258         "click" : true
30259     });
30260    
30261 };
30262
30263 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30264     
30265     rid : '',
30266     active : false,
30267     disabled : false,
30268     html : '',
30269     position : 'bottom',
30270     icon : false,
30271     
30272     getAutoCreate : function()
30273     {
30274         var iconCls = 'roo-navigation-bar-item-icon';
30275         
30276         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30277         
30278         var cfg = {
30279             tag: 'li',
30280             cls: 'roo-navigation-bar-item',
30281             cn : [
30282                 {
30283                     tag : 'i',
30284                     cls : iconCls
30285                 }
30286             ]
30287         };
30288         
30289         if(this.active){
30290             cfg.cls += ' active';
30291         }
30292         if(this.disabled){
30293             cfg.cls += ' disabled';
30294         }
30295         
30296         return cfg;
30297     },
30298     
30299     disable : function()
30300     {
30301         this.setDisabled(true);
30302     },
30303     
30304     enable : function()
30305     {
30306         this.setDisabled(false);
30307     },
30308     
30309     initEvents: function() 
30310     {
30311         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30312         
30313         this.iconEl.on('click', this.onClick, this);
30314     },
30315     
30316     onClick : function(e)
30317     {
30318         e.preventDefault();
30319         
30320         if(this.disabled){
30321             return;
30322         }
30323         
30324         if(this.fireEvent('click', this, e) === false){
30325             return;
30326         };
30327         
30328         this.parent().setActiveItem(this);
30329     },
30330     
30331     isActive: function () 
30332     {
30333         return this.active;
30334     },
30335     
30336     setActive : function(state)
30337     {
30338         if(this.active == state){
30339             return;
30340         }
30341         
30342         this.active = state;
30343         
30344         if (state) {
30345             this.el.addClass('active');
30346             return;
30347         }
30348         
30349         this.el.removeClass('active');
30350         
30351         return;
30352     },
30353     
30354     setDisabled : function(state)
30355     {
30356         if(this.disabled == state){
30357             return;
30358         }
30359         
30360         this.disabled = state;
30361         
30362         if (state) {
30363             this.el.addClass('disabled');
30364             return;
30365         }
30366         
30367         this.el.removeClass('disabled');
30368     },
30369     
30370     tooltipEl : function()
30371     {
30372         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30373     }
30374 });
30375  
30376
30377  /*
30378  * - LGPL
30379  *
30380  * FieldLabel
30381  * 
30382  */
30383
30384 /**
30385  * @class Roo.bootstrap.FieldLabel
30386  * @extends Roo.bootstrap.Component
30387  * Bootstrap FieldLabel class
30388  * @cfg {String} html contents of the element
30389  * @cfg {String} tag tag of the element default label
30390  * @cfg {String} cls class of the element
30391  * @cfg {String} target label target 
30392  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30393  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30394  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30395  * @cfg {String} iconTooltip default "This field is required"
30396  * @cfg {String} indicatorpos (left|right) default left
30397  * 
30398  * @constructor
30399  * Create a new FieldLabel
30400  * @param {Object} config The config object
30401  */
30402
30403 Roo.bootstrap.FieldLabel = function(config){
30404     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30405     
30406     this.addEvents({
30407             /**
30408              * @event invalid
30409              * Fires after the field has been marked as invalid.
30410              * @param {Roo.form.FieldLabel} this
30411              * @param {String} msg The validation message
30412              */
30413             invalid : true,
30414             /**
30415              * @event valid
30416              * Fires after the field has been validated with no errors.
30417              * @param {Roo.form.FieldLabel} this
30418              */
30419             valid : true
30420         });
30421 };
30422
30423 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30424     
30425     tag: 'label',
30426     cls: '',
30427     html: '',
30428     target: '',
30429     allowBlank : true,
30430     invalidClass : 'has-warning',
30431     validClass : 'has-success',
30432     iconTooltip : 'This field is required',
30433     indicatorpos : 'left',
30434     
30435     getAutoCreate : function(){
30436         
30437         var cls = "";
30438         if (!this.allowBlank) {
30439             cls  = "visible";
30440         }
30441         
30442         var cfg = {
30443             tag : this.tag,
30444             cls : 'roo-bootstrap-field-label ' + this.cls,
30445             for : this.target,
30446             cn : [
30447                 {
30448                     tag : 'i',
30449                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30450                     tooltip : this.iconTooltip
30451                 },
30452                 {
30453                     tag : 'span',
30454                     html : this.html
30455                 }
30456             ] 
30457         };
30458         
30459         if(this.indicatorpos == 'right'){
30460             var cfg = {
30461                 tag : this.tag,
30462                 cls : 'roo-bootstrap-field-label ' + this.cls,
30463                 for : this.target,
30464                 cn : [
30465                     {
30466                         tag : 'span',
30467                         html : this.html
30468                     },
30469                     {
30470                         tag : 'i',
30471                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30472                         tooltip : this.iconTooltip
30473                     }
30474                 ] 
30475             };
30476         }
30477         
30478         return cfg;
30479     },
30480     
30481     initEvents: function() 
30482     {
30483         Roo.bootstrap.Element.superclass.initEvents.call(this);
30484         
30485         this.indicator = this.indicatorEl();
30486         
30487         if(this.indicator){
30488             this.indicator.removeClass('visible');
30489             this.indicator.addClass('invisible');
30490         }
30491         
30492         Roo.bootstrap.FieldLabel.register(this);
30493     },
30494     
30495     indicatorEl : function()
30496     {
30497         var indicator = this.el.select('i.roo-required-indicator',true).first();
30498         
30499         if(!indicator){
30500             return false;
30501         }
30502         
30503         return indicator;
30504         
30505     },
30506     
30507     /**
30508      * Mark this field as valid
30509      */
30510     markValid : function()
30511     {
30512         if(this.indicator){
30513             this.indicator.removeClass('visible');
30514             this.indicator.addClass('invisible');
30515         }
30516         if (Roo.bootstrap.version == 3) {
30517             this.el.removeClass(this.invalidClass);
30518             this.el.addClass(this.validClass);
30519         } else {
30520             this.el.removeClass('is-invalid');
30521             this.el.addClass('is-valid');
30522         }
30523         
30524         
30525         this.fireEvent('valid', this);
30526     },
30527     
30528     /**
30529      * Mark this field as invalid
30530      * @param {String} msg The validation message
30531      */
30532     markInvalid : function(msg)
30533     {
30534         if(this.indicator){
30535             this.indicator.removeClass('invisible');
30536             this.indicator.addClass('visible');
30537         }
30538           if (Roo.bootstrap.version == 3) {
30539             this.el.removeClass(this.validClass);
30540             this.el.addClass(this.invalidClass);
30541         } else {
30542             this.el.removeClass('is-valid');
30543             this.el.addClass('is-invalid');
30544         }
30545         
30546         
30547         this.fireEvent('invalid', this, msg);
30548     }
30549     
30550    
30551 });
30552
30553 Roo.apply(Roo.bootstrap.FieldLabel, {
30554     
30555     groups: {},
30556     
30557      /**
30558     * register a FieldLabel Group
30559     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30560     */
30561     register : function(label)
30562     {
30563         if(this.groups.hasOwnProperty(label.target)){
30564             return;
30565         }
30566      
30567         this.groups[label.target] = label;
30568         
30569     },
30570     /**
30571     * fetch a FieldLabel Group based on the target
30572     * @param {string} target
30573     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30574     */
30575     get: function(target) {
30576         if (typeof(this.groups[target]) == 'undefined') {
30577             return false;
30578         }
30579         
30580         return this.groups[target] ;
30581     }
30582 });
30583
30584  
30585
30586  /*
30587  * - LGPL
30588  *
30589  * page DateSplitField.
30590  * 
30591  */
30592
30593
30594 /**
30595  * @class Roo.bootstrap.DateSplitField
30596  * @extends Roo.bootstrap.Component
30597  * Bootstrap DateSplitField class
30598  * @cfg {string} fieldLabel - the label associated
30599  * @cfg {Number} labelWidth set the width of label (0-12)
30600  * @cfg {String} labelAlign (top|left)
30601  * @cfg {Boolean} dayAllowBlank (true|false) default false
30602  * @cfg {Boolean} monthAllowBlank (true|false) default false
30603  * @cfg {Boolean} yearAllowBlank (true|false) default false
30604  * @cfg {string} dayPlaceholder 
30605  * @cfg {string} monthPlaceholder
30606  * @cfg {string} yearPlaceholder
30607  * @cfg {string} dayFormat default 'd'
30608  * @cfg {string} monthFormat default 'm'
30609  * @cfg {string} yearFormat default 'Y'
30610  * @cfg {Number} labellg set the width of label (1-12)
30611  * @cfg {Number} labelmd set the width of label (1-12)
30612  * @cfg {Number} labelsm set the width of label (1-12)
30613  * @cfg {Number} labelxs set the width of label (1-12)
30614
30615  *     
30616  * @constructor
30617  * Create a new DateSplitField
30618  * @param {Object} config The config object
30619  */
30620
30621 Roo.bootstrap.DateSplitField = function(config){
30622     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30623     
30624     this.addEvents({
30625         // raw events
30626          /**
30627          * @event years
30628          * getting the data of years
30629          * @param {Roo.bootstrap.DateSplitField} this
30630          * @param {Object} years
30631          */
30632         "years" : true,
30633         /**
30634          * @event days
30635          * getting the data of days
30636          * @param {Roo.bootstrap.DateSplitField} this
30637          * @param {Object} days
30638          */
30639         "days" : true,
30640         /**
30641          * @event invalid
30642          * Fires after the field has been marked as invalid.
30643          * @param {Roo.form.Field} this
30644          * @param {String} msg The validation message
30645          */
30646         invalid : true,
30647        /**
30648          * @event valid
30649          * Fires after the field has been validated with no errors.
30650          * @param {Roo.form.Field} this
30651          */
30652         valid : true
30653     });
30654 };
30655
30656 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30657     
30658     fieldLabel : '',
30659     labelAlign : 'top',
30660     labelWidth : 3,
30661     dayAllowBlank : false,
30662     monthAllowBlank : false,
30663     yearAllowBlank : false,
30664     dayPlaceholder : '',
30665     monthPlaceholder : '',
30666     yearPlaceholder : '',
30667     dayFormat : 'd',
30668     monthFormat : 'm',
30669     yearFormat : 'Y',
30670     isFormField : true,
30671     labellg : 0,
30672     labelmd : 0,
30673     labelsm : 0,
30674     labelxs : 0,
30675     
30676     getAutoCreate : function()
30677     {
30678         var cfg = {
30679             tag : 'div',
30680             cls : 'row roo-date-split-field-group',
30681             cn : [
30682                 {
30683                     tag : 'input',
30684                     type : 'hidden',
30685                     cls : 'form-hidden-field roo-date-split-field-group-value',
30686                     name : this.name
30687                 }
30688             ]
30689         };
30690         
30691         var labelCls = 'col-md-12';
30692         var contentCls = 'col-md-4';
30693         
30694         if(this.fieldLabel){
30695             
30696             var label = {
30697                 tag : 'div',
30698                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30699                 cn : [
30700                     {
30701                         tag : 'label',
30702                         html : this.fieldLabel
30703                     }
30704                 ]
30705             };
30706             
30707             if(this.labelAlign == 'left'){
30708             
30709                 if(this.labelWidth > 12){
30710                     label.style = "width: " + this.labelWidth + 'px';
30711                 }
30712
30713                 if(this.labelWidth < 13 && this.labelmd == 0){
30714                     this.labelmd = this.labelWidth;
30715                 }
30716
30717                 if(this.labellg > 0){
30718                     labelCls = ' col-lg-' + this.labellg;
30719                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30720                 }
30721
30722                 if(this.labelmd > 0){
30723                     labelCls = ' col-md-' + this.labelmd;
30724                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30725                 }
30726
30727                 if(this.labelsm > 0){
30728                     labelCls = ' col-sm-' + this.labelsm;
30729                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30730                 }
30731
30732                 if(this.labelxs > 0){
30733                     labelCls = ' col-xs-' + this.labelxs;
30734                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30735                 }
30736             }
30737             
30738             label.cls += ' ' + labelCls;
30739             
30740             cfg.cn.push(label);
30741         }
30742         
30743         Roo.each(['day', 'month', 'year'], function(t){
30744             cfg.cn.push({
30745                 tag : 'div',
30746                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30747             });
30748         }, this);
30749         
30750         return cfg;
30751     },
30752     
30753     inputEl: function ()
30754     {
30755         return this.el.select('.roo-date-split-field-group-value', true).first();
30756     },
30757     
30758     onRender : function(ct, position) 
30759     {
30760         var _this = this;
30761         
30762         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30763         
30764         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30765         
30766         this.dayField = new Roo.bootstrap.ComboBox({
30767             allowBlank : this.dayAllowBlank,
30768             alwaysQuery : true,
30769             displayField : 'value',
30770             editable : false,
30771             fieldLabel : '',
30772             forceSelection : true,
30773             mode : 'local',
30774             placeholder : this.dayPlaceholder,
30775             selectOnFocus : true,
30776             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30777             triggerAction : 'all',
30778             typeAhead : true,
30779             valueField : 'value',
30780             store : new Roo.data.SimpleStore({
30781                 data : (function() {    
30782                     var days = [];
30783                     _this.fireEvent('days', _this, days);
30784                     return days;
30785                 })(),
30786                 fields : [ 'value' ]
30787             }),
30788             listeners : {
30789                 select : function (_self, record, index)
30790                 {
30791                     _this.setValue(_this.getValue());
30792                 }
30793             }
30794         });
30795
30796         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30797         
30798         this.monthField = new Roo.bootstrap.MonthField({
30799             after : '<i class=\"fa fa-calendar\"></i>',
30800             allowBlank : this.monthAllowBlank,
30801             placeholder : this.monthPlaceholder,
30802             readOnly : true,
30803             listeners : {
30804                 render : function (_self)
30805                 {
30806                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30807                         e.preventDefault();
30808                         _self.focus();
30809                     });
30810                 },
30811                 select : function (_self, oldvalue, newvalue)
30812                 {
30813                     _this.setValue(_this.getValue());
30814                 }
30815             }
30816         });
30817         
30818         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30819         
30820         this.yearField = new Roo.bootstrap.ComboBox({
30821             allowBlank : this.yearAllowBlank,
30822             alwaysQuery : true,
30823             displayField : 'value',
30824             editable : false,
30825             fieldLabel : '',
30826             forceSelection : true,
30827             mode : 'local',
30828             placeholder : this.yearPlaceholder,
30829             selectOnFocus : true,
30830             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30831             triggerAction : 'all',
30832             typeAhead : true,
30833             valueField : 'value',
30834             store : new Roo.data.SimpleStore({
30835                 data : (function() {
30836                     var years = [];
30837                     _this.fireEvent('years', _this, years);
30838                     return years;
30839                 })(),
30840                 fields : [ 'value' ]
30841             }),
30842             listeners : {
30843                 select : function (_self, record, index)
30844                 {
30845                     _this.setValue(_this.getValue());
30846                 }
30847             }
30848         });
30849
30850         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30851     },
30852     
30853     setValue : function(v, format)
30854     {
30855         this.inputEl.dom.value = v;
30856         
30857         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30858         
30859         var d = Date.parseDate(v, f);
30860         
30861         if(!d){
30862             this.validate();
30863             return;
30864         }
30865         
30866         this.setDay(d.format(this.dayFormat));
30867         this.setMonth(d.format(this.monthFormat));
30868         this.setYear(d.format(this.yearFormat));
30869         
30870         this.validate();
30871         
30872         return;
30873     },
30874     
30875     setDay : function(v)
30876     {
30877         this.dayField.setValue(v);
30878         this.inputEl.dom.value = this.getValue();
30879         this.validate();
30880         return;
30881     },
30882     
30883     setMonth : function(v)
30884     {
30885         this.monthField.setValue(v, true);
30886         this.inputEl.dom.value = this.getValue();
30887         this.validate();
30888         return;
30889     },
30890     
30891     setYear : function(v)
30892     {
30893         this.yearField.setValue(v);
30894         this.inputEl.dom.value = this.getValue();
30895         this.validate();
30896         return;
30897     },
30898     
30899     getDay : function()
30900     {
30901         return this.dayField.getValue();
30902     },
30903     
30904     getMonth : function()
30905     {
30906         return this.monthField.getValue();
30907     },
30908     
30909     getYear : function()
30910     {
30911         return this.yearField.getValue();
30912     },
30913     
30914     getValue : function()
30915     {
30916         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30917         
30918         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30919         
30920         return date;
30921     },
30922     
30923     reset : function()
30924     {
30925         this.setDay('');
30926         this.setMonth('');
30927         this.setYear('');
30928         this.inputEl.dom.value = '';
30929         this.validate();
30930         return;
30931     },
30932     
30933     validate : function()
30934     {
30935         var d = this.dayField.validate();
30936         var m = this.monthField.validate();
30937         var y = this.yearField.validate();
30938         
30939         var valid = true;
30940         
30941         if(
30942                 (!this.dayAllowBlank && !d) ||
30943                 (!this.monthAllowBlank && !m) ||
30944                 (!this.yearAllowBlank && !y)
30945         ){
30946             valid = false;
30947         }
30948         
30949         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30950             return valid;
30951         }
30952         
30953         if(valid){
30954             this.markValid();
30955             return valid;
30956         }
30957         
30958         this.markInvalid();
30959         
30960         return valid;
30961     },
30962     
30963     markValid : function()
30964     {
30965         
30966         var label = this.el.select('label', true).first();
30967         var icon = this.el.select('i.fa-star', true).first();
30968
30969         if(label && icon){
30970             icon.remove();
30971         }
30972         
30973         this.fireEvent('valid', this);
30974     },
30975     
30976      /**
30977      * Mark this field as invalid
30978      * @param {String} msg The validation message
30979      */
30980     markInvalid : function(msg)
30981     {
30982         
30983         var label = this.el.select('label', true).first();
30984         var icon = this.el.select('i.fa-star', true).first();
30985
30986         if(label && !icon){
30987             this.el.select('.roo-date-split-field-label', true).createChild({
30988                 tag : 'i',
30989                 cls : 'text-danger fa fa-lg fa-star',
30990                 tooltip : 'This field is required',
30991                 style : 'margin-right:5px;'
30992             }, label, true);
30993         }
30994         
30995         this.fireEvent('invalid', this, msg);
30996     },
30997     
30998     clearInvalid : function()
30999     {
31000         var label = this.el.select('label', true).first();
31001         var icon = this.el.select('i.fa-star', true).first();
31002
31003         if(label && icon){
31004             icon.remove();
31005         }
31006         
31007         this.fireEvent('valid', this);
31008     },
31009     
31010     getName: function()
31011     {
31012         return this.name;
31013     }
31014     
31015 });
31016
31017  /**
31018  *
31019  * This is based on 
31020  * http://masonry.desandro.com
31021  *
31022  * The idea is to render all the bricks based on vertical width...
31023  *
31024  * The original code extends 'outlayer' - we might need to use that....
31025  * 
31026  */
31027
31028
31029 /**
31030  * @class Roo.bootstrap.LayoutMasonry
31031  * @extends Roo.bootstrap.Component
31032  * Bootstrap Layout Masonry class
31033  * 
31034  * @constructor
31035  * Create a new Element
31036  * @param {Object} config The config object
31037  */
31038
31039 Roo.bootstrap.LayoutMasonry = function(config){
31040     
31041     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31042     
31043     this.bricks = [];
31044     
31045     Roo.bootstrap.LayoutMasonry.register(this);
31046     
31047     this.addEvents({
31048         // raw events
31049         /**
31050          * @event layout
31051          * Fire after layout the items
31052          * @param {Roo.bootstrap.LayoutMasonry} this
31053          * @param {Roo.EventObject} e
31054          */
31055         "layout" : true
31056     });
31057     
31058 };
31059
31060 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31061     
31062     /**
31063      * @cfg {Boolean} isLayoutInstant = no animation?
31064      */   
31065     isLayoutInstant : false, // needed?
31066    
31067     /**
31068      * @cfg {Number} boxWidth  width of the columns
31069      */   
31070     boxWidth : 450,
31071     
31072       /**
31073      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31074      */   
31075     boxHeight : 0,
31076     
31077     /**
31078      * @cfg {Number} padWidth padding below box..
31079      */   
31080     padWidth : 10, 
31081     
31082     /**
31083      * @cfg {Number} gutter gutter width..
31084      */   
31085     gutter : 10,
31086     
31087      /**
31088      * @cfg {Number} maxCols maximum number of columns
31089      */   
31090     
31091     maxCols: 0,
31092     
31093     /**
31094      * @cfg {Boolean} isAutoInitial defalut true
31095      */   
31096     isAutoInitial : true, 
31097     
31098     containerWidth: 0,
31099     
31100     /**
31101      * @cfg {Boolean} isHorizontal defalut false
31102      */   
31103     isHorizontal : false, 
31104
31105     currentSize : null,
31106     
31107     tag: 'div',
31108     
31109     cls: '',
31110     
31111     bricks: null, //CompositeElement
31112     
31113     cols : 1,
31114     
31115     _isLayoutInited : false,
31116     
31117 //    isAlternative : false, // only use for vertical layout...
31118     
31119     /**
31120      * @cfg {Number} alternativePadWidth padding below box..
31121      */   
31122     alternativePadWidth : 50,
31123     
31124     selectedBrick : [],
31125     
31126     getAutoCreate : function(){
31127         
31128         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31129         
31130         var cfg = {
31131             tag: this.tag,
31132             cls: 'blog-masonary-wrapper ' + this.cls,
31133             cn : {
31134                 cls : 'mas-boxes masonary'
31135             }
31136         };
31137         
31138         return cfg;
31139     },
31140     
31141     getChildContainer: function( )
31142     {
31143         if (this.boxesEl) {
31144             return this.boxesEl;
31145         }
31146         
31147         this.boxesEl = this.el.select('.mas-boxes').first();
31148         
31149         return this.boxesEl;
31150     },
31151     
31152     
31153     initEvents : function()
31154     {
31155         var _this = this;
31156         
31157         if(this.isAutoInitial){
31158             Roo.log('hook children rendered');
31159             this.on('childrenrendered', function() {
31160                 Roo.log('children rendered');
31161                 _this.initial();
31162             } ,this);
31163         }
31164     },
31165     
31166     initial : function()
31167     {
31168         this.selectedBrick = [];
31169         
31170         this.currentSize = this.el.getBox(true);
31171         
31172         Roo.EventManager.onWindowResize(this.resize, this); 
31173
31174         if(!this.isAutoInitial){
31175             this.layout();
31176             return;
31177         }
31178         
31179         this.layout();
31180         
31181         return;
31182         //this.layout.defer(500,this);
31183         
31184     },
31185     
31186     resize : function()
31187     {
31188         var cs = this.el.getBox(true);
31189         
31190         if (
31191                 this.currentSize.width == cs.width && 
31192                 this.currentSize.x == cs.x && 
31193                 this.currentSize.height == cs.height && 
31194                 this.currentSize.y == cs.y 
31195         ) {
31196             Roo.log("no change in with or X or Y");
31197             return;
31198         }
31199         
31200         this.currentSize = cs;
31201         
31202         this.layout();
31203         
31204     },
31205     
31206     layout : function()
31207     {   
31208         this._resetLayout();
31209         
31210         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31211         
31212         this.layoutItems( isInstant );
31213       
31214         this._isLayoutInited = true;
31215         
31216         this.fireEvent('layout', this);
31217         
31218     },
31219     
31220     _resetLayout : function()
31221     {
31222         if(this.isHorizontal){
31223             this.horizontalMeasureColumns();
31224             return;
31225         }
31226         
31227         this.verticalMeasureColumns();
31228         
31229     },
31230     
31231     verticalMeasureColumns : function()
31232     {
31233         this.getContainerWidth();
31234         
31235 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31236 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31237 //            return;
31238 //        }
31239         
31240         var boxWidth = this.boxWidth + this.padWidth;
31241         
31242         if(this.containerWidth < this.boxWidth){
31243             boxWidth = this.containerWidth
31244         }
31245         
31246         var containerWidth = this.containerWidth;
31247         
31248         var cols = Math.floor(containerWidth / boxWidth);
31249         
31250         this.cols = Math.max( cols, 1 );
31251         
31252         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31253         
31254         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31255         
31256         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31257         
31258         this.colWidth = boxWidth + avail - this.padWidth;
31259         
31260         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31261         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31262     },
31263     
31264     horizontalMeasureColumns : function()
31265     {
31266         this.getContainerWidth();
31267         
31268         var boxWidth = this.boxWidth;
31269         
31270         if(this.containerWidth < boxWidth){
31271             boxWidth = this.containerWidth;
31272         }
31273         
31274         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31275         
31276         this.el.setHeight(boxWidth);
31277         
31278     },
31279     
31280     getContainerWidth : function()
31281     {
31282         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31283     },
31284     
31285     layoutItems : function( isInstant )
31286     {
31287         Roo.log(this.bricks);
31288         
31289         var items = Roo.apply([], this.bricks);
31290         
31291         if(this.isHorizontal){
31292             this._horizontalLayoutItems( items , isInstant );
31293             return;
31294         }
31295         
31296 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31297 //            this._verticalAlternativeLayoutItems( items , isInstant );
31298 //            return;
31299 //        }
31300         
31301         this._verticalLayoutItems( items , isInstant );
31302         
31303     },
31304     
31305     _verticalLayoutItems : function ( items , isInstant)
31306     {
31307         if ( !items || !items.length ) {
31308             return;
31309         }
31310         
31311         var standard = [
31312             ['xs', 'xs', 'xs', 'tall'],
31313             ['xs', 'xs', 'tall'],
31314             ['xs', 'xs', 'sm'],
31315             ['xs', 'xs', 'xs'],
31316             ['xs', 'tall'],
31317             ['xs', 'sm'],
31318             ['xs', 'xs'],
31319             ['xs'],
31320             
31321             ['sm', 'xs', 'xs'],
31322             ['sm', 'xs'],
31323             ['sm'],
31324             
31325             ['tall', 'xs', 'xs', 'xs'],
31326             ['tall', 'xs', 'xs'],
31327             ['tall', 'xs'],
31328             ['tall']
31329             
31330         ];
31331         
31332         var queue = [];
31333         
31334         var boxes = [];
31335         
31336         var box = [];
31337         
31338         Roo.each(items, function(item, k){
31339             
31340             switch (item.size) {
31341                 // these layouts take up a full box,
31342                 case 'md' :
31343                 case 'md-left' :
31344                 case 'md-right' :
31345                 case 'wide' :
31346                     
31347                     if(box.length){
31348                         boxes.push(box);
31349                         box = [];
31350                     }
31351                     
31352                     boxes.push([item]);
31353                     
31354                     break;
31355                     
31356                 case 'xs' :
31357                 case 'sm' :
31358                 case 'tall' :
31359                     
31360                     box.push(item);
31361                     
31362                     break;
31363                 default :
31364                     break;
31365                     
31366             }
31367             
31368         }, this);
31369         
31370         if(box.length){
31371             boxes.push(box);
31372             box = [];
31373         }
31374         
31375         var filterPattern = function(box, length)
31376         {
31377             if(!box.length){
31378                 return;
31379             }
31380             
31381             var match = false;
31382             
31383             var pattern = box.slice(0, length);
31384             
31385             var format = [];
31386             
31387             Roo.each(pattern, function(i){
31388                 format.push(i.size);
31389             }, this);
31390             
31391             Roo.each(standard, function(s){
31392                 
31393                 if(String(s) != String(format)){
31394                     return;
31395                 }
31396                 
31397                 match = true;
31398                 return false;
31399                 
31400             }, this);
31401             
31402             if(!match && length == 1){
31403                 return;
31404             }
31405             
31406             if(!match){
31407                 filterPattern(box, length - 1);
31408                 return;
31409             }
31410                 
31411             queue.push(pattern);
31412
31413             box = box.slice(length, box.length);
31414
31415             filterPattern(box, 4);
31416
31417             return;
31418             
31419         }
31420         
31421         Roo.each(boxes, function(box, k){
31422             
31423             if(!box.length){
31424                 return;
31425             }
31426             
31427             if(box.length == 1){
31428                 queue.push(box);
31429                 return;
31430             }
31431             
31432             filterPattern(box, 4);
31433             
31434         }, this);
31435         
31436         this._processVerticalLayoutQueue( queue, isInstant );
31437         
31438     },
31439     
31440 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31441 //    {
31442 //        if ( !items || !items.length ) {
31443 //            return;
31444 //        }
31445 //
31446 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31447 //        
31448 //    },
31449     
31450     _horizontalLayoutItems : function ( items , isInstant)
31451     {
31452         if ( !items || !items.length || items.length < 3) {
31453             return;
31454         }
31455         
31456         items.reverse();
31457         
31458         var eItems = items.slice(0, 3);
31459         
31460         items = items.slice(3, items.length);
31461         
31462         var standard = [
31463             ['xs', 'xs', 'xs', 'wide'],
31464             ['xs', 'xs', 'wide'],
31465             ['xs', 'xs', 'sm'],
31466             ['xs', 'xs', 'xs'],
31467             ['xs', 'wide'],
31468             ['xs', 'sm'],
31469             ['xs', 'xs'],
31470             ['xs'],
31471             
31472             ['sm', 'xs', 'xs'],
31473             ['sm', 'xs'],
31474             ['sm'],
31475             
31476             ['wide', 'xs', 'xs', 'xs'],
31477             ['wide', 'xs', 'xs'],
31478             ['wide', 'xs'],
31479             ['wide'],
31480             
31481             ['wide-thin']
31482         ];
31483         
31484         var queue = [];
31485         
31486         var boxes = [];
31487         
31488         var box = [];
31489         
31490         Roo.each(items, function(item, k){
31491             
31492             switch (item.size) {
31493                 case 'md' :
31494                 case 'md-left' :
31495                 case 'md-right' :
31496                 case 'tall' :
31497                     
31498                     if(box.length){
31499                         boxes.push(box);
31500                         box = [];
31501                     }
31502                     
31503                     boxes.push([item]);
31504                     
31505                     break;
31506                     
31507                 case 'xs' :
31508                 case 'sm' :
31509                 case 'wide' :
31510                 case 'wide-thin' :
31511                     
31512                     box.push(item);
31513                     
31514                     break;
31515                 default :
31516                     break;
31517                     
31518             }
31519             
31520         }, this);
31521         
31522         if(box.length){
31523             boxes.push(box);
31524             box = [];
31525         }
31526         
31527         var filterPattern = function(box, length)
31528         {
31529             if(!box.length){
31530                 return;
31531             }
31532             
31533             var match = false;
31534             
31535             var pattern = box.slice(0, length);
31536             
31537             var format = [];
31538             
31539             Roo.each(pattern, function(i){
31540                 format.push(i.size);
31541             }, this);
31542             
31543             Roo.each(standard, function(s){
31544                 
31545                 if(String(s) != String(format)){
31546                     return;
31547                 }
31548                 
31549                 match = true;
31550                 return false;
31551                 
31552             }, this);
31553             
31554             if(!match && length == 1){
31555                 return;
31556             }
31557             
31558             if(!match){
31559                 filterPattern(box, length - 1);
31560                 return;
31561             }
31562                 
31563             queue.push(pattern);
31564
31565             box = box.slice(length, box.length);
31566
31567             filterPattern(box, 4);
31568
31569             return;
31570             
31571         }
31572         
31573         Roo.each(boxes, function(box, k){
31574             
31575             if(!box.length){
31576                 return;
31577             }
31578             
31579             if(box.length == 1){
31580                 queue.push(box);
31581                 return;
31582             }
31583             
31584             filterPattern(box, 4);
31585             
31586         }, this);
31587         
31588         
31589         var prune = [];
31590         
31591         var pos = this.el.getBox(true);
31592         
31593         var minX = pos.x;
31594         
31595         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31596         
31597         var hit_end = false;
31598         
31599         Roo.each(queue, function(box){
31600             
31601             if(hit_end){
31602                 
31603                 Roo.each(box, function(b){
31604                 
31605                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31606                     b.el.hide();
31607
31608                 }, this);
31609
31610                 return;
31611             }
31612             
31613             var mx = 0;
31614             
31615             Roo.each(box, function(b){
31616                 
31617                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31618                 b.el.show();
31619
31620                 mx = Math.max(mx, b.x);
31621                 
31622             }, this);
31623             
31624             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31625             
31626             if(maxX < minX){
31627                 
31628                 Roo.each(box, function(b){
31629                 
31630                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31631                     b.el.hide();
31632                     
31633                 }, this);
31634                 
31635                 hit_end = true;
31636                 
31637                 return;
31638             }
31639             
31640             prune.push(box);
31641             
31642         }, this);
31643         
31644         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31645     },
31646     
31647     /** Sets position of item in DOM
31648     * @param {Element} item
31649     * @param {Number} x - horizontal position
31650     * @param {Number} y - vertical position
31651     * @param {Boolean} isInstant - disables transitions
31652     */
31653     _processVerticalLayoutQueue : function( queue, isInstant )
31654     {
31655         var pos = this.el.getBox(true);
31656         var x = pos.x;
31657         var y = pos.y;
31658         var maxY = [];
31659         
31660         for (var i = 0; i < this.cols; i++){
31661             maxY[i] = pos.y;
31662         }
31663         
31664         Roo.each(queue, function(box, k){
31665             
31666             var col = k % this.cols;
31667             
31668             Roo.each(box, function(b,kk){
31669                 
31670                 b.el.position('absolute');
31671                 
31672                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31673                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31674                 
31675                 if(b.size == 'md-left' || b.size == 'md-right'){
31676                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31677                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31678                 }
31679                 
31680                 b.el.setWidth(width);
31681                 b.el.setHeight(height);
31682                 // iframe?
31683                 b.el.select('iframe',true).setSize(width,height);
31684                 
31685             }, this);
31686             
31687             for (var i = 0; i < this.cols; i++){
31688                 
31689                 if(maxY[i] < maxY[col]){
31690                     col = i;
31691                     continue;
31692                 }
31693                 
31694                 col = Math.min(col, i);
31695                 
31696             }
31697             
31698             x = pos.x + col * (this.colWidth + this.padWidth);
31699             
31700             y = maxY[col];
31701             
31702             var positions = [];
31703             
31704             switch (box.length){
31705                 case 1 :
31706                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31707                     break;
31708                 case 2 :
31709                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31710                     break;
31711                 case 3 :
31712                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31713                     break;
31714                 case 4 :
31715                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31716                     break;
31717                 default :
31718                     break;
31719             }
31720             
31721             Roo.each(box, function(b,kk){
31722                 
31723                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31724                 
31725                 var sz = b.el.getSize();
31726                 
31727                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31728                 
31729             }, this);
31730             
31731         }, this);
31732         
31733         var mY = 0;
31734         
31735         for (var i = 0; i < this.cols; i++){
31736             mY = Math.max(mY, maxY[i]);
31737         }
31738         
31739         this.el.setHeight(mY - pos.y);
31740         
31741     },
31742     
31743 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31744 //    {
31745 //        var pos = this.el.getBox(true);
31746 //        var x = pos.x;
31747 //        var y = pos.y;
31748 //        var maxX = pos.right;
31749 //        
31750 //        var maxHeight = 0;
31751 //        
31752 //        Roo.each(items, function(item, k){
31753 //            
31754 //            var c = k % 2;
31755 //            
31756 //            item.el.position('absolute');
31757 //                
31758 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31759 //
31760 //            item.el.setWidth(width);
31761 //
31762 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31763 //
31764 //            item.el.setHeight(height);
31765 //            
31766 //            if(c == 0){
31767 //                item.el.setXY([x, y], isInstant ? false : true);
31768 //            } else {
31769 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31770 //            }
31771 //            
31772 //            y = y + height + this.alternativePadWidth;
31773 //            
31774 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31775 //            
31776 //        }, this);
31777 //        
31778 //        this.el.setHeight(maxHeight);
31779 //        
31780 //    },
31781     
31782     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31783     {
31784         var pos = this.el.getBox(true);
31785         
31786         var minX = pos.x;
31787         var minY = pos.y;
31788         
31789         var maxX = pos.right;
31790         
31791         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31792         
31793         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31794         
31795         Roo.each(queue, function(box, k){
31796             
31797             Roo.each(box, function(b, kk){
31798                 
31799                 b.el.position('absolute');
31800                 
31801                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31802                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31803                 
31804                 if(b.size == 'md-left' || b.size == 'md-right'){
31805                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31806                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31807                 }
31808                 
31809                 b.el.setWidth(width);
31810                 b.el.setHeight(height);
31811                 
31812             }, this);
31813             
31814             if(!box.length){
31815                 return;
31816             }
31817             
31818             var positions = [];
31819             
31820             switch (box.length){
31821                 case 1 :
31822                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31823                     break;
31824                 case 2 :
31825                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31826                     break;
31827                 case 3 :
31828                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31829                     break;
31830                 case 4 :
31831                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31832                     break;
31833                 default :
31834                     break;
31835             }
31836             
31837             Roo.each(box, function(b,kk){
31838                 
31839                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31840                 
31841                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31842                 
31843             }, this);
31844             
31845         }, this);
31846         
31847     },
31848     
31849     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31850     {
31851         Roo.each(eItems, function(b,k){
31852             
31853             b.size = (k == 0) ? 'sm' : 'xs';
31854             b.x = (k == 0) ? 2 : 1;
31855             b.y = (k == 0) ? 2 : 1;
31856             
31857             b.el.position('absolute');
31858             
31859             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31860                 
31861             b.el.setWidth(width);
31862             
31863             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31864             
31865             b.el.setHeight(height);
31866             
31867         }, this);
31868
31869         var positions = [];
31870         
31871         positions.push({
31872             x : maxX - this.unitWidth * 2 - this.gutter,
31873             y : minY
31874         });
31875         
31876         positions.push({
31877             x : maxX - this.unitWidth,
31878             y : minY + (this.unitWidth + this.gutter) * 2
31879         });
31880         
31881         positions.push({
31882             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31883             y : minY
31884         });
31885         
31886         Roo.each(eItems, function(b,k){
31887             
31888             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31889
31890         }, this);
31891         
31892     },
31893     
31894     getVerticalOneBoxColPositions : function(x, y, box)
31895     {
31896         var pos = [];
31897         
31898         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31899         
31900         if(box[0].size == 'md-left'){
31901             rand = 0;
31902         }
31903         
31904         if(box[0].size == 'md-right'){
31905             rand = 1;
31906         }
31907         
31908         pos.push({
31909             x : x + (this.unitWidth + this.gutter) * rand,
31910             y : y
31911         });
31912         
31913         return pos;
31914     },
31915     
31916     getVerticalTwoBoxColPositions : function(x, y, box)
31917     {
31918         var pos = [];
31919         
31920         if(box[0].size == 'xs'){
31921             
31922             pos.push({
31923                 x : x,
31924                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31925             });
31926
31927             pos.push({
31928                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31929                 y : y
31930             });
31931             
31932             return pos;
31933             
31934         }
31935         
31936         pos.push({
31937             x : x,
31938             y : y
31939         });
31940
31941         pos.push({
31942             x : x + (this.unitWidth + this.gutter) * 2,
31943             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31944         });
31945         
31946         return pos;
31947         
31948     },
31949     
31950     getVerticalThreeBoxColPositions : function(x, y, box)
31951     {
31952         var pos = [];
31953         
31954         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31955             
31956             pos.push({
31957                 x : x,
31958                 y : y
31959             });
31960
31961             pos.push({
31962                 x : x + (this.unitWidth + this.gutter) * 1,
31963                 y : y
31964             });
31965             
31966             pos.push({
31967                 x : x + (this.unitWidth + this.gutter) * 2,
31968                 y : y
31969             });
31970             
31971             return pos;
31972             
31973         }
31974         
31975         if(box[0].size == 'xs' && box[1].size == 'xs'){
31976             
31977             pos.push({
31978                 x : x,
31979                 y : y
31980             });
31981
31982             pos.push({
31983                 x : x,
31984                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31985             });
31986             
31987             pos.push({
31988                 x : x + (this.unitWidth + this.gutter) * 1,
31989                 y : y
31990             });
31991             
31992             return pos;
31993             
31994         }
31995         
31996         pos.push({
31997             x : x,
31998             y : y
31999         });
32000
32001         pos.push({
32002             x : x + (this.unitWidth + this.gutter) * 2,
32003             y : y
32004         });
32005
32006         pos.push({
32007             x : x + (this.unitWidth + this.gutter) * 2,
32008             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32009         });
32010             
32011         return pos;
32012         
32013     },
32014     
32015     getVerticalFourBoxColPositions : function(x, y, box)
32016     {
32017         var pos = [];
32018         
32019         if(box[0].size == 'xs'){
32020             
32021             pos.push({
32022                 x : x,
32023                 y : y
32024             });
32025
32026             pos.push({
32027                 x : x,
32028                 y : y + (this.unitHeight + this.gutter) * 1
32029             });
32030             
32031             pos.push({
32032                 x : x,
32033                 y : y + (this.unitHeight + this.gutter) * 2
32034             });
32035             
32036             pos.push({
32037                 x : x + (this.unitWidth + this.gutter) * 1,
32038                 y : y
32039             });
32040             
32041             return pos;
32042             
32043         }
32044         
32045         pos.push({
32046             x : x,
32047             y : y
32048         });
32049
32050         pos.push({
32051             x : x + (this.unitWidth + this.gutter) * 2,
32052             y : y
32053         });
32054
32055         pos.push({
32056             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32057             y : y + (this.unitHeight + this.gutter) * 1
32058         });
32059
32060         pos.push({
32061             x : x + (this.unitWidth + this.gutter) * 2,
32062             y : y + (this.unitWidth + this.gutter) * 2
32063         });
32064
32065         return pos;
32066         
32067     },
32068     
32069     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32070     {
32071         var pos = [];
32072         
32073         if(box[0].size == 'md-left'){
32074             pos.push({
32075                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32076                 y : minY
32077             });
32078             
32079             return pos;
32080         }
32081         
32082         if(box[0].size == 'md-right'){
32083             pos.push({
32084                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32085                 y : minY + (this.unitWidth + this.gutter) * 1
32086             });
32087             
32088             return pos;
32089         }
32090         
32091         var rand = Math.floor(Math.random() * (4 - box[0].y));
32092         
32093         pos.push({
32094             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32095             y : minY + (this.unitWidth + this.gutter) * rand
32096         });
32097         
32098         return pos;
32099         
32100     },
32101     
32102     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32103     {
32104         var pos = [];
32105         
32106         if(box[0].size == 'xs'){
32107             
32108             pos.push({
32109                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32110                 y : minY
32111             });
32112
32113             pos.push({
32114                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32115                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32116             });
32117             
32118             return pos;
32119             
32120         }
32121         
32122         pos.push({
32123             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32124             y : minY
32125         });
32126
32127         pos.push({
32128             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32129             y : minY + (this.unitWidth + this.gutter) * 2
32130         });
32131         
32132         return pos;
32133         
32134     },
32135     
32136     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32137     {
32138         var pos = [];
32139         
32140         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32141             
32142             pos.push({
32143                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32144                 y : minY
32145             });
32146
32147             pos.push({
32148                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32149                 y : minY + (this.unitWidth + this.gutter) * 1
32150             });
32151             
32152             pos.push({
32153                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32154                 y : minY + (this.unitWidth + this.gutter) * 2
32155             });
32156             
32157             return pos;
32158             
32159         }
32160         
32161         if(box[0].size == 'xs' && box[1].size == 'xs'){
32162             
32163             pos.push({
32164                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32165                 y : minY
32166             });
32167
32168             pos.push({
32169                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32170                 y : minY
32171             });
32172             
32173             pos.push({
32174                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32175                 y : minY + (this.unitWidth + this.gutter) * 1
32176             });
32177             
32178             return pos;
32179             
32180         }
32181         
32182         pos.push({
32183             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32184             y : minY
32185         });
32186
32187         pos.push({
32188             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32189             y : minY + (this.unitWidth + this.gutter) * 2
32190         });
32191
32192         pos.push({
32193             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32194             y : minY + (this.unitWidth + this.gutter) * 2
32195         });
32196             
32197         return pos;
32198         
32199     },
32200     
32201     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32202     {
32203         var pos = [];
32204         
32205         if(box[0].size == 'xs'){
32206             
32207             pos.push({
32208                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32209                 y : minY
32210             });
32211
32212             pos.push({
32213                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32214                 y : minY
32215             });
32216             
32217             pos.push({
32218                 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),
32219                 y : minY
32220             });
32221             
32222             pos.push({
32223                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32224                 y : minY + (this.unitWidth + this.gutter) * 1
32225             });
32226             
32227             return pos;
32228             
32229         }
32230         
32231         pos.push({
32232             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32233             y : minY
32234         });
32235         
32236         pos.push({
32237             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32238             y : minY + (this.unitWidth + this.gutter) * 2
32239         });
32240         
32241         pos.push({
32242             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32243             y : minY + (this.unitWidth + this.gutter) * 2
32244         });
32245         
32246         pos.push({
32247             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),
32248             y : minY + (this.unitWidth + this.gutter) * 2
32249         });
32250
32251         return pos;
32252         
32253     },
32254     
32255     /**
32256     * remove a Masonry Brick
32257     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32258     */
32259     removeBrick : function(brick_id)
32260     {
32261         if (!brick_id) {
32262             return;
32263         }
32264         
32265         for (var i = 0; i<this.bricks.length; i++) {
32266             if (this.bricks[i].id == brick_id) {
32267                 this.bricks.splice(i,1);
32268                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32269                 this.initial();
32270             }
32271         }
32272     },
32273     
32274     /**
32275     * adds a Masonry Brick
32276     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32277     */
32278     addBrick : function(cfg)
32279     {
32280         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32281         //this.register(cn);
32282         cn.parentId = this.id;
32283         cn.render(this.el);
32284         return cn;
32285     },
32286     
32287     /**
32288     * register a Masonry Brick
32289     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32290     */
32291     
32292     register : function(brick)
32293     {
32294         this.bricks.push(brick);
32295         brick.masonryId = this.id;
32296     },
32297     
32298     /**
32299     * clear all the Masonry Brick
32300     */
32301     clearAll : function()
32302     {
32303         this.bricks = [];
32304         //this.getChildContainer().dom.innerHTML = "";
32305         this.el.dom.innerHTML = '';
32306     },
32307     
32308     getSelected : function()
32309     {
32310         if (!this.selectedBrick) {
32311             return false;
32312         }
32313         
32314         return this.selectedBrick;
32315     }
32316 });
32317
32318 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32319     
32320     groups: {},
32321      /**
32322     * register a Masonry Layout
32323     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32324     */
32325     
32326     register : function(layout)
32327     {
32328         this.groups[layout.id] = layout;
32329     },
32330     /**
32331     * fetch a  Masonry Layout based on the masonry layout ID
32332     * @param {string} the masonry layout to add
32333     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32334     */
32335     
32336     get: function(layout_id) {
32337         if (typeof(this.groups[layout_id]) == 'undefined') {
32338             return false;
32339         }
32340         return this.groups[layout_id] ;
32341     }
32342     
32343     
32344     
32345 });
32346
32347  
32348
32349  /**
32350  *
32351  * This is based on 
32352  * http://masonry.desandro.com
32353  *
32354  * The idea is to render all the bricks based on vertical width...
32355  *
32356  * The original code extends 'outlayer' - we might need to use that....
32357  * 
32358  */
32359
32360
32361 /**
32362  * @class Roo.bootstrap.LayoutMasonryAuto
32363  * @extends Roo.bootstrap.Component
32364  * Bootstrap Layout Masonry class
32365  * 
32366  * @constructor
32367  * Create a new Element
32368  * @param {Object} config The config object
32369  */
32370
32371 Roo.bootstrap.LayoutMasonryAuto = function(config){
32372     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32373 };
32374
32375 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32376     
32377       /**
32378      * @cfg {Boolean} isFitWidth  - resize the width..
32379      */   
32380     isFitWidth : false,  // options..
32381     /**
32382      * @cfg {Boolean} isOriginLeft = left align?
32383      */   
32384     isOriginLeft : true,
32385     /**
32386      * @cfg {Boolean} isOriginTop = top align?
32387      */   
32388     isOriginTop : false,
32389     /**
32390      * @cfg {Boolean} isLayoutInstant = no animation?
32391      */   
32392     isLayoutInstant : false, // needed?
32393     /**
32394      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32395      */   
32396     isResizingContainer : true,
32397     /**
32398      * @cfg {Number} columnWidth  width of the columns 
32399      */   
32400     
32401     columnWidth : 0,
32402     
32403     /**
32404      * @cfg {Number} maxCols maximum number of columns
32405      */   
32406     
32407     maxCols: 0,
32408     /**
32409      * @cfg {Number} padHeight padding below box..
32410      */   
32411     
32412     padHeight : 10, 
32413     
32414     /**
32415      * @cfg {Boolean} isAutoInitial defalut true
32416      */   
32417     
32418     isAutoInitial : true, 
32419     
32420     // private?
32421     gutter : 0,
32422     
32423     containerWidth: 0,
32424     initialColumnWidth : 0,
32425     currentSize : null,
32426     
32427     colYs : null, // array.
32428     maxY : 0,
32429     padWidth: 10,
32430     
32431     
32432     tag: 'div',
32433     cls: '',
32434     bricks: null, //CompositeElement
32435     cols : 0, // array?
32436     // element : null, // wrapped now this.el
32437     _isLayoutInited : null, 
32438     
32439     
32440     getAutoCreate : function(){
32441         
32442         var cfg = {
32443             tag: this.tag,
32444             cls: 'blog-masonary-wrapper ' + this.cls,
32445             cn : {
32446                 cls : 'mas-boxes masonary'
32447             }
32448         };
32449         
32450         return cfg;
32451     },
32452     
32453     getChildContainer: function( )
32454     {
32455         if (this.boxesEl) {
32456             return this.boxesEl;
32457         }
32458         
32459         this.boxesEl = this.el.select('.mas-boxes').first();
32460         
32461         return this.boxesEl;
32462     },
32463     
32464     
32465     initEvents : function()
32466     {
32467         var _this = this;
32468         
32469         if(this.isAutoInitial){
32470             Roo.log('hook children rendered');
32471             this.on('childrenrendered', function() {
32472                 Roo.log('children rendered');
32473                 _this.initial();
32474             } ,this);
32475         }
32476         
32477     },
32478     
32479     initial : function()
32480     {
32481         this.reloadItems();
32482
32483         this.currentSize = this.el.getBox(true);
32484
32485         /// was window resize... - let's see if this works..
32486         Roo.EventManager.onWindowResize(this.resize, this); 
32487
32488         if(!this.isAutoInitial){
32489             this.layout();
32490             return;
32491         }
32492         
32493         this.layout.defer(500,this);
32494     },
32495     
32496     reloadItems: function()
32497     {
32498         this.bricks = this.el.select('.masonry-brick', true);
32499         
32500         this.bricks.each(function(b) {
32501             //Roo.log(b.getSize());
32502             if (!b.attr('originalwidth')) {
32503                 b.attr('originalwidth',  b.getSize().width);
32504             }
32505             
32506         });
32507         
32508         Roo.log(this.bricks.elements.length);
32509     },
32510     
32511     resize : function()
32512     {
32513         Roo.log('resize');
32514         var cs = this.el.getBox(true);
32515         
32516         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32517             Roo.log("no change in with or X");
32518             return;
32519         }
32520         this.currentSize = cs;
32521         this.layout();
32522     },
32523     
32524     layout : function()
32525     {
32526          Roo.log('layout');
32527         this._resetLayout();
32528         //this._manageStamps();
32529       
32530         // don't animate first layout
32531         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32532         this.layoutItems( isInstant );
32533       
32534         // flag for initalized
32535         this._isLayoutInited = true;
32536     },
32537     
32538     layoutItems : function( isInstant )
32539     {
32540         //var items = this._getItemsForLayout( this.items );
32541         // original code supports filtering layout items.. we just ignore it..
32542         
32543         this._layoutItems( this.bricks , isInstant );
32544       
32545         this._postLayout();
32546     },
32547     _layoutItems : function ( items , isInstant)
32548     {
32549        //this.fireEvent( 'layout', this, items );
32550     
32551
32552         if ( !items || !items.elements.length ) {
32553           // no items, emit event with empty array
32554             return;
32555         }
32556
32557         var queue = [];
32558         items.each(function(item) {
32559             Roo.log("layout item");
32560             Roo.log(item);
32561             // get x/y object from method
32562             var position = this._getItemLayoutPosition( item );
32563             // enqueue
32564             position.item = item;
32565             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32566             queue.push( position );
32567         }, this);
32568       
32569         this._processLayoutQueue( queue );
32570     },
32571     /** Sets position of item in DOM
32572     * @param {Element} item
32573     * @param {Number} x - horizontal position
32574     * @param {Number} y - vertical position
32575     * @param {Boolean} isInstant - disables transitions
32576     */
32577     _processLayoutQueue : function( queue )
32578     {
32579         for ( var i=0, len = queue.length; i < len; i++ ) {
32580             var obj = queue[i];
32581             obj.item.position('absolute');
32582             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32583         }
32584     },
32585       
32586     
32587     /**
32588     * Any logic you want to do after each layout,
32589     * i.e. size the container
32590     */
32591     _postLayout : function()
32592     {
32593         this.resizeContainer();
32594     },
32595     
32596     resizeContainer : function()
32597     {
32598         if ( !this.isResizingContainer ) {
32599             return;
32600         }
32601         var size = this._getContainerSize();
32602         if ( size ) {
32603             this.el.setSize(size.width,size.height);
32604             this.boxesEl.setSize(size.width,size.height);
32605         }
32606     },
32607     
32608     
32609     
32610     _resetLayout : function()
32611     {
32612         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32613         this.colWidth = this.el.getWidth();
32614         //this.gutter = this.el.getWidth(); 
32615         
32616         this.measureColumns();
32617
32618         // reset column Y
32619         var i = this.cols;
32620         this.colYs = [];
32621         while (i--) {
32622             this.colYs.push( 0 );
32623         }
32624     
32625         this.maxY = 0;
32626     },
32627
32628     measureColumns : function()
32629     {
32630         this.getContainerWidth();
32631       // if columnWidth is 0, default to outerWidth of first item
32632         if ( !this.columnWidth ) {
32633             var firstItem = this.bricks.first();
32634             Roo.log(firstItem);
32635             this.columnWidth  = this.containerWidth;
32636             if (firstItem && firstItem.attr('originalwidth') ) {
32637                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32638             }
32639             // columnWidth fall back to item of first element
32640             Roo.log("set column width?");
32641                         this.initialColumnWidth = this.columnWidth  ;
32642
32643             // if first elem has no width, default to size of container
32644             
32645         }
32646         
32647         
32648         if (this.initialColumnWidth) {
32649             this.columnWidth = this.initialColumnWidth;
32650         }
32651         
32652         
32653             
32654         // column width is fixed at the top - however if container width get's smaller we should
32655         // reduce it...
32656         
32657         // this bit calcs how man columns..
32658             
32659         var columnWidth = this.columnWidth += this.gutter;
32660       
32661         // calculate columns
32662         var containerWidth = this.containerWidth + this.gutter;
32663         
32664         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32665         // fix rounding errors, typically with gutters
32666         var excess = columnWidth - containerWidth % columnWidth;
32667         
32668         
32669         // if overshoot is less than a pixel, round up, otherwise floor it
32670         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32671         cols = Math[ mathMethod ]( cols );
32672         this.cols = Math.max( cols, 1 );
32673         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32674         
32675          // padding positioning..
32676         var totalColWidth = this.cols * this.columnWidth;
32677         var padavail = this.containerWidth - totalColWidth;
32678         // so for 2 columns - we need 3 'pads'
32679         
32680         var padNeeded = (1+this.cols) * this.padWidth;
32681         
32682         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32683         
32684         this.columnWidth += padExtra
32685         //this.padWidth = Math.floor(padavail /  ( this.cols));
32686         
32687         // adjust colum width so that padding is fixed??
32688         
32689         // we have 3 columns ... total = width * 3
32690         // we have X left over... that should be used by 
32691         
32692         //if (this.expandC) {
32693             
32694         //}
32695         
32696         
32697         
32698     },
32699     
32700     getContainerWidth : function()
32701     {
32702        /* // container is parent if fit width
32703         var container = this.isFitWidth ? this.element.parentNode : this.element;
32704         // check that this.size and size are there
32705         // IE8 triggers resize on body size change, so they might not be
32706         
32707         var size = getSize( container );  //FIXME
32708         this.containerWidth = size && size.innerWidth; //FIXME
32709         */
32710          
32711         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32712         
32713     },
32714     
32715     _getItemLayoutPosition : function( item )  // what is item?
32716     {
32717         // we resize the item to our columnWidth..
32718       
32719         item.setWidth(this.columnWidth);
32720         item.autoBoxAdjust  = false;
32721         
32722         var sz = item.getSize();
32723  
32724         // how many columns does this brick span
32725         var remainder = this.containerWidth % this.columnWidth;
32726         
32727         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32728         // round if off by 1 pixel, otherwise use ceil
32729         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32730         colSpan = Math.min( colSpan, this.cols );
32731         
32732         // normally this should be '1' as we dont' currently allow multi width columns..
32733         
32734         var colGroup = this._getColGroup( colSpan );
32735         // get the minimum Y value from the columns
32736         var minimumY = Math.min.apply( Math, colGroup );
32737         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32738         
32739         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32740          
32741         // position the brick
32742         var position = {
32743             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32744             y: this.currentSize.y + minimumY + this.padHeight
32745         };
32746         
32747         Roo.log(position);
32748         // apply setHeight to necessary columns
32749         var setHeight = minimumY + sz.height + this.padHeight;
32750         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32751         
32752         var setSpan = this.cols + 1 - colGroup.length;
32753         for ( var i = 0; i < setSpan; i++ ) {
32754           this.colYs[ shortColIndex + i ] = setHeight ;
32755         }
32756       
32757         return position;
32758     },
32759     
32760     /**
32761      * @param {Number} colSpan - number of columns the element spans
32762      * @returns {Array} colGroup
32763      */
32764     _getColGroup : function( colSpan )
32765     {
32766         if ( colSpan < 2 ) {
32767           // if brick spans only one column, use all the column Ys
32768           return this.colYs;
32769         }
32770       
32771         var colGroup = [];
32772         // how many different places could this brick fit horizontally
32773         var groupCount = this.cols + 1 - colSpan;
32774         // for each group potential horizontal position
32775         for ( var i = 0; i < groupCount; i++ ) {
32776           // make an array of colY values for that one group
32777           var groupColYs = this.colYs.slice( i, i + colSpan );
32778           // and get the max value of the array
32779           colGroup[i] = Math.max.apply( Math, groupColYs );
32780         }
32781         return colGroup;
32782     },
32783     /*
32784     _manageStamp : function( stamp )
32785     {
32786         var stampSize =  stamp.getSize();
32787         var offset = stamp.getBox();
32788         // get the columns that this stamp affects
32789         var firstX = this.isOriginLeft ? offset.x : offset.right;
32790         var lastX = firstX + stampSize.width;
32791         var firstCol = Math.floor( firstX / this.columnWidth );
32792         firstCol = Math.max( 0, firstCol );
32793         
32794         var lastCol = Math.floor( lastX / this.columnWidth );
32795         // lastCol should not go over if multiple of columnWidth #425
32796         lastCol -= lastX % this.columnWidth ? 0 : 1;
32797         lastCol = Math.min( this.cols - 1, lastCol );
32798         
32799         // set colYs to bottom of the stamp
32800         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32801             stampSize.height;
32802             
32803         for ( var i = firstCol; i <= lastCol; i++ ) {
32804           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32805         }
32806     },
32807     */
32808     
32809     _getContainerSize : function()
32810     {
32811         this.maxY = Math.max.apply( Math, this.colYs );
32812         var size = {
32813             height: this.maxY
32814         };
32815       
32816         if ( this.isFitWidth ) {
32817             size.width = this._getContainerFitWidth();
32818         }
32819       
32820         return size;
32821     },
32822     
32823     _getContainerFitWidth : function()
32824     {
32825         var unusedCols = 0;
32826         // count unused columns
32827         var i = this.cols;
32828         while ( --i ) {
32829           if ( this.colYs[i] !== 0 ) {
32830             break;
32831           }
32832           unusedCols++;
32833         }
32834         // fit container to columns that have been used
32835         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32836     },
32837     
32838     needsResizeLayout : function()
32839     {
32840         var previousWidth = this.containerWidth;
32841         this.getContainerWidth();
32842         return previousWidth !== this.containerWidth;
32843     }
32844  
32845 });
32846
32847  
32848
32849  /*
32850  * - LGPL
32851  *
32852  * element
32853  * 
32854  */
32855
32856 /**
32857  * @class Roo.bootstrap.MasonryBrick
32858  * @extends Roo.bootstrap.Component
32859  * Bootstrap MasonryBrick class
32860  * 
32861  * @constructor
32862  * Create a new MasonryBrick
32863  * @param {Object} config The config object
32864  */
32865
32866 Roo.bootstrap.MasonryBrick = function(config){
32867     
32868     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32869     
32870     Roo.bootstrap.MasonryBrick.register(this);
32871     
32872     this.addEvents({
32873         // raw events
32874         /**
32875          * @event click
32876          * When a MasonryBrick is clcik
32877          * @param {Roo.bootstrap.MasonryBrick} this
32878          * @param {Roo.EventObject} e
32879          */
32880         "click" : true
32881     });
32882 };
32883
32884 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32885     
32886     /**
32887      * @cfg {String} title
32888      */   
32889     title : '',
32890     /**
32891      * @cfg {String} html
32892      */   
32893     html : '',
32894     /**
32895      * @cfg {String} bgimage
32896      */   
32897     bgimage : '',
32898     /**
32899      * @cfg {String} videourl
32900      */   
32901     videourl : '',
32902     /**
32903      * @cfg {String} cls
32904      */   
32905     cls : '',
32906     /**
32907      * @cfg {String} href
32908      */   
32909     href : '',
32910     /**
32911      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32912      */   
32913     size : 'xs',
32914     
32915     /**
32916      * @cfg {String} placetitle (center|bottom)
32917      */   
32918     placetitle : '',
32919     
32920     /**
32921      * @cfg {Boolean} isFitContainer defalut true
32922      */   
32923     isFitContainer : true, 
32924     
32925     /**
32926      * @cfg {Boolean} preventDefault defalut false
32927      */   
32928     preventDefault : false, 
32929     
32930     /**
32931      * @cfg {Boolean} inverse defalut false
32932      */   
32933     maskInverse : false, 
32934     
32935     getAutoCreate : function()
32936     {
32937         if(!this.isFitContainer){
32938             return this.getSplitAutoCreate();
32939         }
32940         
32941         var cls = 'masonry-brick masonry-brick-full';
32942         
32943         if(this.href.length){
32944             cls += ' masonry-brick-link';
32945         }
32946         
32947         if(this.bgimage.length){
32948             cls += ' masonry-brick-image';
32949         }
32950         
32951         if(this.maskInverse){
32952             cls += ' mask-inverse';
32953         }
32954         
32955         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32956             cls += ' enable-mask';
32957         }
32958         
32959         if(this.size){
32960             cls += ' masonry-' + this.size + '-brick';
32961         }
32962         
32963         if(this.placetitle.length){
32964             
32965             switch (this.placetitle) {
32966                 case 'center' :
32967                     cls += ' masonry-center-title';
32968                     break;
32969                 case 'bottom' :
32970                     cls += ' masonry-bottom-title';
32971                     break;
32972                 default:
32973                     break;
32974             }
32975             
32976         } else {
32977             if(!this.html.length && !this.bgimage.length){
32978                 cls += ' masonry-center-title';
32979             }
32980
32981             if(!this.html.length && this.bgimage.length){
32982                 cls += ' masonry-bottom-title';
32983             }
32984         }
32985         
32986         if(this.cls){
32987             cls += ' ' + this.cls;
32988         }
32989         
32990         var cfg = {
32991             tag: (this.href.length) ? 'a' : 'div',
32992             cls: cls,
32993             cn: [
32994                 {
32995                     tag: 'div',
32996                     cls: 'masonry-brick-mask'
32997                 },
32998                 {
32999                     tag: 'div',
33000                     cls: 'masonry-brick-paragraph',
33001                     cn: []
33002                 }
33003             ]
33004         };
33005         
33006         if(this.href.length){
33007             cfg.href = this.href;
33008         }
33009         
33010         var cn = cfg.cn[1].cn;
33011         
33012         if(this.title.length){
33013             cn.push({
33014                 tag: 'h4',
33015                 cls: 'masonry-brick-title',
33016                 html: this.title
33017             });
33018         }
33019         
33020         if(this.html.length){
33021             cn.push({
33022                 tag: 'p',
33023                 cls: 'masonry-brick-text',
33024                 html: this.html
33025             });
33026         }
33027         
33028         if (!this.title.length && !this.html.length) {
33029             cfg.cn[1].cls += ' hide';
33030         }
33031         
33032         if(this.bgimage.length){
33033             cfg.cn.push({
33034                 tag: 'img',
33035                 cls: 'masonry-brick-image-view',
33036                 src: this.bgimage
33037             });
33038         }
33039         
33040         if(this.videourl.length){
33041             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33042             // youtube support only?
33043             cfg.cn.push({
33044                 tag: 'iframe',
33045                 cls: 'masonry-brick-image-view',
33046                 src: vurl,
33047                 frameborder : 0,
33048                 allowfullscreen : true
33049             });
33050         }
33051         
33052         return cfg;
33053         
33054     },
33055     
33056     getSplitAutoCreate : function()
33057     {
33058         var cls = 'masonry-brick masonry-brick-split';
33059         
33060         if(this.href.length){
33061             cls += ' masonry-brick-link';
33062         }
33063         
33064         if(this.bgimage.length){
33065             cls += ' masonry-brick-image';
33066         }
33067         
33068         if(this.size){
33069             cls += ' masonry-' + this.size + '-brick';
33070         }
33071         
33072         switch (this.placetitle) {
33073             case 'center' :
33074                 cls += ' masonry-center-title';
33075                 break;
33076             case 'bottom' :
33077                 cls += ' masonry-bottom-title';
33078                 break;
33079             default:
33080                 if(!this.bgimage.length){
33081                     cls += ' masonry-center-title';
33082                 }
33083
33084                 if(this.bgimage.length){
33085                     cls += ' masonry-bottom-title';
33086                 }
33087                 break;
33088         }
33089         
33090         if(this.cls){
33091             cls += ' ' + this.cls;
33092         }
33093         
33094         var cfg = {
33095             tag: (this.href.length) ? 'a' : 'div',
33096             cls: cls,
33097             cn: [
33098                 {
33099                     tag: 'div',
33100                     cls: 'masonry-brick-split-head',
33101                     cn: [
33102                         {
33103                             tag: 'div',
33104                             cls: 'masonry-brick-paragraph',
33105                             cn: []
33106                         }
33107                     ]
33108                 },
33109                 {
33110                     tag: 'div',
33111                     cls: 'masonry-brick-split-body',
33112                     cn: []
33113                 }
33114             ]
33115         };
33116         
33117         if(this.href.length){
33118             cfg.href = this.href;
33119         }
33120         
33121         if(this.title.length){
33122             cfg.cn[0].cn[0].cn.push({
33123                 tag: 'h4',
33124                 cls: 'masonry-brick-title',
33125                 html: this.title
33126             });
33127         }
33128         
33129         if(this.html.length){
33130             cfg.cn[1].cn.push({
33131                 tag: 'p',
33132                 cls: 'masonry-brick-text',
33133                 html: this.html
33134             });
33135         }
33136
33137         if(this.bgimage.length){
33138             cfg.cn[0].cn.push({
33139                 tag: 'img',
33140                 cls: 'masonry-brick-image-view',
33141                 src: this.bgimage
33142             });
33143         }
33144         
33145         if(this.videourl.length){
33146             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33147             // youtube support only?
33148             cfg.cn[0].cn.cn.push({
33149                 tag: 'iframe',
33150                 cls: 'masonry-brick-image-view',
33151                 src: vurl,
33152                 frameborder : 0,
33153                 allowfullscreen : true
33154             });
33155         }
33156         
33157         return cfg;
33158     },
33159     
33160     initEvents: function() 
33161     {
33162         switch (this.size) {
33163             case 'xs' :
33164                 this.x = 1;
33165                 this.y = 1;
33166                 break;
33167             case 'sm' :
33168                 this.x = 2;
33169                 this.y = 2;
33170                 break;
33171             case 'md' :
33172             case 'md-left' :
33173             case 'md-right' :
33174                 this.x = 3;
33175                 this.y = 3;
33176                 break;
33177             case 'tall' :
33178                 this.x = 2;
33179                 this.y = 3;
33180                 break;
33181             case 'wide' :
33182                 this.x = 3;
33183                 this.y = 2;
33184                 break;
33185             case 'wide-thin' :
33186                 this.x = 3;
33187                 this.y = 1;
33188                 break;
33189                         
33190             default :
33191                 break;
33192         }
33193         
33194         if(Roo.isTouch){
33195             this.el.on('touchstart', this.onTouchStart, this);
33196             this.el.on('touchmove', this.onTouchMove, this);
33197             this.el.on('touchend', this.onTouchEnd, this);
33198             this.el.on('contextmenu', this.onContextMenu, this);
33199         } else {
33200             this.el.on('mouseenter'  ,this.enter, this);
33201             this.el.on('mouseleave', this.leave, this);
33202             this.el.on('click', this.onClick, this);
33203         }
33204         
33205         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33206             this.parent().bricks.push(this);   
33207         }
33208         
33209     },
33210     
33211     onClick: function(e, el)
33212     {
33213         var time = this.endTimer - this.startTimer;
33214         // Roo.log(e.preventDefault());
33215         if(Roo.isTouch){
33216             if(time > 1000){
33217                 e.preventDefault();
33218                 return;
33219             }
33220         }
33221         
33222         if(!this.preventDefault){
33223             return;
33224         }
33225         
33226         e.preventDefault();
33227         
33228         if (this.activeClass != '') {
33229             this.selectBrick();
33230         }
33231         
33232         this.fireEvent('click', this, e);
33233     },
33234     
33235     enter: function(e, el)
33236     {
33237         e.preventDefault();
33238         
33239         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33240             return;
33241         }
33242         
33243         if(this.bgimage.length && this.html.length){
33244             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33245         }
33246     },
33247     
33248     leave: function(e, el)
33249     {
33250         e.preventDefault();
33251         
33252         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33253             return;
33254         }
33255         
33256         if(this.bgimage.length && this.html.length){
33257             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33258         }
33259     },
33260     
33261     onTouchStart: function(e, el)
33262     {
33263 //        e.preventDefault();
33264         
33265         this.touchmoved = false;
33266         
33267         if(!this.isFitContainer){
33268             return;
33269         }
33270         
33271         if(!this.bgimage.length || !this.html.length){
33272             return;
33273         }
33274         
33275         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33276         
33277         this.timer = new Date().getTime();
33278         
33279     },
33280     
33281     onTouchMove: function(e, el)
33282     {
33283         this.touchmoved = true;
33284     },
33285     
33286     onContextMenu : function(e,el)
33287     {
33288         e.preventDefault();
33289         e.stopPropagation();
33290         return false;
33291     },
33292     
33293     onTouchEnd: function(e, el)
33294     {
33295 //        e.preventDefault();
33296         
33297         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33298         
33299             this.leave(e,el);
33300             
33301             return;
33302         }
33303         
33304         if(!this.bgimage.length || !this.html.length){
33305             
33306             if(this.href.length){
33307                 window.location.href = this.href;
33308             }
33309             
33310             return;
33311         }
33312         
33313         if(!this.isFitContainer){
33314             return;
33315         }
33316         
33317         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33318         
33319         window.location.href = this.href;
33320     },
33321     
33322     //selection on single brick only
33323     selectBrick : function() {
33324         
33325         if (!this.parentId) {
33326             return;
33327         }
33328         
33329         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33330         var index = m.selectedBrick.indexOf(this.id);
33331         
33332         if ( index > -1) {
33333             m.selectedBrick.splice(index,1);
33334             this.el.removeClass(this.activeClass);
33335             return;
33336         }
33337         
33338         for(var i = 0; i < m.selectedBrick.length; i++) {
33339             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33340             b.el.removeClass(b.activeClass);
33341         }
33342         
33343         m.selectedBrick = [];
33344         
33345         m.selectedBrick.push(this.id);
33346         this.el.addClass(this.activeClass);
33347         return;
33348     },
33349     
33350     isSelected : function(){
33351         return this.el.hasClass(this.activeClass);
33352         
33353     }
33354 });
33355
33356 Roo.apply(Roo.bootstrap.MasonryBrick, {
33357     
33358     //groups: {},
33359     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33360      /**
33361     * register a Masonry Brick
33362     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33363     */
33364     
33365     register : function(brick)
33366     {
33367         //this.groups[brick.id] = brick;
33368         this.groups.add(brick.id, brick);
33369     },
33370     /**
33371     * fetch a  masonry brick based on the masonry brick ID
33372     * @param {string} the masonry brick to add
33373     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33374     */
33375     
33376     get: function(brick_id) 
33377     {
33378         // if (typeof(this.groups[brick_id]) == 'undefined') {
33379         //     return false;
33380         // }
33381         // return this.groups[brick_id] ;
33382         
33383         if(this.groups.key(brick_id)) {
33384             return this.groups.key(brick_id);
33385         }
33386         
33387         return false;
33388     }
33389     
33390     
33391     
33392 });
33393
33394  /*
33395  * - LGPL
33396  *
33397  * element
33398  * 
33399  */
33400
33401 /**
33402  * @class Roo.bootstrap.Brick
33403  * @extends Roo.bootstrap.Component
33404  * Bootstrap Brick class
33405  * 
33406  * @constructor
33407  * Create a new Brick
33408  * @param {Object} config The config object
33409  */
33410
33411 Roo.bootstrap.Brick = function(config){
33412     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33413     
33414     this.addEvents({
33415         // raw events
33416         /**
33417          * @event click
33418          * When a Brick is click
33419          * @param {Roo.bootstrap.Brick} this
33420          * @param {Roo.EventObject} e
33421          */
33422         "click" : true
33423     });
33424 };
33425
33426 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33427     
33428     /**
33429      * @cfg {String} title
33430      */   
33431     title : '',
33432     /**
33433      * @cfg {String} html
33434      */   
33435     html : '',
33436     /**
33437      * @cfg {String} bgimage
33438      */   
33439     bgimage : '',
33440     /**
33441      * @cfg {String} cls
33442      */   
33443     cls : '',
33444     /**
33445      * @cfg {String} href
33446      */   
33447     href : '',
33448     /**
33449      * @cfg {String} video
33450      */   
33451     video : '',
33452     /**
33453      * @cfg {Boolean} square
33454      */   
33455     square : true,
33456     
33457     getAutoCreate : function()
33458     {
33459         var cls = 'roo-brick';
33460         
33461         if(this.href.length){
33462             cls += ' roo-brick-link';
33463         }
33464         
33465         if(this.bgimage.length){
33466             cls += ' roo-brick-image';
33467         }
33468         
33469         if(!this.html.length && !this.bgimage.length){
33470             cls += ' roo-brick-center-title';
33471         }
33472         
33473         if(!this.html.length && this.bgimage.length){
33474             cls += ' roo-brick-bottom-title';
33475         }
33476         
33477         if(this.cls){
33478             cls += ' ' + this.cls;
33479         }
33480         
33481         var cfg = {
33482             tag: (this.href.length) ? 'a' : 'div',
33483             cls: cls,
33484             cn: [
33485                 {
33486                     tag: 'div',
33487                     cls: 'roo-brick-paragraph',
33488                     cn: []
33489                 }
33490             ]
33491         };
33492         
33493         if(this.href.length){
33494             cfg.href = this.href;
33495         }
33496         
33497         var cn = cfg.cn[0].cn;
33498         
33499         if(this.title.length){
33500             cn.push({
33501                 tag: 'h4',
33502                 cls: 'roo-brick-title',
33503                 html: this.title
33504             });
33505         }
33506         
33507         if(this.html.length){
33508             cn.push({
33509                 tag: 'p',
33510                 cls: 'roo-brick-text',
33511                 html: this.html
33512             });
33513         } else {
33514             cn.cls += ' hide';
33515         }
33516         
33517         if(this.bgimage.length){
33518             cfg.cn.push({
33519                 tag: 'img',
33520                 cls: 'roo-brick-image-view',
33521                 src: this.bgimage
33522             });
33523         }
33524         
33525         return cfg;
33526     },
33527     
33528     initEvents: function() 
33529     {
33530         if(this.title.length || this.html.length){
33531             this.el.on('mouseenter'  ,this.enter, this);
33532             this.el.on('mouseleave', this.leave, this);
33533         }
33534         
33535         Roo.EventManager.onWindowResize(this.resize, this); 
33536         
33537         if(this.bgimage.length){
33538             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33539             this.imageEl.on('load', this.onImageLoad, this);
33540             return;
33541         }
33542         
33543         this.resize();
33544     },
33545     
33546     onImageLoad : function()
33547     {
33548         this.resize();
33549     },
33550     
33551     resize : function()
33552     {
33553         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33554         
33555         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33556         
33557         if(this.bgimage.length){
33558             var image = this.el.select('.roo-brick-image-view', true).first();
33559             
33560             image.setWidth(paragraph.getWidth());
33561             
33562             if(this.square){
33563                 image.setHeight(paragraph.getWidth());
33564             }
33565             
33566             this.el.setHeight(image.getHeight());
33567             paragraph.setHeight(image.getHeight());
33568             
33569         }
33570         
33571     },
33572     
33573     enter: function(e, el)
33574     {
33575         e.preventDefault();
33576         
33577         if(this.bgimage.length){
33578             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33579             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33580         }
33581     },
33582     
33583     leave: function(e, el)
33584     {
33585         e.preventDefault();
33586         
33587         if(this.bgimage.length){
33588             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33589             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33590         }
33591     }
33592     
33593 });
33594
33595  
33596
33597  /*
33598  * - LGPL
33599  *
33600  * Number field 
33601  */
33602
33603 /**
33604  * @class Roo.bootstrap.NumberField
33605  * @extends Roo.bootstrap.Input
33606  * Bootstrap NumberField class
33607  * 
33608  * 
33609  * 
33610  * 
33611  * @constructor
33612  * Create a new NumberField
33613  * @param {Object} config The config object
33614  */
33615
33616 Roo.bootstrap.NumberField = function(config){
33617     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33618 };
33619
33620 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33621     
33622     /**
33623      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33624      */
33625     allowDecimals : true,
33626     /**
33627      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33628      */
33629     decimalSeparator : ".",
33630     /**
33631      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33632      */
33633     decimalPrecision : 2,
33634     /**
33635      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33636      */
33637     allowNegative : true,
33638     
33639     /**
33640      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33641      */
33642     allowZero: true,
33643     /**
33644      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33645      */
33646     minValue : Number.NEGATIVE_INFINITY,
33647     /**
33648      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33649      */
33650     maxValue : Number.MAX_VALUE,
33651     /**
33652      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33653      */
33654     minText : "The minimum value for this field is {0}",
33655     /**
33656      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33657      */
33658     maxText : "The maximum value for this field is {0}",
33659     /**
33660      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33661      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33662      */
33663     nanText : "{0} is not a valid number",
33664     /**
33665      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33666      */
33667     thousandsDelimiter : false,
33668     /**
33669      * @cfg {String} valueAlign alignment of value
33670      */
33671     valueAlign : "left",
33672
33673     getAutoCreate : function()
33674     {
33675         var hiddenInput = {
33676             tag: 'input',
33677             type: 'hidden',
33678             id: Roo.id(),
33679             cls: 'hidden-number-input'
33680         };
33681         
33682         if (this.name) {
33683             hiddenInput.name = this.name;
33684         }
33685         
33686         this.name = '';
33687         
33688         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33689         
33690         this.name = hiddenInput.name;
33691         
33692         if(cfg.cn.length > 0) {
33693             cfg.cn.push(hiddenInput);
33694         }
33695         
33696         return cfg;
33697     },
33698
33699     // private
33700     initEvents : function()
33701     {   
33702         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33703         
33704         var allowed = "0123456789";
33705         
33706         if(this.allowDecimals){
33707             allowed += this.decimalSeparator;
33708         }
33709         
33710         if(this.allowNegative){
33711             allowed += "-";
33712         }
33713         
33714         if(this.thousandsDelimiter) {
33715             allowed += ",";
33716         }
33717         
33718         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33719         
33720         var keyPress = function(e){
33721             
33722             var k = e.getKey();
33723             
33724             var c = e.getCharCode();
33725             
33726             if(
33727                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33728                     allowed.indexOf(String.fromCharCode(c)) === -1
33729             ){
33730                 e.stopEvent();
33731                 return;
33732             }
33733             
33734             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33735                 return;
33736             }
33737             
33738             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33739                 e.stopEvent();
33740             }
33741         };
33742         
33743         this.el.on("keypress", keyPress, this);
33744     },
33745     
33746     validateValue : function(value)
33747     {
33748         
33749         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33750             return false;
33751         }
33752         
33753         var num = this.parseValue(value);
33754         
33755         if(isNaN(num)){
33756             this.markInvalid(String.format(this.nanText, value));
33757             return false;
33758         }
33759         
33760         if(num < this.minValue){
33761             this.markInvalid(String.format(this.minText, this.minValue));
33762             return false;
33763         }
33764         
33765         if(num > this.maxValue){
33766             this.markInvalid(String.format(this.maxText, this.maxValue));
33767             return false;
33768         }
33769         
33770         return true;
33771     },
33772
33773     getValue : function()
33774     {
33775         var v = this.hiddenEl().getValue();
33776         
33777         return this.fixPrecision(this.parseValue(v));
33778     },
33779
33780     parseValue : function(value)
33781     {
33782         if(this.thousandsDelimiter) {
33783             value += "";
33784             r = new RegExp(",", "g");
33785             value = value.replace(r, "");
33786         }
33787         
33788         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33789         return isNaN(value) ? '' : value;
33790     },
33791
33792     fixPrecision : function(value)
33793     {
33794         if(this.thousandsDelimiter) {
33795             value += "";
33796             r = new RegExp(",", "g");
33797             value = value.replace(r, "");
33798         }
33799         
33800         var nan = isNaN(value);
33801         
33802         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33803             return nan ? '' : value;
33804         }
33805         return parseFloat(value).toFixed(this.decimalPrecision);
33806     },
33807
33808     setValue : function(v)
33809     {
33810         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33811         
33812         this.value = v;
33813         
33814         if(this.rendered){
33815             
33816             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33817             
33818             this.inputEl().dom.value = (v == '') ? '' :
33819                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33820             
33821             if(!this.allowZero && v === '0') {
33822                 this.hiddenEl().dom.value = '';
33823                 this.inputEl().dom.value = '';
33824             }
33825             
33826             this.validate();
33827         }
33828     },
33829
33830     decimalPrecisionFcn : function(v)
33831     {
33832         return Math.floor(v);
33833     },
33834
33835     beforeBlur : function()
33836     {
33837         var v = this.parseValue(this.getRawValue());
33838         
33839         if(v || v === 0 || v === ''){
33840             this.setValue(v);
33841         }
33842     },
33843     
33844     hiddenEl : function()
33845     {
33846         return this.el.select('input.hidden-number-input',true).first();
33847     }
33848     
33849 });
33850
33851  
33852
33853 /*
33854 * Licence: LGPL
33855 */
33856
33857 /**
33858  * @class Roo.bootstrap.DocumentSlider
33859  * @extends Roo.bootstrap.Component
33860  * Bootstrap DocumentSlider class
33861  * 
33862  * @constructor
33863  * Create a new DocumentViewer
33864  * @param {Object} config The config object
33865  */
33866
33867 Roo.bootstrap.DocumentSlider = function(config){
33868     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33869     
33870     this.files = [];
33871     
33872     this.addEvents({
33873         /**
33874          * @event initial
33875          * Fire after initEvent
33876          * @param {Roo.bootstrap.DocumentSlider} this
33877          */
33878         "initial" : true,
33879         /**
33880          * @event update
33881          * Fire after update
33882          * @param {Roo.bootstrap.DocumentSlider} this
33883          */
33884         "update" : true,
33885         /**
33886          * @event click
33887          * Fire after click
33888          * @param {Roo.bootstrap.DocumentSlider} this
33889          */
33890         "click" : true
33891     });
33892 };
33893
33894 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33895     
33896     files : false,
33897     
33898     indicator : 0,
33899     
33900     getAutoCreate : function()
33901     {
33902         var cfg = {
33903             tag : 'div',
33904             cls : 'roo-document-slider',
33905             cn : [
33906                 {
33907                     tag : 'div',
33908                     cls : 'roo-document-slider-header',
33909                     cn : [
33910                         {
33911                             tag : 'div',
33912                             cls : 'roo-document-slider-header-title'
33913                         }
33914                     ]
33915                 },
33916                 {
33917                     tag : 'div',
33918                     cls : 'roo-document-slider-body',
33919                     cn : [
33920                         {
33921                             tag : 'div',
33922                             cls : 'roo-document-slider-prev',
33923                             cn : [
33924                                 {
33925                                     tag : 'i',
33926                                     cls : 'fa fa-chevron-left'
33927                                 }
33928                             ]
33929                         },
33930                         {
33931                             tag : 'div',
33932                             cls : 'roo-document-slider-thumb',
33933                             cn : [
33934                                 {
33935                                     tag : 'img',
33936                                     cls : 'roo-document-slider-image'
33937                                 }
33938                             ]
33939                         },
33940                         {
33941                             tag : 'div',
33942                             cls : 'roo-document-slider-next',
33943                             cn : [
33944                                 {
33945                                     tag : 'i',
33946                                     cls : 'fa fa-chevron-right'
33947                                 }
33948                             ]
33949                         }
33950                     ]
33951                 }
33952             ]
33953         };
33954         
33955         return cfg;
33956     },
33957     
33958     initEvents : function()
33959     {
33960         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33961         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33962         
33963         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33964         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33965         
33966         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33967         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33968         
33969         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33970         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33971         
33972         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33973         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33974         
33975         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33976         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33977         
33978         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33979         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33980         
33981         this.thumbEl.on('click', this.onClick, this);
33982         
33983         this.prevIndicator.on('click', this.prev, this);
33984         
33985         this.nextIndicator.on('click', this.next, this);
33986         
33987     },
33988     
33989     initial : function()
33990     {
33991         if(this.files.length){
33992             this.indicator = 1;
33993             this.update()
33994         }
33995         
33996         this.fireEvent('initial', this);
33997     },
33998     
33999     update : function()
34000     {
34001         this.imageEl.attr('src', this.files[this.indicator - 1]);
34002         
34003         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34004         
34005         this.prevIndicator.show();
34006         
34007         if(this.indicator == 1){
34008             this.prevIndicator.hide();
34009         }
34010         
34011         this.nextIndicator.show();
34012         
34013         if(this.indicator == this.files.length){
34014             this.nextIndicator.hide();
34015         }
34016         
34017         this.thumbEl.scrollTo('top');
34018         
34019         this.fireEvent('update', this);
34020     },
34021     
34022     onClick : function(e)
34023     {
34024         e.preventDefault();
34025         
34026         this.fireEvent('click', this);
34027     },
34028     
34029     prev : function(e)
34030     {
34031         e.preventDefault();
34032         
34033         this.indicator = Math.max(1, this.indicator - 1);
34034         
34035         this.update();
34036     },
34037     
34038     next : function(e)
34039     {
34040         e.preventDefault();
34041         
34042         this.indicator = Math.min(this.files.length, this.indicator + 1);
34043         
34044         this.update();
34045     }
34046 });
34047 /*
34048  * - LGPL
34049  *
34050  * RadioSet
34051  *
34052  *
34053  */
34054
34055 /**
34056  * @class Roo.bootstrap.RadioSet
34057  * @extends Roo.bootstrap.Input
34058  * Bootstrap RadioSet class
34059  * @cfg {String} indicatorpos (left|right) default left
34060  * @cfg {Boolean} inline (true|false) inline the element (default true)
34061  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34062  * @constructor
34063  * Create a new RadioSet
34064  * @param {Object} config The config object
34065  */
34066
34067 Roo.bootstrap.RadioSet = function(config){
34068     
34069     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34070     
34071     this.radioes = [];
34072     
34073     Roo.bootstrap.RadioSet.register(this);
34074     
34075     this.addEvents({
34076         /**
34077         * @event check
34078         * Fires when the element is checked or unchecked.
34079         * @param {Roo.bootstrap.RadioSet} this This radio
34080         * @param {Roo.bootstrap.Radio} item The checked item
34081         */
34082        check : true,
34083        /**
34084         * @event click
34085         * Fires when the element is click.
34086         * @param {Roo.bootstrap.RadioSet} this This radio set
34087         * @param {Roo.bootstrap.Radio} item The checked item
34088         * @param {Roo.EventObject} e The event object
34089         */
34090        click : true
34091     });
34092     
34093 };
34094
34095 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34096
34097     radioes : false,
34098     
34099     inline : true,
34100     
34101     weight : '',
34102     
34103     indicatorpos : 'left',
34104     
34105     getAutoCreate : function()
34106     {
34107         var label = {
34108             tag : 'label',
34109             cls : 'roo-radio-set-label',
34110             cn : [
34111                 {
34112                     tag : 'span',
34113                     html : this.fieldLabel
34114                 }
34115             ]
34116         };
34117         if (Roo.bootstrap.version == 3) {
34118             
34119             
34120             if(this.indicatorpos == 'left'){
34121                 label.cn.unshift({
34122                     tag : 'i',
34123                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34124                     tooltip : 'This field is required'
34125                 });
34126             } else {
34127                 label.cn.push({
34128                     tag : 'i',
34129                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34130                     tooltip : 'This field is required'
34131                 });
34132             }
34133         }
34134         var items = {
34135             tag : 'div',
34136             cls : 'roo-radio-set-items'
34137         };
34138         
34139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34140         
34141         if (align === 'left' && this.fieldLabel.length) {
34142             
34143             items = {
34144                 cls : "roo-radio-set-right", 
34145                 cn: [
34146                     items
34147                 ]
34148             };
34149             
34150             if(this.labelWidth > 12){
34151                 label.style = "width: " + this.labelWidth + 'px';
34152             }
34153             
34154             if(this.labelWidth < 13 && this.labelmd == 0){
34155                 this.labelmd = this.labelWidth;
34156             }
34157             
34158             if(this.labellg > 0){
34159                 label.cls += ' col-lg-' + this.labellg;
34160                 items.cls += ' col-lg-' + (12 - this.labellg);
34161             }
34162             
34163             if(this.labelmd > 0){
34164                 label.cls += ' col-md-' + this.labelmd;
34165                 items.cls += ' col-md-' + (12 - this.labelmd);
34166             }
34167             
34168             if(this.labelsm > 0){
34169                 label.cls += ' col-sm-' + this.labelsm;
34170                 items.cls += ' col-sm-' + (12 - this.labelsm);
34171             }
34172             
34173             if(this.labelxs > 0){
34174                 label.cls += ' col-xs-' + this.labelxs;
34175                 items.cls += ' col-xs-' + (12 - this.labelxs);
34176             }
34177         }
34178         
34179         var cfg = {
34180             tag : 'div',
34181             cls : 'roo-radio-set',
34182             cn : [
34183                 {
34184                     tag : 'input',
34185                     cls : 'roo-radio-set-input',
34186                     type : 'hidden',
34187                     name : this.name,
34188                     value : this.value ? this.value :  ''
34189                 },
34190                 label,
34191                 items
34192             ]
34193         };
34194         
34195         if(this.weight.length){
34196             cfg.cls += ' roo-radio-' + this.weight;
34197         }
34198         
34199         if(this.inline) {
34200             cfg.cls += ' roo-radio-set-inline';
34201         }
34202         
34203         var settings=this;
34204         ['xs','sm','md','lg'].map(function(size){
34205             if (settings[size]) {
34206                 cfg.cls += ' col-' + size + '-' + settings[size];
34207             }
34208         });
34209         
34210         return cfg;
34211         
34212     },
34213
34214     initEvents : function()
34215     {
34216         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34217         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34218         
34219         if(!this.fieldLabel.length){
34220             this.labelEl.hide();
34221         }
34222         
34223         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34224         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34225         
34226         this.indicator = this.indicatorEl();
34227         
34228         if(this.indicator){
34229             this.indicator.addClass('invisible');
34230         }
34231         
34232         this.originalValue = this.getValue();
34233         
34234     },
34235     
34236     inputEl: function ()
34237     {
34238         return this.el.select('.roo-radio-set-input', true).first();
34239     },
34240     
34241     getChildContainer : function()
34242     {
34243         return this.itemsEl;
34244     },
34245     
34246     register : function(item)
34247     {
34248         this.radioes.push(item);
34249         
34250     },
34251     
34252     validate : function()
34253     {   
34254         if(this.getVisibilityEl().hasClass('hidden')){
34255             return true;
34256         }
34257         
34258         var valid = false;
34259         
34260         Roo.each(this.radioes, function(i){
34261             if(!i.checked){
34262                 return;
34263             }
34264             
34265             valid = true;
34266             return false;
34267         });
34268         
34269         if(this.allowBlank) {
34270             return true;
34271         }
34272         
34273         if(this.disabled || valid){
34274             this.markValid();
34275             return true;
34276         }
34277         
34278         this.markInvalid();
34279         return false;
34280         
34281     },
34282     
34283     markValid : function()
34284     {
34285         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34286             this.indicatorEl().removeClass('visible');
34287             this.indicatorEl().addClass('invisible');
34288         }
34289         
34290         
34291         if (Roo.bootstrap.version == 3) {
34292             this.el.removeClass([this.invalidClass, this.validClass]);
34293             this.el.addClass(this.validClass);
34294         } else {
34295             this.el.removeClass(['is-invalid','is-valid']);
34296             this.el.addClass(['is-valid']);
34297         }
34298         this.fireEvent('valid', this);
34299     },
34300     
34301     markInvalid : function(msg)
34302     {
34303         if(this.allowBlank || this.disabled){
34304             return;
34305         }
34306         
34307         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34308             this.indicatorEl().removeClass('invisible');
34309             this.indicatorEl().addClass('visible');
34310         }
34311         if (Roo.bootstrap.version == 3) {
34312             this.el.removeClass([this.invalidClass, this.validClass]);
34313             this.el.addClass(this.invalidClass);
34314         } else {
34315             this.el.removeClass(['is-invalid','is-valid']);
34316             this.el.addClass(['is-invalid']);
34317         }
34318         
34319         this.fireEvent('invalid', this, msg);
34320         
34321     },
34322     
34323     setValue : function(v, suppressEvent)
34324     {   
34325         if(this.value === v){
34326             return;
34327         }
34328         
34329         this.value = v;
34330         
34331         if(this.rendered){
34332             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34333         }
34334         
34335         Roo.each(this.radioes, function(i){
34336             i.checked = false;
34337             i.el.removeClass('checked');
34338         });
34339         
34340         Roo.each(this.radioes, function(i){
34341             
34342             if(i.value === v || i.value.toString() === v.toString()){
34343                 i.checked = true;
34344                 i.el.addClass('checked');
34345                 
34346                 if(suppressEvent !== true){
34347                     this.fireEvent('check', this, i);
34348                 }
34349                 
34350                 return false;
34351             }
34352             
34353         }, this);
34354         
34355         this.validate();
34356     },
34357     
34358     clearInvalid : function(){
34359         
34360         if(!this.el || this.preventMark){
34361             return;
34362         }
34363         
34364         this.el.removeClass([this.invalidClass]);
34365         
34366         this.fireEvent('valid', this);
34367     }
34368     
34369 });
34370
34371 Roo.apply(Roo.bootstrap.RadioSet, {
34372     
34373     groups: {},
34374     
34375     register : function(set)
34376     {
34377         this.groups[set.name] = set;
34378     },
34379     
34380     get: function(name) 
34381     {
34382         if (typeof(this.groups[name]) == 'undefined') {
34383             return false;
34384         }
34385         
34386         return this.groups[name] ;
34387     }
34388     
34389 });
34390 /*
34391  * Based on:
34392  * Ext JS Library 1.1.1
34393  * Copyright(c) 2006-2007, Ext JS, LLC.
34394  *
34395  * Originally Released Under LGPL - original licence link has changed is not relivant.
34396  *
34397  * Fork - LGPL
34398  * <script type="text/javascript">
34399  */
34400
34401
34402 /**
34403  * @class Roo.bootstrap.SplitBar
34404  * @extends Roo.util.Observable
34405  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34406  * <br><br>
34407  * Usage:
34408  * <pre><code>
34409 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34410                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34411 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34412 split.minSize = 100;
34413 split.maxSize = 600;
34414 split.animate = true;
34415 split.on('moved', splitterMoved);
34416 </code></pre>
34417  * @constructor
34418  * Create a new SplitBar
34419  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34420  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34421  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34422  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34423                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34424                         position of the SplitBar).
34425  */
34426 Roo.bootstrap.SplitBar = function(cfg){
34427     
34428     /** @private */
34429     
34430     //{
34431     //  dragElement : elm
34432     //  resizingElement: el,
34433         // optional..
34434     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34435     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34436         // existingProxy ???
34437     //}
34438     
34439     this.el = Roo.get(cfg.dragElement, true);
34440     this.el.dom.unselectable = "on";
34441     /** @private */
34442     this.resizingEl = Roo.get(cfg.resizingElement, true);
34443
34444     /**
34445      * @private
34446      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34447      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34448      * @type Number
34449      */
34450     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34451     
34452     /**
34453      * The minimum size of the resizing element. (Defaults to 0)
34454      * @type Number
34455      */
34456     this.minSize = 0;
34457     
34458     /**
34459      * The maximum size of the resizing element. (Defaults to 2000)
34460      * @type Number
34461      */
34462     this.maxSize = 2000;
34463     
34464     /**
34465      * Whether to animate the transition to the new size
34466      * @type Boolean
34467      */
34468     this.animate = false;
34469     
34470     /**
34471      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34472      * @type Boolean
34473      */
34474     this.useShim = false;
34475     
34476     /** @private */
34477     this.shim = null;
34478     
34479     if(!cfg.existingProxy){
34480         /** @private */
34481         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34482     }else{
34483         this.proxy = Roo.get(cfg.existingProxy).dom;
34484     }
34485     /** @private */
34486     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34487     
34488     /** @private */
34489     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34490     
34491     /** @private */
34492     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34493     
34494     /** @private */
34495     this.dragSpecs = {};
34496     
34497     /**
34498      * @private The adapter to use to positon and resize elements
34499      */
34500     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34501     this.adapter.init(this);
34502     
34503     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34504         /** @private */
34505         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34506         this.el.addClass("roo-splitbar-h");
34507     }else{
34508         /** @private */
34509         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34510         this.el.addClass("roo-splitbar-v");
34511     }
34512     
34513     this.addEvents({
34514         /**
34515          * @event resize
34516          * Fires when the splitter is moved (alias for {@link #event-moved})
34517          * @param {Roo.bootstrap.SplitBar} this
34518          * @param {Number} newSize the new width or height
34519          */
34520         "resize" : true,
34521         /**
34522          * @event moved
34523          * Fires when the splitter is moved
34524          * @param {Roo.bootstrap.SplitBar} this
34525          * @param {Number} newSize the new width or height
34526          */
34527         "moved" : true,
34528         /**
34529          * @event beforeresize
34530          * Fires before the splitter is dragged
34531          * @param {Roo.bootstrap.SplitBar} this
34532          */
34533         "beforeresize" : true,
34534
34535         "beforeapply" : true
34536     });
34537
34538     Roo.util.Observable.call(this);
34539 };
34540
34541 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34542     onStartProxyDrag : function(x, y){
34543         this.fireEvent("beforeresize", this);
34544         if(!this.overlay){
34545             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34546             o.unselectable();
34547             o.enableDisplayMode("block");
34548             // all splitbars share the same overlay
34549             Roo.bootstrap.SplitBar.prototype.overlay = o;
34550         }
34551         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34552         this.overlay.show();
34553         Roo.get(this.proxy).setDisplayed("block");
34554         var size = this.adapter.getElementSize(this);
34555         this.activeMinSize = this.getMinimumSize();;
34556         this.activeMaxSize = this.getMaximumSize();;
34557         var c1 = size - this.activeMinSize;
34558         var c2 = Math.max(this.activeMaxSize - size, 0);
34559         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34560             this.dd.resetConstraints();
34561             this.dd.setXConstraint(
34562                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34563                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34564             );
34565             this.dd.setYConstraint(0, 0);
34566         }else{
34567             this.dd.resetConstraints();
34568             this.dd.setXConstraint(0, 0);
34569             this.dd.setYConstraint(
34570                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34571                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34572             );
34573          }
34574         this.dragSpecs.startSize = size;
34575         this.dragSpecs.startPoint = [x, y];
34576         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34577     },
34578     
34579     /** 
34580      * @private Called after the drag operation by the DDProxy
34581      */
34582     onEndProxyDrag : function(e){
34583         Roo.get(this.proxy).setDisplayed(false);
34584         var endPoint = Roo.lib.Event.getXY(e);
34585         if(this.overlay){
34586             this.overlay.hide();
34587         }
34588         var newSize;
34589         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34590             newSize = this.dragSpecs.startSize + 
34591                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34592                     endPoint[0] - this.dragSpecs.startPoint[0] :
34593                     this.dragSpecs.startPoint[0] - endPoint[0]
34594                 );
34595         }else{
34596             newSize = this.dragSpecs.startSize + 
34597                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34598                     endPoint[1] - this.dragSpecs.startPoint[1] :
34599                     this.dragSpecs.startPoint[1] - endPoint[1]
34600                 );
34601         }
34602         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34603         if(newSize != this.dragSpecs.startSize){
34604             if(this.fireEvent('beforeapply', this, newSize) !== false){
34605                 this.adapter.setElementSize(this, newSize);
34606                 this.fireEvent("moved", this, newSize);
34607                 this.fireEvent("resize", this, newSize);
34608             }
34609         }
34610     },
34611     
34612     /**
34613      * Get the adapter this SplitBar uses
34614      * @return The adapter object
34615      */
34616     getAdapter : function(){
34617         return this.adapter;
34618     },
34619     
34620     /**
34621      * Set the adapter this SplitBar uses
34622      * @param {Object} adapter A SplitBar adapter object
34623      */
34624     setAdapter : function(adapter){
34625         this.adapter = adapter;
34626         this.adapter.init(this);
34627     },
34628     
34629     /**
34630      * Gets the minimum size for the resizing element
34631      * @return {Number} The minimum size
34632      */
34633     getMinimumSize : function(){
34634         return this.minSize;
34635     },
34636     
34637     /**
34638      * Sets the minimum size for the resizing element
34639      * @param {Number} minSize The minimum size
34640      */
34641     setMinimumSize : function(minSize){
34642         this.minSize = minSize;
34643     },
34644     
34645     /**
34646      * Gets the maximum size for the resizing element
34647      * @return {Number} The maximum size
34648      */
34649     getMaximumSize : function(){
34650         return this.maxSize;
34651     },
34652     
34653     /**
34654      * Sets the maximum size for the resizing element
34655      * @param {Number} maxSize The maximum size
34656      */
34657     setMaximumSize : function(maxSize){
34658         this.maxSize = maxSize;
34659     },
34660     
34661     /**
34662      * Sets the initialize size for the resizing element
34663      * @param {Number} size The initial size
34664      */
34665     setCurrentSize : function(size){
34666         var oldAnimate = this.animate;
34667         this.animate = false;
34668         this.adapter.setElementSize(this, size);
34669         this.animate = oldAnimate;
34670     },
34671     
34672     /**
34673      * Destroy this splitbar. 
34674      * @param {Boolean} removeEl True to remove the element
34675      */
34676     destroy : function(removeEl){
34677         if(this.shim){
34678             this.shim.remove();
34679         }
34680         this.dd.unreg();
34681         this.proxy.parentNode.removeChild(this.proxy);
34682         if(removeEl){
34683             this.el.remove();
34684         }
34685     }
34686 });
34687
34688 /**
34689  * @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.
34690  */
34691 Roo.bootstrap.SplitBar.createProxy = function(dir){
34692     var proxy = new Roo.Element(document.createElement("div"));
34693     proxy.unselectable();
34694     var cls = 'roo-splitbar-proxy';
34695     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34696     document.body.appendChild(proxy.dom);
34697     return proxy.dom;
34698 };
34699
34700 /** 
34701  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34702  * Default Adapter. It assumes the splitter and resizing element are not positioned
34703  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34704  */
34705 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34706 };
34707
34708 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34709     // do nothing for now
34710     init : function(s){
34711     
34712     },
34713     /**
34714      * Called before drag operations to get the current size of the resizing element. 
34715      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34716      */
34717      getElementSize : function(s){
34718         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34719             return s.resizingEl.getWidth();
34720         }else{
34721             return s.resizingEl.getHeight();
34722         }
34723     },
34724     
34725     /**
34726      * Called after drag operations to set the size of the resizing element.
34727      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34728      * @param {Number} newSize The new size to set
34729      * @param {Function} onComplete A function to be invoked when resizing is complete
34730      */
34731     setElementSize : function(s, newSize, onComplete){
34732         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34733             if(!s.animate){
34734                 s.resizingEl.setWidth(newSize);
34735                 if(onComplete){
34736                     onComplete(s, newSize);
34737                 }
34738             }else{
34739                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34740             }
34741         }else{
34742             
34743             if(!s.animate){
34744                 s.resizingEl.setHeight(newSize);
34745                 if(onComplete){
34746                     onComplete(s, newSize);
34747                 }
34748             }else{
34749                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34750             }
34751         }
34752     }
34753 };
34754
34755 /** 
34756  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34757  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34758  * Adapter that  moves the splitter element to align with the resized sizing element. 
34759  * Used with an absolute positioned SplitBar.
34760  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34761  * document.body, make sure you assign an id to the body element.
34762  */
34763 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34764     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34765     this.container = Roo.get(container);
34766 };
34767
34768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34769     init : function(s){
34770         this.basic.init(s);
34771     },
34772     
34773     getElementSize : function(s){
34774         return this.basic.getElementSize(s);
34775     },
34776     
34777     setElementSize : function(s, newSize, onComplete){
34778         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34779     },
34780     
34781     moveSplitter : function(s){
34782         var yes = Roo.bootstrap.SplitBar;
34783         switch(s.placement){
34784             case yes.LEFT:
34785                 s.el.setX(s.resizingEl.getRight());
34786                 break;
34787             case yes.RIGHT:
34788                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34789                 break;
34790             case yes.TOP:
34791                 s.el.setY(s.resizingEl.getBottom());
34792                 break;
34793             case yes.BOTTOM:
34794                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34795                 break;
34796         }
34797     }
34798 };
34799
34800 /**
34801  * Orientation constant - Create a vertical SplitBar
34802  * @static
34803  * @type Number
34804  */
34805 Roo.bootstrap.SplitBar.VERTICAL = 1;
34806
34807 /**
34808  * Orientation constant - Create a horizontal SplitBar
34809  * @static
34810  * @type Number
34811  */
34812 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34813
34814 /**
34815  * Placement constant - The resizing element is to the left of the splitter element
34816  * @static
34817  * @type Number
34818  */
34819 Roo.bootstrap.SplitBar.LEFT = 1;
34820
34821 /**
34822  * Placement constant - The resizing element is to the right of the splitter element
34823  * @static
34824  * @type Number
34825  */
34826 Roo.bootstrap.SplitBar.RIGHT = 2;
34827
34828 /**
34829  * Placement constant - The resizing element is positioned above the splitter element
34830  * @static
34831  * @type Number
34832  */
34833 Roo.bootstrap.SplitBar.TOP = 3;
34834
34835 /**
34836  * Placement constant - The resizing element is positioned under splitter element
34837  * @static
34838  * @type Number
34839  */
34840 Roo.bootstrap.SplitBar.BOTTOM = 4;
34841 Roo.namespace("Roo.bootstrap.layout");/*
34842  * Based on:
34843  * Ext JS Library 1.1.1
34844  * Copyright(c) 2006-2007, Ext JS, LLC.
34845  *
34846  * Originally Released Under LGPL - original licence link has changed is not relivant.
34847  *
34848  * Fork - LGPL
34849  * <script type="text/javascript">
34850  */
34851
34852 /**
34853  * @class Roo.bootstrap.layout.Manager
34854  * @extends Roo.bootstrap.Component
34855  * Base class for layout managers.
34856  */
34857 Roo.bootstrap.layout.Manager = function(config)
34858 {
34859     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34860
34861
34862
34863
34864
34865     /** false to disable window resize monitoring @type Boolean */
34866     this.monitorWindowResize = true;
34867     this.regions = {};
34868     this.addEvents({
34869         /**
34870          * @event layout
34871          * Fires when a layout is performed.
34872          * @param {Roo.LayoutManager} this
34873          */
34874         "layout" : true,
34875         /**
34876          * @event regionresized
34877          * Fires when the user resizes a region.
34878          * @param {Roo.LayoutRegion} region The resized region
34879          * @param {Number} newSize The new size (width for east/west, height for north/south)
34880          */
34881         "regionresized" : true,
34882         /**
34883          * @event regioncollapsed
34884          * Fires when a region is collapsed.
34885          * @param {Roo.LayoutRegion} region The collapsed region
34886          */
34887         "regioncollapsed" : true,
34888         /**
34889          * @event regionexpanded
34890          * Fires when a region is expanded.
34891          * @param {Roo.LayoutRegion} region The expanded region
34892          */
34893         "regionexpanded" : true
34894     });
34895     this.updating = false;
34896
34897     if (config.el) {
34898         this.el = Roo.get(config.el);
34899         this.initEvents();
34900     }
34901
34902 };
34903
34904 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34905
34906
34907     regions : null,
34908
34909     monitorWindowResize : true,
34910
34911
34912     updating : false,
34913
34914
34915     onRender : function(ct, position)
34916     {
34917         if(!this.el){
34918             this.el = Roo.get(ct);
34919             this.initEvents();
34920         }
34921         //this.fireEvent('render',this);
34922     },
34923
34924
34925     initEvents: function()
34926     {
34927
34928
34929         // ie scrollbar fix
34930         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34931             document.body.scroll = "no";
34932         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34933             this.el.position('relative');
34934         }
34935         this.id = this.el.id;
34936         this.el.addClass("roo-layout-container");
34937         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34938         if(this.el.dom != document.body ) {
34939             this.el.on('resize', this.layout,this);
34940             this.el.on('show', this.layout,this);
34941         }
34942
34943     },
34944
34945     /**
34946      * Returns true if this layout is currently being updated
34947      * @return {Boolean}
34948      */
34949     isUpdating : function(){
34950         return this.updating;
34951     },
34952
34953     /**
34954      * Suspend the LayoutManager from doing auto-layouts while
34955      * making multiple add or remove calls
34956      */
34957     beginUpdate : function(){
34958         this.updating = true;
34959     },
34960
34961     /**
34962      * Restore auto-layouts and optionally disable the manager from performing a layout
34963      * @param {Boolean} noLayout true to disable a layout update
34964      */
34965     endUpdate : function(noLayout){
34966         this.updating = false;
34967         if(!noLayout){
34968             this.layout();
34969         }
34970     },
34971
34972     layout: function(){
34973         // abstract...
34974     },
34975
34976     onRegionResized : function(region, newSize){
34977         this.fireEvent("regionresized", region, newSize);
34978         this.layout();
34979     },
34980
34981     onRegionCollapsed : function(region){
34982         this.fireEvent("regioncollapsed", region);
34983     },
34984
34985     onRegionExpanded : function(region){
34986         this.fireEvent("regionexpanded", region);
34987     },
34988
34989     /**
34990      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34991      * performs box-model adjustments.
34992      * @return {Object} The size as an object {width: (the width), height: (the height)}
34993      */
34994     getViewSize : function()
34995     {
34996         var size;
34997         if(this.el.dom != document.body){
34998             size = this.el.getSize();
34999         }else{
35000             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35001         }
35002         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35003         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35004         return size;
35005     },
35006
35007     /**
35008      * Returns the Element this layout is bound to.
35009      * @return {Roo.Element}
35010      */
35011     getEl : function(){
35012         return this.el;
35013     },
35014
35015     /**
35016      * Returns the specified region.
35017      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35018      * @return {Roo.LayoutRegion}
35019      */
35020     getRegion : function(target){
35021         return this.regions[target.toLowerCase()];
35022     },
35023
35024     onWindowResize : function(){
35025         if(this.monitorWindowResize){
35026             this.layout();
35027         }
35028     }
35029 });
35030 /*
35031  * Based on:
35032  * Ext JS Library 1.1.1
35033  * Copyright(c) 2006-2007, Ext JS, LLC.
35034  *
35035  * Originally Released Under LGPL - original licence link has changed is not relivant.
35036  *
35037  * Fork - LGPL
35038  * <script type="text/javascript">
35039  */
35040 /**
35041  * @class Roo.bootstrap.layout.Border
35042  * @extends Roo.bootstrap.layout.Manager
35043  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35044  * please see: examples/bootstrap/nested.html<br><br>
35045  
35046 <b>The container the layout is rendered into can be either the body element or any other element.
35047 If it is not the body element, the container needs to either be an absolute positioned element,
35048 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35049 the container size if it is not the body element.</b>
35050
35051 * @constructor
35052 * Create a new Border
35053 * @param {Object} config Configuration options
35054  */
35055 Roo.bootstrap.layout.Border = function(config){
35056     config = config || {};
35057     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35058     
35059     
35060     
35061     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35062         if(config[region]){
35063             config[region].region = region;
35064             this.addRegion(config[region]);
35065         }
35066     },this);
35067     
35068 };
35069
35070 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35071
35072 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35073     /**
35074      * Creates and adds a new region if it doesn't already exist.
35075      * @param {String} target The target region key (north, south, east, west or center).
35076      * @param {Object} config The regions config object
35077      * @return {BorderLayoutRegion} The new region
35078      */
35079     addRegion : function(config)
35080     {
35081         if(!this.regions[config.region]){
35082             var r = this.factory(config);
35083             this.bindRegion(r);
35084         }
35085         return this.regions[config.region];
35086     },
35087
35088     // private (kinda)
35089     bindRegion : function(r){
35090         this.regions[r.config.region] = r;
35091         
35092         r.on("visibilitychange",    this.layout, this);
35093         r.on("paneladded",          this.layout, this);
35094         r.on("panelremoved",        this.layout, this);
35095         r.on("invalidated",         this.layout, this);
35096         r.on("resized",             this.onRegionResized, this);
35097         r.on("collapsed",           this.onRegionCollapsed, this);
35098         r.on("expanded",            this.onRegionExpanded, this);
35099     },
35100
35101     /**
35102      * Performs a layout update.
35103      */
35104     layout : function()
35105     {
35106         if(this.updating) {
35107             return;
35108         }
35109         
35110         // render all the rebions if they have not been done alreayd?
35111         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35112             if(this.regions[region] && !this.regions[region].bodyEl){
35113                 this.regions[region].onRender(this.el)
35114             }
35115         },this);
35116         
35117         var size = this.getViewSize();
35118         var w = size.width;
35119         var h = size.height;
35120         var centerW = w;
35121         var centerH = h;
35122         var centerY = 0;
35123         var centerX = 0;
35124         //var x = 0, y = 0;
35125
35126         var rs = this.regions;
35127         var north = rs["north"];
35128         var south = rs["south"]; 
35129         var west = rs["west"];
35130         var east = rs["east"];
35131         var center = rs["center"];
35132         //if(this.hideOnLayout){ // not supported anymore
35133             //c.el.setStyle("display", "none");
35134         //}
35135         if(north && north.isVisible()){
35136             var b = north.getBox();
35137             var m = north.getMargins();
35138             b.width = w - (m.left+m.right);
35139             b.x = m.left;
35140             b.y = m.top;
35141             centerY = b.height + b.y + m.bottom;
35142             centerH -= centerY;
35143             north.updateBox(this.safeBox(b));
35144         }
35145         if(south && south.isVisible()){
35146             var b = south.getBox();
35147             var m = south.getMargins();
35148             b.width = w - (m.left+m.right);
35149             b.x = m.left;
35150             var totalHeight = (b.height + m.top + m.bottom);
35151             b.y = h - totalHeight + m.top;
35152             centerH -= totalHeight;
35153             south.updateBox(this.safeBox(b));
35154         }
35155         if(west && west.isVisible()){
35156             var b = west.getBox();
35157             var m = west.getMargins();
35158             b.height = centerH - (m.top+m.bottom);
35159             b.x = m.left;
35160             b.y = centerY + m.top;
35161             var totalWidth = (b.width + m.left + m.right);
35162             centerX += totalWidth;
35163             centerW -= totalWidth;
35164             west.updateBox(this.safeBox(b));
35165         }
35166         if(east && east.isVisible()){
35167             var b = east.getBox();
35168             var m = east.getMargins();
35169             b.height = centerH - (m.top+m.bottom);
35170             var totalWidth = (b.width + m.left + m.right);
35171             b.x = w - totalWidth + m.left;
35172             b.y = centerY + m.top;
35173             centerW -= totalWidth;
35174             east.updateBox(this.safeBox(b));
35175         }
35176         if(center){
35177             var m = center.getMargins();
35178             var centerBox = {
35179                 x: centerX + m.left,
35180                 y: centerY + m.top,
35181                 width: centerW - (m.left+m.right),
35182                 height: centerH - (m.top+m.bottom)
35183             };
35184             //if(this.hideOnLayout){
35185                 //center.el.setStyle("display", "block");
35186             //}
35187             center.updateBox(this.safeBox(centerBox));
35188         }
35189         this.el.repaint();
35190         this.fireEvent("layout", this);
35191     },
35192
35193     // private
35194     safeBox : function(box){
35195         box.width = Math.max(0, box.width);
35196         box.height = Math.max(0, box.height);
35197         return box;
35198     },
35199
35200     /**
35201      * Adds a ContentPanel (or subclass) to this layout.
35202      * @param {String} target The target region key (north, south, east, west or center).
35203      * @param {Roo.ContentPanel} panel The panel to add
35204      * @return {Roo.ContentPanel} The added panel
35205      */
35206     add : function(target, panel){
35207          
35208         target = target.toLowerCase();
35209         return this.regions[target].add(panel);
35210     },
35211
35212     /**
35213      * Remove a ContentPanel (or subclass) to this layout.
35214      * @param {String} target The target region key (north, south, east, west or center).
35215      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35216      * @return {Roo.ContentPanel} The removed panel
35217      */
35218     remove : function(target, panel){
35219         target = target.toLowerCase();
35220         return this.regions[target].remove(panel);
35221     },
35222
35223     /**
35224      * Searches all regions for a panel with the specified id
35225      * @param {String} panelId
35226      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35227      */
35228     findPanel : function(panelId){
35229         var rs = this.regions;
35230         for(var target in rs){
35231             if(typeof rs[target] != "function"){
35232                 var p = rs[target].getPanel(panelId);
35233                 if(p){
35234                     return p;
35235                 }
35236             }
35237         }
35238         return null;
35239     },
35240
35241     /**
35242      * Searches all regions for a panel with the specified id and activates (shows) it.
35243      * @param {String/ContentPanel} panelId The panels id or the panel itself
35244      * @return {Roo.ContentPanel} The shown panel or null
35245      */
35246     showPanel : function(panelId) {
35247       var rs = this.regions;
35248       for(var target in rs){
35249          var r = rs[target];
35250          if(typeof r != "function"){
35251             if(r.hasPanel(panelId)){
35252                return r.showPanel(panelId);
35253             }
35254          }
35255       }
35256       return null;
35257    },
35258
35259    /**
35260      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35261      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35262      */
35263    /*
35264     restoreState : function(provider){
35265         if(!provider){
35266             provider = Roo.state.Manager;
35267         }
35268         var sm = new Roo.LayoutStateManager();
35269         sm.init(this, provider);
35270     },
35271 */
35272  
35273  
35274     /**
35275      * Adds a xtype elements to the layout.
35276      * <pre><code>
35277
35278 layout.addxtype({
35279        xtype : 'ContentPanel',
35280        region: 'west',
35281        items: [ .... ]
35282    }
35283 );
35284
35285 layout.addxtype({
35286         xtype : 'NestedLayoutPanel',
35287         region: 'west',
35288         layout: {
35289            center: { },
35290            west: { }   
35291         },
35292         items : [ ... list of content panels or nested layout panels.. ]
35293    }
35294 );
35295 </code></pre>
35296      * @param {Object} cfg Xtype definition of item to add.
35297      */
35298     addxtype : function(cfg)
35299     {
35300         // basically accepts a pannel...
35301         // can accept a layout region..!?!?
35302         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35303         
35304         
35305         // theory?  children can only be panels??
35306         
35307         //if (!cfg.xtype.match(/Panel$/)) {
35308         //    return false;
35309         //}
35310         var ret = false;
35311         
35312         if (typeof(cfg.region) == 'undefined') {
35313             Roo.log("Failed to add Panel, region was not set");
35314             Roo.log(cfg);
35315             return false;
35316         }
35317         var region = cfg.region;
35318         delete cfg.region;
35319         
35320           
35321         var xitems = [];
35322         if (cfg.items) {
35323             xitems = cfg.items;
35324             delete cfg.items;
35325         }
35326         var nb = false;
35327         
35328         switch(cfg.xtype) 
35329         {
35330             case 'Content':  // ContentPanel (el, cfg)
35331             case 'Scroll':  // ContentPanel (el, cfg)
35332             case 'View': 
35333                 cfg.autoCreate = true;
35334                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35335                 //} else {
35336                 //    var el = this.el.createChild();
35337                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35338                 //}
35339                 
35340                 this.add(region, ret);
35341                 break;
35342             
35343             /*
35344             case 'TreePanel': // our new panel!
35345                 cfg.el = this.el.createChild();
35346                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35347                 this.add(region, ret);
35348                 break;
35349             */
35350             
35351             case 'Nest': 
35352                 // create a new Layout (which is  a Border Layout...
35353                 
35354                 var clayout = cfg.layout;
35355                 clayout.el  = this.el.createChild();
35356                 clayout.items   = clayout.items  || [];
35357                 
35358                 delete cfg.layout;
35359                 
35360                 // replace this exitems with the clayout ones..
35361                 xitems = clayout.items;
35362                  
35363                 // force background off if it's in center...
35364                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35365                     cfg.background = false;
35366                 }
35367                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35368                 
35369                 
35370                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35371                 //console.log('adding nested layout panel '  + cfg.toSource());
35372                 this.add(region, ret);
35373                 nb = {}; /// find first...
35374                 break;
35375             
35376             case 'Grid':
35377                 
35378                 // needs grid and region
35379                 
35380                 //var el = this.getRegion(region).el.createChild();
35381                 /*
35382                  *var el = this.el.createChild();
35383                 // create the grid first...
35384                 cfg.grid.container = el;
35385                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35386                 */
35387                 
35388                 if (region == 'center' && this.active ) {
35389                     cfg.background = false;
35390                 }
35391                 
35392                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35393                 
35394                 this.add(region, ret);
35395                 /*
35396                 if (cfg.background) {
35397                     // render grid on panel activation (if panel background)
35398                     ret.on('activate', function(gp) {
35399                         if (!gp.grid.rendered) {
35400                     //        gp.grid.render(el);
35401                         }
35402                     });
35403                 } else {
35404                   //  cfg.grid.render(el);
35405                 }
35406                 */
35407                 break;
35408            
35409            
35410             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35411                 // it was the old xcomponent building that caused this before.
35412                 // espeically if border is the top element in the tree.
35413                 ret = this;
35414                 break; 
35415                 
35416                     
35417                 
35418                 
35419                 
35420             default:
35421                 /*
35422                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35423                     
35424                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35425                     this.add(region, ret);
35426                 } else {
35427                 */
35428                     Roo.log(cfg);
35429                     throw "Can not add '" + cfg.xtype + "' to Border";
35430                     return null;
35431              
35432                                 
35433              
35434         }
35435         this.beginUpdate();
35436         // add children..
35437         var region = '';
35438         var abn = {};
35439         Roo.each(xitems, function(i)  {
35440             region = nb && i.region ? i.region : false;
35441             
35442             var add = ret.addxtype(i);
35443            
35444             if (region) {
35445                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35446                 if (!i.background) {
35447                     abn[region] = nb[region] ;
35448                 }
35449             }
35450             
35451         });
35452         this.endUpdate();
35453
35454         // make the last non-background panel active..
35455         //if (nb) { Roo.log(abn); }
35456         if (nb) {
35457             
35458             for(var r in abn) {
35459                 region = this.getRegion(r);
35460                 if (region) {
35461                     // tried using nb[r], but it does not work..
35462                      
35463                     region.showPanel(abn[r]);
35464                    
35465                 }
35466             }
35467         }
35468         return ret;
35469         
35470     },
35471     
35472     
35473 // private
35474     factory : function(cfg)
35475     {
35476         
35477         var validRegions = Roo.bootstrap.layout.Border.regions;
35478
35479         var target = cfg.region;
35480         cfg.mgr = this;
35481         
35482         var r = Roo.bootstrap.layout;
35483         Roo.log(target);
35484         switch(target){
35485             case "north":
35486                 return new r.North(cfg);
35487             case "south":
35488                 return new r.South(cfg);
35489             case "east":
35490                 return new r.East(cfg);
35491             case "west":
35492                 return new r.West(cfg);
35493             case "center":
35494                 return new r.Center(cfg);
35495         }
35496         throw 'Layout region "'+target+'" not supported.';
35497     }
35498     
35499     
35500 });
35501  /*
35502  * Based on:
35503  * Ext JS Library 1.1.1
35504  * Copyright(c) 2006-2007, Ext JS, LLC.
35505  *
35506  * Originally Released Under LGPL - original licence link has changed is not relivant.
35507  *
35508  * Fork - LGPL
35509  * <script type="text/javascript">
35510  */
35511  
35512 /**
35513  * @class Roo.bootstrap.layout.Basic
35514  * @extends Roo.util.Observable
35515  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35516  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35517  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35518  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35519  * @cfg {string}   region  the region that it inhabits..
35520  * @cfg {bool}   skipConfig skip config?
35521  * 
35522
35523  */
35524 Roo.bootstrap.layout.Basic = function(config){
35525     
35526     this.mgr = config.mgr;
35527     
35528     this.position = config.region;
35529     
35530     var skipConfig = config.skipConfig;
35531     
35532     this.events = {
35533         /**
35534          * @scope Roo.BasicLayoutRegion
35535          */
35536         
35537         /**
35538          * @event beforeremove
35539          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35540          * @param {Roo.LayoutRegion} this
35541          * @param {Roo.ContentPanel} panel The panel
35542          * @param {Object} e The cancel event object
35543          */
35544         "beforeremove" : true,
35545         /**
35546          * @event invalidated
35547          * Fires when the layout for this region is changed.
35548          * @param {Roo.LayoutRegion} this
35549          */
35550         "invalidated" : true,
35551         /**
35552          * @event visibilitychange
35553          * Fires when this region is shown or hidden 
35554          * @param {Roo.LayoutRegion} this
35555          * @param {Boolean} visibility true or false
35556          */
35557         "visibilitychange" : true,
35558         /**
35559          * @event paneladded
35560          * Fires when a panel is added. 
35561          * @param {Roo.LayoutRegion} this
35562          * @param {Roo.ContentPanel} panel The panel
35563          */
35564         "paneladded" : true,
35565         /**
35566          * @event panelremoved
35567          * Fires when a panel is removed. 
35568          * @param {Roo.LayoutRegion} this
35569          * @param {Roo.ContentPanel} panel The panel
35570          */
35571         "panelremoved" : true,
35572         /**
35573          * @event beforecollapse
35574          * Fires when this region before collapse.
35575          * @param {Roo.LayoutRegion} this
35576          */
35577         "beforecollapse" : true,
35578         /**
35579          * @event collapsed
35580          * Fires when this region is collapsed.
35581          * @param {Roo.LayoutRegion} this
35582          */
35583         "collapsed" : true,
35584         /**
35585          * @event expanded
35586          * Fires when this region is expanded.
35587          * @param {Roo.LayoutRegion} this
35588          */
35589         "expanded" : true,
35590         /**
35591          * @event slideshow
35592          * Fires when this region is slid into view.
35593          * @param {Roo.LayoutRegion} this
35594          */
35595         "slideshow" : true,
35596         /**
35597          * @event slidehide
35598          * Fires when this region slides out of view. 
35599          * @param {Roo.LayoutRegion} this
35600          */
35601         "slidehide" : true,
35602         /**
35603          * @event panelactivated
35604          * Fires when a panel is activated. 
35605          * @param {Roo.LayoutRegion} this
35606          * @param {Roo.ContentPanel} panel The activated panel
35607          */
35608         "panelactivated" : true,
35609         /**
35610          * @event resized
35611          * Fires when the user resizes this region. 
35612          * @param {Roo.LayoutRegion} this
35613          * @param {Number} newSize The new size (width for east/west, height for north/south)
35614          */
35615         "resized" : true
35616     };
35617     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35618     this.panels = new Roo.util.MixedCollection();
35619     this.panels.getKey = this.getPanelId.createDelegate(this);
35620     this.box = null;
35621     this.activePanel = null;
35622     // ensure listeners are added...
35623     
35624     if (config.listeners || config.events) {
35625         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35626             listeners : config.listeners || {},
35627             events : config.events || {}
35628         });
35629     }
35630     
35631     if(skipConfig !== true){
35632         this.applyConfig(config);
35633     }
35634 };
35635
35636 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35637 {
35638     getPanelId : function(p){
35639         return p.getId();
35640     },
35641     
35642     applyConfig : function(config){
35643         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35644         this.config = config;
35645         
35646     },
35647     
35648     /**
35649      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35650      * the width, for horizontal (north, south) the height.
35651      * @param {Number} newSize The new width or height
35652      */
35653     resizeTo : function(newSize){
35654         var el = this.el ? this.el :
35655                  (this.activePanel ? this.activePanel.getEl() : null);
35656         if(el){
35657             switch(this.position){
35658                 case "east":
35659                 case "west":
35660                     el.setWidth(newSize);
35661                     this.fireEvent("resized", this, newSize);
35662                 break;
35663                 case "north":
35664                 case "south":
35665                     el.setHeight(newSize);
35666                     this.fireEvent("resized", this, newSize);
35667                 break;                
35668             }
35669         }
35670     },
35671     
35672     getBox : function(){
35673         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35674     },
35675     
35676     getMargins : function(){
35677         return this.margins;
35678     },
35679     
35680     updateBox : function(box){
35681         this.box = box;
35682         var el = this.activePanel.getEl();
35683         el.dom.style.left = box.x + "px";
35684         el.dom.style.top = box.y + "px";
35685         this.activePanel.setSize(box.width, box.height);
35686     },
35687     
35688     /**
35689      * Returns the container element for this region.
35690      * @return {Roo.Element}
35691      */
35692     getEl : function(){
35693         return this.activePanel;
35694     },
35695     
35696     /**
35697      * Returns true if this region is currently visible.
35698      * @return {Boolean}
35699      */
35700     isVisible : function(){
35701         return this.activePanel ? true : false;
35702     },
35703     
35704     setActivePanel : function(panel){
35705         panel = this.getPanel(panel);
35706         if(this.activePanel && this.activePanel != panel){
35707             this.activePanel.setActiveState(false);
35708             this.activePanel.getEl().setLeftTop(-10000,-10000);
35709         }
35710         this.activePanel = panel;
35711         panel.setActiveState(true);
35712         if(this.box){
35713             panel.setSize(this.box.width, this.box.height);
35714         }
35715         this.fireEvent("panelactivated", this, panel);
35716         this.fireEvent("invalidated");
35717     },
35718     
35719     /**
35720      * Show the specified panel.
35721      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35722      * @return {Roo.ContentPanel} The shown panel or null
35723      */
35724     showPanel : function(panel){
35725         panel = this.getPanel(panel);
35726         if(panel){
35727             this.setActivePanel(panel);
35728         }
35729         return panel;
35730     },
35731     
35732     /**
35733      * Get the active panel for this region.
35734      * @return {Roo.ContentPanel} The active panel or null
35735      */
35736     getActivePanel : function(){
35737         return this.activePanel;
35738     },
35739     
35740     /**
35741      * Add the passed ContentPanel(s)
35742      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35743      * @return {Roo.ContentPanel} The panel added (if only one was added)
35744      */
35745     add : function(panel){
35746         if(arguments.length > 1){
35747             for(var i = 0, len = arguments.length; i < len; i++) {
35748                 this.add(arguments[i]);
35749             }
35750             return null;
35751         }
35752         if(this.hasPanel(panel)){
35753             this.showPanel(panel);
35754             return panel;
35755         }
35756         var el = panel.getEl();
35757         if(el.dom.parentNode != this.mgr.el.dom){
35758             this.mgr.el.dom.appendChild(el.dom);
35759         }
35760         if(panel.setRegion){
35761             panel.setRegion(this);
35762         }
35763         this.panels.add(panel);
35764         el.setStyle("position", "absolute");
35765         if(!panel.background){
35766             this.setActivePanel(panel);
35767             if(this.config.initialSize && this.panels.getCount()==1){
35768                 this.resizeTo(this.config.initialSize);
35769             }
35770         }
35771         this.fireEvent("paneladded", this, panel);
35772         return panel;
35773     },
35774     
35775     /**
35776      * Returns true if the panel is in this region.
35777      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35778      * @return {Boolean}
35779      */
35780     hasPanel : function(panel){
35781         if(typeof panel == "object"){ // must be panel obj
35782             panel = panel.getId();
35783         }
35784         return this.getPanel(panel) ? true : false;
35785     },
35786     
35787     /**
35788      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35789      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35790      * @param {Boolean} preservePanel Overrides the config preservePanel option
35791      * @return {Roo.ContentPanel} The panel that was removed
35792      */
35793     remove : function(panel, preservePanel){
35794         panel = this.getPanel(panel);
35795         if(!panel){
35796             return null;
35797         }
35798         var e = {};
35799         this.fireEvent("beforeremove", this, panel, e);
35800         if(e.cancel === true){
35801             return null;
35802         }
35803         var panelId = panel.getId();
35804         this.panels.removeKey(panelId);
35805         return panel;
35806     },
35807     
35808     /**
35809      * Returns the panel specified or null if it's not in this region.
35810      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35811      * @return {Roo.ContentPanel}
35812      */
35813     getPanel : function(id){
35814         if(typeof id == "object"){ // must be panel obj
35815             return id;
35816         }
35817         return this.panels.get(id);
35818     },
35819     
35820     /**
35821      * Returns this regions position (north/south/east/west/center).
35822      * @return {String} 
35823      */
35824     getPosition: function(){
35825         return this.position;    
35826     }
35827 });/*
35828  * Based on:
35829  * Ext JS Library 1.1.1
35830  * Copyright(c) 2006-2007, Ext JS, LLC.
35831  *
35832  * Originally Released Under LGPL - original licence link has changed is not relivant.
35833  *
35834  * Fork - LGPL
35835  * <script type="text/javascript">
35836  */
35837  
35838 /**
35839  * @class Roo.bootstrap.layout.Region
35840  * @extends Roo.bootstrap.layout.Basic
35841  * This class represents a region in a layout manager.
35842  
35843  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35844  * @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})
35845  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35846  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35847  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35848  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35849  * @cfg {String}    title           The title for the region (overrides panel titles)
35850  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35851  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35852  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35853  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35854  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35855  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35856  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35857  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35858  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35859  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35860
35861  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35862  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35863  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35864  * @cfg {Number}    width           For East/West panels
35865  * @cfg {Number}    height          For North/South panels
35866  * @cfg {Boolean}   split           To show the splitter
35867  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35868  * 
35869  * @cfg {string}   cls             Extra CSS classes to add to region
35870  * 
35871  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35872  * @cfg {string}   region  the region that it inhabits..
35873  *
35874
35875  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35876  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35877
35878  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35879  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35880  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35881  */
35882 Roo.bootstrap.layout.Region = function(config)
35883 {
35884     this.applyConfig(config);
35885
35886     var mgr = config.mgr;
35887     var pos = config.region;
35888     config.skipConfig = true;
35889     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35890     
35891     if (mgr.el) {
35892         this.onRender(mgr.el);   
35893     }
35894      
35895     this.visible = true;
35896     this.collapsed = false;
35897     this.unrendered_panels = [];
35898 };
35899
35900 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35901
35902     position: '', // set by wrapper (eg. north/south etc..)
35903     unrendered_panels : null,  // unrendered panels.
35904     createBody : function(){
35905         /** This region's body element 
35906         * @type Roo.Element */
35907         this.bodyEl = this.el.createChild({
35908                 tag: "div",
35909                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35910         });
35911     },
35912
35913     onRender: function(ctr, pos)
35914     {
35915         var dh = Roo.DomHelper;
35916         /** This region's container element 
35917         * @type Roo.Element */
35918         this.el = dh.append(ctr.dom, {
35919                 tag: "div",
35920                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35921             }, true);
35922         /** This region's title element 
35923         * @type Roo.Element */
35924     
35925         this.titleEl = dh.append(this.el.dom,
35926             {
35927                     tag: "div",
35928                     unselectable: "on",
35929                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35930                     children:[
35931                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35932                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35933                     ]}, true);
35934         
35935         this.titleEl.enableDisplayMode();
35936         /** This region's title text element 
35937         * @type HTMLElement */
35938         this.titleTextEl = this.titleEl.dom.firstChild;
35939         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35940         /*
35941         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35942         this.closeBtn.enableDisplayMode();
35943         this.closeBtn.on("click", this.closeClicked, this);
35944         this.closeBtn.hide();
35945     */
35946         this.createBody(this.config);
35947         if(this.config.hideWhenEmpty){
35948             this.hide();
35949             this.on("paneladded", this.validateVisibility, this);
35950             this.on("panelremoved", this.validateVisibility, this);
35951         }
35952         if(this.autoScroll){
35953             this.bodyEl.setStyle("overflow", "auto");
35954         }else{
35955             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35956         }
35957         //if(c.titlebar !== false){
35958             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35959                 this.titleEl.hide();
35960             }else{
35961                 this.titleEl.show();
35962                 if(this.config.title){
35963                     this.titleTextEl.innerHTML = this.config.title;
35964                 }
35965             }
35966         //}
35967         if(this.config.collapsed){
35968             this.collapse(true);
35969         }
35970         if(this.config.hidden){
35971             this.hide();
35972         }
35973         
35974         if (this.unrendered_panels && this.unrendered_panels.length) {
35975             for (var i =0;i< this.unrendered_panels.length; i++) {
35976                 this.add(this.unrendered_panels[i]);
35977             }
35978             this.unrendered_panels = null;
35979             
35980         }
35981         
35982     },
35983     
35984     applyConfig : function(c)
35985     {
35986         /*
35987          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35988             var dh = Roo.DomHelper;
35989             if(c.titlebar !== false){
35990                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35991                 this.collapseBtn.on("click", this.collapse, this);
35992                 this.collapseBtn.enableDisplayMode();
35993                 /*
35994                 if(c.showPin === true || this.showPin){
35995                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35996                     this.stickBtn.enableDisplayMode();
35997                     this.stickBtn.on("click", this.expand, this);
35998                     this.stickBtn.hide();
35999                 }
36000                 
36001             }
36002             */
36003             /** This region's collapsed element
36004             * @type Roo.Element */
36005             /*
36006              *
36007             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36008                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36009             ]}, true);
36010             
36011             if(c.floatable !== false){
36012                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36013                this.collapsedEl.on("click", this.collapseClick, this);
36014             }
36015
36016             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36017                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36018                    id: "message", unselectable: "on", style:{"float":"left"}});
36019                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36020              }
36021             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36022             this.expandBtn.on("click", this.expand, this);
36023             
36024         }
36025         
36026         if(this.collapseBtn){
36027             this.collapseBtn.setVisible(c.collapsible == true);
36028         }
36029         
36030         this.cmargins = c.cmargins || this.cmargins ||
36031                          (this.position == "west" || this.position == "east" ?
36032                              {top: 0, left: 2, right:2, bottom: 0} :
36033                              {top: 2, left: 0, right:0, bottom: 2});
36034         */
36035         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36036         
36037         
36038         this.bottomTabs = c.tabPosition != "top";
36039         
36040         this.autoScroll = c.autoScroll || false;
36041         
36042         
36043        
36044         
36045         this.duration = c.duration || .30;
36046         this.slideDuration = c.slideDuration || .45;
36047         this.config = c;
36048        
36049     },
36050     /**
36051      * Returns true if this region is currently visible.
36052      * @return {Boolean}
36053      */
36054     isVisible : function(){
36055         return this.visible;
36056     },
36057
36058     /**
36059      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36060      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36061      */
36062     //setCollapsedTitle : function(title){
36063     //    title = title || "&#160;";
36064      //   if(this.collapsedTitleTextEl){
36065       //      this.collapsedTitleTextEl.innerHTML = title;
36066        // }
36067     //},
36068
36069     getBox : function(){
36070         var b;
36071       //  if(!this.collapsed){
36072             b = this.el.getBox(false, true);
36073        // }else{
36074           //  b = this.collapsedEl.getBox(false, true);
36075         //}
36076         return b;
36077     },
36078
36079     getMargins : function(){
36080         return this.margins;
36081         //return this.collapsed ? this.cmargins : this.margins;
36082     },
36083 /*
36084     highlight : function(){
36085         this.el.addClass("x-layout-panel-dragover");
36086     },
36087
36088     unhighlight : function(){
36089         this.el.removeClass("x-layout-panel-dragover");
36090     },
36091 */
36092     updateBox : function(box)
36093     {
36094         if (!this.bodyEl) {
36095             return; // not rendered yet..
36096         }
36097         
36098         this.box = box;
36099         if(!this.collapsed){
36100             this.el.dom.style.left = box.x + "px";
36101             this.el.dom.style.top = box.y + "px";
36102             this.updateBody(box.width, box.height);
36103         }else{
36104             this.collapsedEl.dom.style.left = box.x + "px";
36105             this.collapsedEl.dom.style.top = box.y + "px";
36106             this.collapsedEl.setSize(box.width, box.height);
36107         }
36108         if(this.tabs){
36109             this.tabs.autoSizeTabs();
36110         }
36111     },
36112
36113     updateBody : function(w, h)
36114     {
36115         if(w !== null){
36116             this.el.setWidth(w);
36117             w -= this.el.getBorderWidth("rl");
36118             if(this.config.adjustments){
36119                 w += this.config.adjustments[0];
36120             }
36121         }
36122         if(h !== null && h > 0){
36123             this.el.setHeight(h);
36124             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36125             h -= this.el.getBorderWidth("tb");
36126             if(this.config.adjustments){
36127                 h += this.config.adjustments[1];
36128             }
36129             this.bodyEl.setHeight(h);
36130             if(this.tabs){
36131                 h = this.tabs.syncHeight(h);
36132             }
36133         }
36134         if(this.panelSize){
36135             w = w !== null ? w : this.panelSize.width;
36136             h = h !== null ? h : this.panelSize.height;
36137         }
36138         if(this.activePanel){
36139             var el = this.activePanel.getEl();
36140             w = w !== null ? w : el.getWidth();
36141             h = h !== null ? h : el.getHeight();
36142             this.panelSize = {width: w, height: h};
36143             this.activePanel.setSize(w, h);
36144         }
36145         if(Roo.isIE && this.tabs){
36146             this.tabs.el.repaint();
36147         }
36148     },
36149
36150     /**
36151      * Returns the container element for this region.
36152      * @return {Roo.Element}
36153      */
36154     getEl : function(){
36155         return this.el;
36156     },
36157
36158     /**
36159      * Hides this region.
36160      */
36161     hide : function(){
36162         //if(!this.collapsed){
36163             this.el.dom.style.left = "-2000px";
36164             this.el.hide();
36165         //}else{
36166          //   this.collapsedEl.dom.style.left = "-2000px";
36167          //   this.collapsedEl.hide();
36168        // }
36169         this.visible = false;
36170         this.fireEvent("visibilitychange", this, false);
36171     },
36172
36173     /**
36174      * Shows this region if it was previously hidden.
36175      */
36176     show : function(){
36177         //if(!this.collapsed){
36178             this.el.show();
36179         //}else{
36180         //    this.collapsedEl.show();
36181        // }
36182         this.visible = true;
36183         this.fireEvent("visibilitychange", this, true);
36184     },
36185 /*
36186     closeClicked : function(){
36187         if(this.activePanel){
36188             this.remove(this.activePanel);
36189         }
36190     },
36191
36192     collapseClick : function(e){
36193         if(this.isSlid){
36194            e.stopPropagation();
36195            this.slideIn();
36196         }else{
36197            e.stopPropagation();
36198            this.slideOut();
36199         }
36200     },
36201 */
36202     /**
36203      * Collapses this region.
36204      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36205      */
36206     /*
36207     collapse : function(skipAnim, skipCheck = false){
36208         if(this.collapsed) {
36209             return;
36210         }
36211         
36212         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36213             
36214             this.collapsed = true;
36215             if(this.split){
36216                 this.split.el.hide();
36217             }
36218             if(this.config.animate && skipAnim !== true){
36219                 this.fireEvent("invalidated", this);
36220                 this.animateCollapse();
36221             }else{
36222                 this.el.setLocation(-20000,-20000);
36223                 this.el.hide();
36224                 this.collapsedEl.show();
36225                 this.fireEvent("collapsed", this);
36226                 this.fireEvent("invalidated", this);
36227             }
36228         }
36229         
36230     },
36231 */
36232     animateCollapse : function(){
36233         // overridden
36234     },
36235
36236     /**
36237      * Expands this region if it was previously collapsed.
36238      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36239      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36240      */
36241     /*
36242     expand : function(e, skipAnim){
36243         if(e) {
36244             e.stopPropagation();
36245         }
36246         if(!this.collapsed || this.el.hasActiveFx()) {
36247             return;
36248         }
36249         if(this.isSlid){
36250             this.afterSlideIn();
36251             skipAnim = true;
36252         }
36253         this.collapsed = false;
36254         if(this.config.animate && skipAnim !== true){
36255             this.animateExpand();
36256         }else{
36257             this.el.show();
36258             if(this.split){
36259                 this.split.el.show();
36260             }
36261             this.collapsedEl.setLocation(-2000,-2000);
36262             this.collapsedEl.hide();
36263             this.fireEvent("invalidated", this);
36264             this.fireEvent("expanded", this);
36265         }
36266     },
36267 */
36268     animateExpand : function(){
36269         // overridden
36270     },
36271
36272     initTabs : function()
36273     {
36274         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36275         
36276         var ts = new Roo.bootstrap.panel.Tabs({
36277                 el: this.bodyEl.dom,
36278                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36279                 disableTooltips: this.config.disableTabTips,
36280                 toolbar : this.config.toolbar
36281             });
36282         
36283         if(this.config.hideTabs){
36284             ts.stripWrap.setDisplayed(false);
36285         }
36286         this.tabs = ts;
36287         ts.resizeTabs = this.config.resizeTabs === true;
36288         ts.minTabWidth = this.config.minTabWidth || 40;
36289         ts.maxTabWidth = this.config.maxTabWidth || 250;
36290         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36291         ts.monitorResize = false;
36292         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36293         ts.bodyEl.addClass('roo-layout-tabs-body');
36294         this.panels.each(this.initPanelAsTab, this);
36295     },
36296
36297     initPanelAsTab : function(panel){
36298         var ti = this.tabs.addTab(
36299             panel.getEl().id,
36300             panel.getTitle(),
36301             null,
36302             this.config.closeOnTab && panel.isClosable(),
36303             panel.tpl
36304         );
36305         if(panel.tabTip !== undefined){
36306             ti.setTooltip(panel.tabTip);
36307         }
36308         ti.on("activate", function(){
36309               this.setActivePanel(panel);
36310         }, this);
36311         
36312         if(this.config.closeOnTab){
36313             ti.on("beforeclose", function(t, e){
36314                 e.cancel = true;
36315                 this.remove(panel);
36316             }, this);
36317         }
36318         
36319         panel.tabItem = ti;
36320         
36321         return ti;
36322     },
36323
36324     updatePanelTitle : function(panel, title)
36325     {
36326         if(this.activePanel == panel){
36327             this.updateTitle(title);
36328         }
36329         if(this.tabs){
36330             var ti = this.tabs.getTab(panel.getEl().id);
36331             ti.setText(title);
36332             if(panel.tabTip !== undefined){
36333                 ti.setTooltip(panel.tabTip);
36334             }
36335         }
36336     },
36337
36338     updateTitle : function(title){
36339         if(this.titleTextEl && !this.config.title){
36340             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36341         }
36342     },
36343
36344     setActivePanel : function(panel)
36345     {
36346         panel = this.getPanel(panel);
36347         if(this.activePanel && this.activePanel != panel){
36348             if(this.activePanel.setActiveState(false) === false){
36349                 return;
36350             }
36351         }
36352         this.activePanel = panel;
36353         panel.setActiveState(true);
36354         if(this.panelSize){
36355             panel.setSize(this.panelSize.width, this.panelSize.height);
36356         }
36357         if(this.closeBtn){
36358             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36359         }
36360         this.updateTitle(panel.getTitle());
36361         if(this.tabs){
36362             this.fireEvent("invalidated", this);
36363         }
36364         this.fireEvent("panelactivated", this, panel);
36365     },
36366
36367     /**
36368      * Shows the specified panel.
36369      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36370      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36371      */
36372     showPanel : function(panel)
36373     {
36374         panel = this.getPanel(panel);
36375         if(panel){
36376             if(this.tabs){
36377                 var tab = this.tabs.getTab(panel.getEl().id);
36378                 if(tab.isHidden()){
36379                     this.tabs.unhideTab(tab.id);
36380                 }
36381                 tab.activate();
36382             }else{
36383                 this.setActivePanel(panel);
36384             }
36385         }
36386         return panel;
36387     },
36388
36389     /**
36390      * Get the active panel for this region.
36391      * @return {Roo.ContentPanel} The active panel or null
36392      */
36393     getActivePanel : function(){
36394         return this.activePanel;
36395     },
36396
36397     validateVisibility : function(){
36398         if(this.panels.getCount() < 1){
36399             this.updateTitle("&#160;");
36400             this.closeBtn.hide();
36401             this.hide();
36402         }else{
36403             if(!this.isVisible()){
36404                 this.show();
36405             }
36406         }
36407     },
36408
36409     /**
36410      * Adds the passed ContentPanel(s) to this region.
36411      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36412      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36413      */
36414     add : function(panel)
36415     {
36416         if(arguments.length > 1){
36417             for(var i = 0, len = arguments.length; i < len; i++) {
36418                 this.add(arguments[i]);
36419             }
36420             return null;
36421         }
36422         
36423         // if we have not been rendered yet, then we can not really do much of this..
36424         if (!this.bodyEl) {
36425             this.unrendered_panels.push(panel);
36426             return panel;
36427         }
36428         
36429         
36430         
36431         
36432         if(this.hasPanel(panel)){
36433             this.showPanel(panel);
36434             return panel;
36435         }
36436         panel.setRegion(this);
36437         this.panels.add(panel);
36438        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36439             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36440             // and hide them... ???
36441             this.bodyEl.dom.appendChild(panel.getEl().dom);
36442             if(panel.background !== true){
36443                 this.setActivePanel(panel);
36444             }
36445             this.fireEvent("paneladded", this, panel);
36446             return panel;
36447         }
36448         */
36449         if(!this.tabs){
36450             this.initTabs();
36451         }else{
36452             this.initPanelAsTab(panel);
36453         }
36454         
36455         
36456         if(panel.background !== true){
36457             this.tabs.activate(panel.getEl().id);
36458         }
36459         this.fireEvent("paneladded", this, panel);
36460         return panel;
36461     },
36462
36463     /**
36464      * Hides the tab for the specified panel.
36465      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36466      */
36467     hidePanel : function(panel){
36468         if(this.tabs && (panel = this.getPanel(panel))){
36469             this.tabs.hideTab(panel.getEl().id);
36470         }
36471     },
36472
36473     /**
36474      * Unhides the tab for a previously hidden panel.
36475      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36476      */
36477     unhidePanel : function(panel){
36478         if(this.tabs && (panel = this.getPanel(panel))){
36479             this.tabs.unhideTab(panel.getEl().id);
36480         }
36481     },
36482
36483     clearPanels : function(){
36484         while(this.panels.getCount() > 0){
36485              this.remove(this.panels.first());
36486         }
36487     },
36488
36489     /**
36490      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36491      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36492      * @param {Boolean} preservePanel Overrides the config preservePanel option
36493      * @return {Roo.ContentPanel} The panel that was removed
36494      */
36495     remove : function(panel, preservePanel)
36496     {
36497         panel = this.getPanel(panel);
36498         if(!panel){
36499             return null;
36500         }
36501         var e = {};
36502         this.fireEvent("beforeremove", this, panel, e);
36503         if(e.cancel === true){
36504             return null;
36505         }
36506         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36507         var panelId = panel.getId();
36508         this.panels.removeKey(panelId);
36509         if(preservePanel){
36510             document.body.appendChild(panel.getEl().dom);
36511         }
36512         if(this.tabs){
36513             this.tabs.removeTab(panel.getEl().id);
36514         }else if (!preservePanel){
36515             this.bodyEl.dom.removeChild(panel.getEl().dom);
36516         }
36517         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36518             var p = this.panels.first();
36519             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36520             tempEl.appendChild(p.getEl().dom);
36521             this.bodyEl.update("");
36522             this.bodyEl.dom.appendChild(p.getEl().dom);
36523             tempEl = null;
36524             this.updateTitle(p.getTitle());
36525             this.tabs = null;
36526             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36527             this.setActivePanel(p);
36528         }
36529         panel.setRegion(null);
36530         if(this.activePanel == panel){
36531             this.activePanel = null;
36532         }
36533         if(this.config.autoDestroy !== false && preservePanel !== true){
36534             try{panel.destroy();}catch(e){}
36535         }
36536         this.fireEvent("panelremoved", this, panel);
36537         return panel;
36538     },
36539
36540     /**
36541      * Returns the TabPanel component used by this region
36542      * @return {Roo.TabPanel}
36543      */
36544     getTabs : function(){
36545         return this.tabs;
36546     },
36547
36548     createTool : function(parentEl, className){
36549         var btn = Roo.DomHelper.append(parentEl, {
36550             tag: "div",
36551             cls: "x-layout-tools-button",
36552             children: [ {
36553                 tag: "div",
36554                 cls: "roo-layout-tools-button-inner " + className,
36555                 html: "&#160;"
36556             }]
36557         }, true);
36558         btn.addClassOnOver("roo-layout-tools-button-over");
36559         return btn;
36560     }
36561 });/*
36562  * Based on:
36563  * Ext JS Library 1.1.1
36564  * Copyright(c) 2006-2007, Ext JS, LLC.
36565  *
36566  * Originally Released Under LGPL - original licence link has changed is not relivant.
36567  *
36568  * Fork - LGPL
36569  * <script type="text/javascript">
36570  */
36571  
36572
36573
36574 /**
36575  * @class Roo.SplitLayoutRegion
36576  * @extends Roo.LayoutRegion
36577  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36578  */
36579 Roo.bootstrap.layout.Split = function(config){
36580     this.cursor = config.cursor;
36581     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36582 };
36583
36584 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36585 {
36586     splitTip : "Drag to resize.",
36587     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36588     useSplitTips : false,
36589
36590     applyConfig : function(config){
36591         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36592     },
36593     
36594     onRender : function(ctr,pos) {
36595         
36596         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36597         if(!this.config.split){
36598             return;
36599         }
36600         if(!this.split){
36601             
36602             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36603                             tag: "div",
36604                             id: this.el.id + "-split",
36605                             cls: "roo-layout-split roo-layout-split-"+this.position,
36606                             html: "&#160;"
36607             });
36608             /** The SplitBar for this region 
36609             * @type Roo.SplitBar */
36610             // does not exist yet...
36611             Roo.log([this.position, this.orientation]);
36612             
36613             this.split = new Roo.bootstrap.SplitBar({
36614                 dragElement : splitEl,
36615                 resizingElement: this.el,
36616                 orientation : this.orientation
36617             });
36618             
36619             this.split.on("moved", this.onSplitMove, this);
36620             this.split.useShim = this.config.useShim === true;
36621             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36622             if(this.useSplitTips){
36623                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36624             }
36625             //if(config.collapsible){
36626             //    this.split.el.on("dblclick", this.collapse,  this);
36627             //}
36628         }
36629         if(typeof this.config.minSize != "undefined"){
36630             this.split.minSize = this.config.minSize;
36631         }
36632         if(typeof this.config.maxSize != "undefined"){
36633             this.split.maxSize = this.config.maxSize;
36634         }
36635         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36636             this.hideSplitter();
36637         }
36638         
36639     },
36640
36641     getHMaxSize : function(){
36642          var cmax = this.config.maxSize || 10000;
36643          var center = this.mgr.getRegion("center");
36644          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36645     },
36646
36647     getVMaxSize : function(){
36648          var cmax = this.config.maxSize || 10000;
36649          var center = this.mgr.getRegion("center");
36650          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36651     },
36652
36653     onSplitMove : function(split, newSize){
36654         this.fireEvent("resized", this, newSize);
36655     },
36656     
36657     /** 
36658      * Returns the {@link Roo.SplitBar} for this region.
36659      * @return {Roo.SplitBar}
36660      */
36661     getSplitBar : function(){
36662         return this.split;
36663     },
36664     
36665     hide : function(){
36666         this.hideSplitter();
36667         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36668     },
36669
36670     hideSplitter : function(){
36671         if(this.split){
36672             this.split.el.setLocation(-2000,-2000);
36673             this.split.el.hide();
36674         }
36675     },
36676
36677     show : function(){
36678         if(this.split){
36679             this.split.el.show();
36680         }
36681         Roo.bootstrap.layout.Split.superclass.show.call(this);
36682     },
36683     
36684     beforeSlide: function(){
36685         if(Roo.isGecko){// firefox overflow auto bug workaround
36686             this.bodyEl.clip();
36687             if(this.tabs) {
36688                 this.tabs.bodyEl.clip();
36689             }
36690             if(this.activePanel){
36691                 this.activePanel.getEl().clip();
36692                 
36693                 if(this.activePanel.beforeSlide){
36694                     this.activePanel.beforeSlide();
36695                 }
36696             }
36697         }
36698     },
36699     
36700     afterSlide : function(){
36701         if(Roo.isGecko){// firefox overflow auto bug workaround
36702             this.bodyEl.unclip();
36703             if(this.tabs) {
36704                 this.tabs.bodyEl.unclip();
36705             }
36706             if(this.activePanel){
36707                 this.activePanel.getEl().unclip();
36708                 if(this.activePanel.afterSlide){
36709                     this.activePanel.afterSlide();
36710                 }
36711             }
36712         }
36713     },
36714
36715     initAutoHide : function(){
36716         if(this.autoHide !== false){
36717             if(!this.autoHideHd){
36718                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36719                 this.autoHideHd = {
36720                     "mouseout": function(e){
36721                         if(!e.within(this.el, true)){
36722                             st.delay(500);
36723                         }
36724                     },
36725                     "mouseover" : function(e){
36726                         st.cancel();
36727                     },
36728                     scope : this
36729                 };
36730             }
36731             this.el.on(this.autoHideHd);
36732         }
36733     },
36734
36735     clearAutoHide : function(){
36736         if(this.autoHide !== false){
36737             this.el.un("mouseout", this.autoHideHd.mouseout);
36738             this.el.un("mouseover", this.autoHideHd.mouseover);
36739         }
36740     },
36741
36742     clearMonitor : function(){
36743         Roo.get(document).un("click", this.slideInIf, this);
36744     },
36745
36746     // these names are backwards but not changed for compat
36747     slideOut : function(){
36748         if(this.isSlid || this.el.hasActiveFx()){
36749             return;
36750         }
36751         this.isSlid = true;
36752         if(this.collapseBtn){
36753             this.collapseBtn.hide();
36754         }
36755         this.closeBtnState = this.closeBtn.getStyle('display');
36756         this.closeBtn.hide();
36757         if(this.stickBtn){
36758             this.stickBtn.show();
36759         }
36760         this.el.show();
36761         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36762         this.beforeSlide();
36763         this.el.setStyle("z-index", 10001);
36764         this.el.slideIn(this.getSlideAnchor(), {
36765             callback: function(){
36766                 this.afterSlide();
36767                 this.initAutoHide();
36768                 Roo.get(document).on("click", this.slideInIf, this);
36769                 this.fireEvent("slideshow", this);
36770             },
36771             scope: this,
36772             block: true
36773         });
36774     },
36775
36776     afterSlideIn : function(){
36777         this.clearAutoHide();
36778         this.isSlid = false;
36779         this.clearMonitor();
36780         this.el.setStyle("z-index", "");
36781         if(this.collapseBtn){
36782             this.collapseBtn.show();
36783         }
36784         this.closeBtn.setStyle('display', this.closeBtnState);
36785         if(this.stickBtn){
36786             this.stickBtn.hide();
36787         }
36788         this.fireEvent("slidehide", this);
36789     },
36790
36791     slideIn : function(cb){
36792         if(!this.isSlid || this.el.hasActiveFx()){
36793             Roo.callback(cb);
36794             return;
36795         }
36796         this.isSlid = false;
36797         this.beforeSlide();
36798         this.el.slideOut(this.getSlideAnchor(), {
36799             callback: function(){
36800                 this.el.setLeftTop(-10000, -10000);
36801                 this.afterSlide();
36802                 this.afterSlideIn();
36803                 Roo.callback(cb);
36804             },
36805             scope: this,
36806             block: true
36807         });
36808     },
36809     
36810     slideInIf : function(e){
36811         if(!e.within(this.el)){
36812             this.slideIn();
36813         }
36814     },
36815
36816     animateCollapse : function(){
36817         this.beforeSlide();
36818         this.el.setStyle("z-index", 20000);
36819         var anchor = this.getSlideAnchor();
36820         this.el.slideOut(anchor, {
36821             callback : function(){
36822                 this.el.setStyle("z-index", "");
36823                 this.collapsedEl.slideIn(anchor, {duration:.3});
36824                 this.afterSlide();
36825                 this.el.setLocation(-10000,-10000);
36826                 this.el.hide();
36827                 this.fireEvent("collapsed", this);
36828             },
36829             scope: this,
36830             block: true
36831         });
36832     },
36833
36834     animateExpand : function(){
36835         this.beforeSlide();
36836         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36837         this.el.setStyle("z-index", 20000);
36838         this.collapsedEl.hide({
36839             duration:.1
36840         });
36841         this.el.slideIn(this.getSlideAnchor(), {
36842             callback : function(){
36843                 this.el.setStyle("z-index", "");
36844                 this.afterSlide();
36845                 if(this.split){
36846                     this.split.el.show();
36847                 }
36848                 this.fireEvent("invalidated", this);
36849                 this.fireEvent("expanded", this);
36850             },
36851             scope: this,
36852             block: true
36853         });
36854     },
36855
36856     anchors : {
36857         "west" : "left",
36858         "east" : "right",
36859         "north" : "top",
36860         "south" : "bottom"
36861     },
36862
36863     sanchors : {
36864         "west" : "l",
36865         "east" : "r",
36866         "north" : "t",
36867         "south" : "b"
36868     },
36869
36870     canchors : {
36871         "west" : "tl-tr",
36872         "east" : "tr-tl",
36873         "north" : "tl-bl",
36874         "south" : "bl-tl"
36875     },
36876
36877     getAnchor : function(){
36878         return this.anchors[this.position];
36879     },
36880
36881     getCollapseAnchor : function(){
36882         return this.canchors[this.position];
36883     },
36884
36885     getSlideAnchor : function(){
36886         return this.sanchors[this.position];
36887     },
36888
36889     getAlignAdj : function(){
36890         var cm = this.cmargins;
36891         switch(this.position){
36892             case "west":
36893                 return [0, 0];
36894             break;
36895             case "east":
36896                 return [0, 0];
36897             break;
36898             case "north":
36899                 return [0, 0];
36900             break;
36901             case "south":
36902                 return [0, 0];
36903             break;
36904         }
36905     },
36906
36907     getExpandAdj : function(){
36908         var c = this.collapsedEl, cm = this.cmargins;
36909         switch(this.position){
36910             case "west":
36911                 return [-(cm.right+c.getWidth()+cm.left), 0];
36912             break;
36913             case "east":
36914                 return [cm.right+c.getWidth()+cm.left, 0];
36915             break;
36916             case "north":
36917                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36918             break;
36919             case "south":
36920                 return [0, cm.top+cm.bottom+c.getHeight()];
36921             break;
36922         }
36923     }
36924 });/*
36925  * Based on:
36926  * Ext JS Library 1.1.1
36927  * Copyright(c) 2006-2007, Ext JS, LLC.
36928  *
36929  * Originally Released Under LGPL - original licence link has changed is not relivant.
36930  *
36931  * Fork - LGPL
36932  * <script type="text/javascript">
36933  */
36934 /*
36935  * These classes are private internal classes
36936  */
36937 Roo.bootstrap.layout.Center = function(config){
36938     config.region = "center";
36939     Roo.bootstrap.layout.Region.call(this, config);
36940     this.visible = true;
36941     this.minWidth = config.minWidth || 20;
36942     this.minHeight = config.minHeight || 20;
36943 };
36944
36945 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36946     hide : function(){
36947         // center panel can't be hidden
36948     },
36949     
36950     show : function(){
36951         // center panel can't be hidden
36952     },
36953     
36954     getMinWidth: function(){
36955         return this.minWidth;
36956     },
36957     
36958     getMinHeight: function(){
36959         return this.minHeight;
36960     }
36961 });
36962
36963
36964
36965
36966  
36967
36968
36969
36970
36971
36972 Roo.bootstrap.layout.North = function(config)
36973 {
36974     config.region = 'north';
36975     config.cursor = 'n-resize';
36976     
36977     Roo.bootstrap.layout.Split.call(this, config);
36978     
36979     
36980     if(this.split){
36981         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36982         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36983         this.split.el.addClass("roo-layout-split-v");
36984     }
36985     var size = config.initialSize || config.height;
36986     if(typeof size != "undefined"){
36987         this.el.setHeight(size);
36988     }
36989 };
36990 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36991 {
36992     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36993     
36994     
36995     
36996     getBox : function(){
36997         if(this.collapsed){
36998             return this.collapsedEl.getBox();
36999         }
37000         var box = this.el.getBox();
37001         if(this.split){
37002             box.height += this.split.el.getHeight();
37003         }
37004         return box;
37005     },
37006     
37007     updateBox : function(box){
37008         if(this.split && !this.collapsed){
37009             box.height -= this.split.el.getHeight();
37010             this.split.el.setLeft(box.x);
37011             this.split.el.setTop(box.y+box.height);
37012             this.split.el.setWidth(box.width);
37013         }
37014         if(this.collapsed){
37015             this.updateBody(box.width, null);
37016         }
37017         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37018     }
37019 });
37020
37021
37022
37023
37024
37025 Roo.bootstrap.layout.South = function(config){
37026     config.region = 'south';
37027     config.cursor = 's-resize';
37028     Roo.bootstrap.layout.Split.call(this, config);
37029     if(this.split){
37030         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37031         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37032         this.split.el.addClass("roo-layout-split-v");
37033     }
37034     var size = config.initialSize || config.height;
37035     if(typeof size != "undefined"){
37036         this.el.setHeight(size);
37037     }
37038 };
37039
37040 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37041     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37042     getBox : function(){
37043         if(this.collapsed){
37044             return this.collapsedEl.getBox();
37045         }
37046         var box = this.el.getBox();
37047         if(this.split){
37048             var sh = this.split.el.getHeight();
37049             box.height += sh;
37050             box.y -= sh;
37051         }
37052         return box;
37053     },
37054     
37055     updateBox : function(box){
37056         if(this.split && !this.collapsed){
37057             var sh = this.split.el.getHeight();
37058             box.height -= sh;
37059             box.y += sh;
37060             this.split.el.setLeft(box.x);
37061             this.split.el.setTop(box.y-sh);
37062             this.split.el.setWidth(box.width);
37063         }
37064         if(this.collapsed){
37065             this.updateBody(box.width, null);
37066         }
37067         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37068     }
37069 });
37070
37071 Roo.bootstrap.layout.East = function(config){
37072     config.region = "east";
37073     config.cursor = "e-resize";
37074     Roo.bootstrap.layout.Split.call(this, config);
37075     if(this.split){
37076         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37077         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37078         this.split.el.addClass("roo-layout-split-h");
37079     }
37080     var size = config.initialSize || config.width;
37081     if(typeof size != "undefined"){
37082         this.el.setWidth(size);
37083     }
37084 };
37085 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37086     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37087     getBox : function(){
37088         if(this.collapsed){
37089             return this.collapsedEl.getBox();
37090         }
37091         var box = this.el.getBox();
37092         if(this.split){
37093             var sw = this.split.el.getWidth();
37094             box.width += sw;
37095             box.x -= sw;
37096         }
37097         return box;
37098     },
37099
37100     updateBox : function(box){
37101         if(this.split && !this.collapsed){
37102             var sw = this.split.el.getWidth();
37103             box.width -= sw;
37104             this.split.el.setLeft(box.x);
37105             this.split.el.setTop(box.y);
37106             this.split.el.setHeight(box.height);
37107             box.x += sw;
37108         }
37109         if(this.collapsed){
37110             this.updateBody(null, box.height);
37111         }
37112         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37113     }
37114 });
37115
37116 Roo.bootstrap.layout.West = function(config){
37117     config.region = "west";
37118     config.cursor = "w-resize";
37119     
37120     Roo.bootstrap.layout.Split.call(this, config);
37121     if(this.split){
37122         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37123         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37124         this.split.el.addClass("roo-layout-split-h");
37125     }
37126     
37127 };
37128 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37129     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37130     
37131     onRender: function(ctr, pos)
37132     {
37133         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37134         var size = this.config.initialSize || this.config.width;
37135         if(typeof size != "undefined"){
37136             this.el.setWidth(size);
37137         }
37138     },
37139     
37140     getBox : function(){
37141         if(this.collapsed){
37142             return this.collapsedEl.getBox();
37143         }
37144         var box = this.el.getBox();
37145         if(this.split){
37146             box.width += this.split.el.getWidth();
37147         }
37148         return box;
37149     },
37150     
37151     updateBox : function(box){
37152         if(this.split && !this.collapsed){
37153             var sw = this.split.el.getWidth();
37154             box.width -= sw;
37155             this.split.el.setLeft(box.x+box.width);
37156             this.split.el.setTop(box.y);
37157             this.split.el.setHeight(box.height);
37158         }
37159         if(this.collapsed){
37160             this.updateBody(null, box.height);
37161         }
37162         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37163     }
37164 });
37165 Roo.namespace("Roo.bootstrap.panel");/*
37166  * Based on:
37167  * Ext JS Library 1.1.1
37168  * Copyright(c) 2006-2007, Ext JS, LLC.
37169  *
37170  * Originally Released Under LGPL - original licence link has changed is not relivant.
37171  *
37172  * Fork - LGPL
37173  * <script type="text/javascript">
37174  */
37175 /**
37176  * @class Roo.ContentPanel
37177  * @extends Roo.util.Observable
37178  * A basic ContentPanel element.
37179  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37180  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37181  * @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
37182  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37183  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37184  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37185  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37186  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37187  * @cfg {String} title          The title for this panel
37188  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37189  * @cfg {String} url            Calls {@link #setUrl} with this value
37190  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37191  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37192  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37193  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37194  * @cfg {Boolean} badges render the badges
37195
37196  * @constructor
37197  * Create a new ContentPanel.
37198  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37199  * @param {String/Object} config A string to set only the title or a config object
37200  * @param {String} content (optional) Set the HTML content for this panel
37201  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37202  */
37203 Roo.bootstrap.panel.Content = function( config){
37204     
37205     this.tpl = config.tpl || false;
37206     
37207     var el = config.el;
37208     var content = config.content;
37209
37210     if(config.autoCreate){ // xtype is available if this is called from factory
37211         el = Roo.id();
37212     }
37213     this.el = Roo.get(el);
37214     if(!this.el && config && config.autoCreate){
37215         if(typeof config.autoCreate == "object"){
37216             if(!config.autoCreate.id){
37217                 config.autoCreate.id = config.id||el;
37218             }
37219             this.el = Roo.DomHelper.append(document.body,
37220                         config.autoCreate, true);
37221         }else{
37222             var elcfg =  {   tag: "div",
37223                             cls: "roo-layout-inactive-content",
37224                             id: config.id||el
37225                             };
37226             if (config.html) {
37227                 elcfg.html = config.html;
37228                 
37229             }
37230                         
37231             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37232         }
37233     } 
37234     this.closable = false;
37235     this.loaded = false;
37236     this.active = false;
37237    
37238       
37239     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37240         
37241         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37242         
37243         this.wrapEl = this.el; //this.el.wrap();
37244         var ti = [];
37245         if (config.toolbar.items) {
37246             ti = config.toolbar.items ;
37247             delete config.toolbar.items ;
37248         }
37249         
37250         var nitems = [];
37251         this.toolbar.render(this.wrapEl, 'before');
37252         for(var i =0;i < ti.length;i++) {
37253           //  Roo.log(['add child', items[i]]);
37254             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37255         }
37256         this.toolbar.items = nitems;
37257         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37258         delete config.toolbar;
37259         
37260     }
37261     /*
37262     // xtype created footer. - not sure if will work as we normally have to render first..
37263     if (this.footer && !this.footer.el && this.footer.xtype) {
37264         if (!this.wrapEl) {
37265             this.wrapEl = this.el.wrap();
37266         }
37267     
37268         this.footer.container = this.wrapEl.createChild();
37269          
37270         this.footer = Roo.factory(this.footer, Roo);
37271         
37272     }
37273     */
37274     
37275      if(typeof config == "string"){
37276         this.title = config;
37277     }else{
37278         Roo.apply(this, config);
37279     }
37280     
37281     if(this.resizeEl){
37282         this.resizeEl = Roo.get(this.resizeEl, true);
37283     }else{
37284         this.resizeEl = this.el;
37285     }
37286     // handle view.xtype
37287     
37288  
37289     
37290     
37291     this.addEvents({
37292         /**
37293          * @event activate
37294          * Fires when this panel is activated. 
37295          * @param {Roo.ContentPanel} this
37296          */
37297         "activate" : true,
37298         /**
37299          * @event deactivate
37300          * Fires when this panel is activated. 
37301          * @param {Roo.ContentPanel} this
37302          */
37303         "deactivate" : true,
37304
37305         /**
37306          * @event resize
37307          * Fires when this panel is resized if fitToFrame is true.
37308          * @param {Roo.ContentPanel} this
37309          * @param {Number} width The width after any component adjustments
37310          * @param {Number} height The height after any component adjustments
37311          */
37312         "resize" : true,
37313         
37314          /**
37315          * @event render
37316          * Fires when this tab is created
37317          * @param {Roo.ContentPanel} this
37318          */
37319         "render" : true
37320         
37321         
37322         
37323     });
37324     
37325
37326     
37327     
37328     if(this.autoScroll){
37329         this.resizeEl.setStyle("overflow", "auto");
37330     } else {
37331         // fix randome scrolling
37332         //this.el.on('scroll', function() {
37333         //    Roo.log('fix random scolling');
37334         //    this.scrollTo('top',0); 
37335         //});
37336     }
37337     content = content || this.content;
37338     if(content){
37339         this.setContent(content);
37340     }
37341     if(config && config.url){
37342         this.setUrl(this.url, this.params, this.loadOnce);
37343     }
37344     
37345     
37346     
37347     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37348     
37349     if (this.view && typeof(this.view.xtype) != 'undefined') {
37350         this.view.el = this.el.appendChild(document.createElement("div"));
37351         this.view = Roo.factory(this.view); 
37352         this.view.render  &&  this.view.render(false, '');  
37353     }
37354     
37355     
37356     this.fireEvent('render', this);
37357 };
37358
37359 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37360     
37361     tabTip : '',
37362     
37363     setRegion : function(region){
37364         this.region = region;
37365         this.setActiveClass(region && !this.background);
37366     },
37367     
37368     
37369     setActiveClass: function(state)
37370     {
37371         if(state){
37372            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37373            this.el.setStyle('position','relative');
37374         }else{
37375            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37376            this.el.setStyle('position', 'absolute');
37377         } 
37378     },
37379     
37380     /**
37381      * Returns the toolbar for this Panel if one was configured. 
37382      * @return {Roo.Toolbar} 
37383      */
37384     getToolbar : function(){
37385         return this.toolbar;
37386     },
37387     
37388     setActiveState : function(active)
37389     {
37390         this.active = active;
37391         this.setActiveClass(active);
37392         if(!active){
37393             if(this.fireEvent("deactivate", this) === false){
37394                 return false;
37395             }
37396             return true;
37397         }
37398         this.fireEvent("activate", this);
37399         return true;
37400     },
37401     /**
37402      * Updates this panel's element
37403      * @param {String} content The new content
37404      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37405     */
37406     setContent : function(content, loadScripts){
37407         this.el.update(content, loadScripts);
37408     },
37409
37410     ignoreResize : function(w, h){
37411         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37412             return true;
37413         }else{
37414             this.lastSize = {width: w, height: h};
37415             return false;
37416         }
37417     },
37418     /**
37419      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37420      * @return {Roo.UpdateManager} The UpdateManager
37421      */
37422     getUpdateManager : function(){
37423         return this.el.getUpdateManager();
37424     },
37425      /**
37426      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37427      * @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:
37428 <pre><code>
37429 panel.load({
37430     url: "your-url.php",
37431     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37432     callback: yourFunction,
37433     scope: yourObject, //(optional scope)
37434     discardUrl: false,
37435     nocache: false,
37436     text: "Loading...",
37437     timeout: 30,
37438     scripts: false
37439 });
37440 </code></pre>
37441      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37442      * 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.
37443      * @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}
37444      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37445      * @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.
37446      * @return {Roo.ContentPanel} this
37447      */
37448     load : function(){
37449         var um = this.el.getUpdateManager();
37450         um.update.apply(um, arguments);
37451         return this;
37452     },
37453
37454
37455     /**
37456      * 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.
37457      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37458      * @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)
37459      * @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)
37460      * @return {Roo.UpdateManager} The UpdateManager
37461      */
37462     setUrl : function(url, params, loadOnce){
37463         if(this.refreshDelegate){
37464             this.removeListener("activate", this.refreshDelegate);
37465         }
37466         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37467         this.on("activate", this.refreshDelegate);
37468         return this.el.getUpdateManager();
37469     },
37470     
37471     _handleRefresh : function(url, params, loadOnce){
37472         if(!loadOnce || !this.loaded){
37473             var updater = this.el.getUpdateManager();
37474             updater.update(url, params, this._setLoaded.createDelegate(this));
37475         }
37476     },
37477     
37478     _setLoaded : function(){
37479         this.loaded = true;
37480     }, 
37481     
37482     /**
37483      * Returns this panel's id
37484      * @return {String} 
37485      */
37486     getId : function(){
37487         return this.el.id;
37488     },
37489     
37490     /** 
37491      * Returns this panel's element - used by regiosn to add.
37492      * @return {Roo.Element} 
37493      */
37494     getEl : function(){
37495         return this.wrapEl || this.el;
37496     },
37497     
37498    
37499     
37500     adjustForComponents : function(width, height)
37501     {
37502         //Roo.log('adjustForComponents ');
37503         if(this.resizeEl != this.el){
37504             width -= this.el.getFrameWidth('lr');
37505             height -= this.el.getFrameWidth('tb');
37506         }
37507         if(this.toolbar){
37508             var te = this.toolbar.getEl();
37509             te.setWidth(width);
37510             height -= te.getHeight();
37511         }
37512         if(this.footer){
37513             var te = this.footer.getEl();
37514             te.setWidth(width);
37515             height -= te.getHeight();
37516         }
37517         
37518         
37519         if(this.adjustments){
37520             width += this.adjustments[0];
37521             height += this.adjustments[1];
37522         }
37523         return {"width": width, "height": height};
37524     },
37525     
37526     setSize : function(width, height){
37527         if(this.fitToFrame && !this.ignoreResize(width, height)){
37528             if(this.fitContainer && this.resizeEl != this.el){
37529                 this.el.setSize(width, height);
37530             }
37531             var size = this.adjustForComponents(width, height);
37532             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37533             this.fireEvent('resize', this, size.width, size.height);
37534         }
37535     },
37536     
37537     /**
37538      * Returns this panel's title
37539      * @return {String} 
37540      */
37541     getTitle : function(){
37542         
37543         if (typeof(this.title) != 'object') {
37544             return this.title;
37545         }
37546         
37547         var t = '';
37548         for (var k in this.title) {
37549             if (!this.title.hasOwnProperty(k)) {
37550                 continue;
37551             }
37552             
37553             if (k.indexOf('-') >= 0) {
37554                 var s = k.split('-');
37555                 for (var i = 0; i<s.length; i++) {
37556                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37557                 }
37558             } else {
37559                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37560             }
37561         }
37562         return t;
37563     },
37564     
37565     /**
37566      * Set this panel's title
37567      * @param {String} title
37568      */
37569     setTitle : function(title){
37570         this.title = title;
37571         if(this.region){
37572             this.region.updatePanelTitle(this, title);
37573         }
37574     },
37575     
37576     /**
37577      * Returns true is this panel was configured to be closable
37578      * @return {Boolean} 
37579      */
37580     isClosable : function(){
37581         return this.closable;
37582     },
37583     
37584     beforeSlide : function(){
37585         this.el.clip();
37586         this.resizeEl.clip();
37587     },
37588     
37589     afterSlide : function(){
37590         this.el.unclip();
37591         this.resizeEl.unclip();
37592     },
37593     
37594     /**
37595      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37596      *   Will fail silently if the {@link #setUrl} method has not been called.
37597      *   This does not activate the panel, just updates its content.
37598      */
37599     refresh : function(){
37600         if(this.refreshDelegate){
37601            this.loaded = false;
37602            this.refreshDelegate();
37603         }
37604     },
37605     
37606     /**
37607      * Destroys this panel
37608      */
37609     destroy : function(){
37610         this.el.removeAllListeners();
37611         var tempEl = document.createElement("span");
37612         tempEl.appendChild(this.el.dom);
37613         tempEl.innerHTML = "";
37614         this.el.remove();
37615         this.el = null;
37616     },
37617     
37618     /**
37619      * form - if the content panel contains a form - this is a reference to it.
37620      * @type {Roo.form.Form}
37621      */
37622     form : false,
37623     /**
37624      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37625      *    This contains a reference to it.
37626      * @type {Roo.View}
37627      */
37628     view : false,
37629     
37630       /**
37631      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37632      * <pre><code>
37633
37634 layout.addxtype({
37635        xtype : 'Form',
37636        items: [ .... ]
37637    }
37638 );
37639
37640 </code></pre>
37641      * @param {Object} cfg Xtype definition of item to add.
37642      */
37643     
37644     
37645     getChildContainer: function () {
37646         return this.getEl();
37647     }
37648     
37649     
37650     /*
37651         var  ret = new Roo.factory(cfg);
37652         return ret;
37653         
37654         
37655         // add form..
37656         if (cfg.xtype.match(/^Form$/)) {
37657             
37658             var el;
37659             //if (this.footer) {
37660             //    el = this.footer.container.insertSibling(false, 'before');
37661             //} else {
37662                 el = this.el.createChild();
37663             //}
37664
37665             this.form = new  Roo.form.Form(cfg);
37666             
37667             
37668             if ( this.form.allItems.length) {
37669                 this.form.render(el.dom);
37670             }
37671             return this.form;
37672         }
37673         // should only have one of theses..
37674         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37675             // views.. should not be just added - used named prop 'view''
37676             
37677             cfg.el = this.el.appendChild(document.createElement("div"));
37678             // factory?
37679             
37680             var ret = new Roo.factory(cfg);
37681              
37682              ret.render && ret.render(false, ''); // render blank..
37683             this.view = ret;
37684             return ret;
37685         }
37686         return false;
37687     }
37688     \*/
37689 });
37690  
37691 /**
37692  * @class Roo.bootstrap.panel.Grid
37693  * @extends Roo.bootstrap.panel.Content
37694  * @constructor
37695  * Create a new GridPanel.
37696  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37697  * @param {Object} config A the config object
37698   
37699  */
37700
37701
37702
37703 Roo.bootstrap.panel.Grid = function(config)
37704 {
37705     
37706       
37707     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37708         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37709
37710     config.el = this.wrapper;
37711     //this.el = this.wrapper;
37712     
37713       if (config.container) {
37714         // ctor'ed from a Border/panel.grid
37715         
37716         
37717         this.wrapper.setStyle("overflow", "hidden");
37718         this.wrapper.addClass('roo-grid-container');
37719
37720     }
37721     
37722     
37723     if(config.toolbar){
37724         var tool_el = this.wrapper.createChild();    
37725         this.toolbar = Roo.factory(config.toolbar);
37726         var ti = [];
37727         if (config.toolbar.items) {
37728             ti = config.toolbar.items ;
37729             delete config.toolbar.items ;
37730         }
37731         
37732         var nitems = [];
37733         this.toolbar.render(tool_el);
37734         for(var i =0;i < ti.length;i++) {
37735           //  Roo.log(['add child', items[i]]);
37736             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37737         }
37738         this.toolbar.items = nitems;
37739         
37740         delete config.toolbar;
37741     }
37742     
37743     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37744     config.grid.scrollBody = true;;
37745     config.grid.monitorWindowResize = false; // turn off autosizing
37746     config.grid.autoHeight = false;
37747     config.grid.autoWidth = false;
37748     
37749     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37750     
37751     if (config.background) {
37752         // render grid on panel activation (if panel background)
37753         this.on('activate', function(gp) {
37754             if (!gp.grid.rendered) {
37755                 gp.grid.render(this.wrapper);
37756                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37757             }
37758         });
37759             
37760     } else {
37761         this.grid.render(this.wrapper);
37762         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37763
37764     }
37765     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37766     // ??? needed ??? config.el = this.wrapper;
37767     
37768     
37769     
37770   
37771     // xtype created footer. - not sure if will work as we normally have to render first..
37772     if (this.footer && !this.footer.el && this.footer.xtype) {
37773         
37774         var ctr = this.grid.getView().getFooterPanel(true);
37775         this.footer.dataSource = this.grid.dataSource;
37776         this.footer = Roo.factory(this.footer, Roo);
37777         this.footer.render(ctr);
37778         
37779     }
37780     
37781     
37782     
37783     
37784      
37785 };
37786
37787 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37788     getId : function(){
37789         return this.grid.id;
37790     },
37791     
37792     /**
37793      * Returns the grid for this panel
37794      * @return {Roo.bootstrap.Table} 
37795      */
37796     getGrid : function(){
37797         return this.grid;    
37798     },
37799     
37800     setSize : function(width, height){
37801         if(!this.ignoreResize(width, height)){
37802             var grid = this.grid;
37803             var size = this.adjustForComponents(width, height);
37804             var gridel = grid.getGridEl();
37805             gridel.setSize(size.width, size.height);
37806             /*
37807             var thd = grid.getGridEl().select('thead',true).first();
37808             var tbd = grid.getGridEl().select('tbody', true).first();
37809             if (tbd) {
37810                 tbd.setSize(width, height - thd.getHeight());
37811             }
37812             */
37813             grid.autoSize();
37814         }
37815     },
37816      
37817     
37818     
37819     beforeSlide : function(){
37820         this.grid.getView().scroller.clip();
37821     },
37822     
37823     afterSlide : function(){
37824         this.grid.getView().scroller.unclip();
37825     },
37826     
37827     destroy : function(){
37828         this.grid.destroy();
37829         delete this.grid;
37830         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37831     }
37832 });
37833
37834 /**
37835  * @class Roo.bootstrap.panel.Nest
37836  * @extends Roo.bootstrap.panel.Content
37837  * @constructor
37838  * Create a new Panel, that can contain a layout.Border.
37839  * 
37840  * 
37841  * @param {Roo.BorderLayout} layout The layout for this panel
37842  * @param {String/Object} config A string to set only the title or a config object
37843  */
37844 Roo.bootstrap.panel.Nest = function(config)
37845 {
37846     // construct with only one argument..
37847     /* FIXME - implement nicer consturctors
37848     if (layout.layout) {
37849         config = layout;
37850         layout = config.layout;
37851         delete config.layout;
37852     }
37853     if (layout.xtype && !layout.getEl) {
37854         // then layout needs constructing..
37855         layout = Roo.factory(layout, Roo);
37856     }
37857     */
37858     
37859     config.el =  config.layout.getEl();
37860     
37861     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37862     
37863     config.layout.monitorWindowResize = false; // turn off autosizing
37864     this.layout = config.layout;
37865     this.layout.getEl().addClass("roo-layout-nested-layout");
37866     
37867     
37868     
37869     
37870 };
37871
37872 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37873
37874     setSize : function(width, height){
37875         if(!this.ignoreResize(width, height)){
37876             var size = this.adjustForComponents(width, height);
37877             var el = this.layout.getEl();
37878             if (size.height < 1) {
37879                 el.setWidth(size.width);   
37880             } else {
37881                 el.setSize(size.width, size.height);
37882             }
37883             var touch = el.dom.offsetWidth;
37884             this.layout.layout();
37885             // ie requires a double layout on the first pass
37886             if(Roo.isIE && !this.initialized){
37887                 this.initialized = true;
37888                 this.layout.layout();
37889             }
37890         }
37891     },
37892     
37893     // activate all subpanels if not currently active..
37894     
37895     setActiveState : function(active){
37896         this.active = active;
37897         this.setActiveClass(active);
37898         
37899         if(!active){
37900             this.fireEvent("deactivate", this);
37901             return;
37902         }
37903         
37904         this.fireEvent("activate", this);
37905         // not sure if this should happen before or after..
37906         if (!this.layout) {
37907             return; // should not happen..
37908         }
37909         var reg = false;
37910         for (var r in this.layout.regions) {
37911             reg = this.layout.getRegion(r);
37912             if (reg.getActivePanel()) {
37913                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37914                 reg.setActivePanel(reg.getActivePanel());
37915                 continue;
37916             }
37917             if (!reg.panels.length) {
37918                 continue;
37919             }
37920             reg.showPanel(reg.getPanel(0));
37921         }
37922         
37923         
37924         
37925         
37926     },
37927     
37928     /**
37929      * Returns the nested BorderLayout for this panel
37930      * @return {Roo.BorderLayout} 
37931      */
37932     getLayout : function(){
37933         return this.layout;
37934     },
37935     
37936      /**
37937      * Adds a xtype elements to the layout of the nested panel
37938      * <pre><code>
37939
37940 panel.addxtype({
37941        xtype : 'ContentPanel',
37942        region: 'west',
37943        items: [ .... ]
37944    }
37945 );
37946
37947 panel.addxtype({
37948         xtype : 'NestedLayoutPanel',
37949         region: 'west',
37950         layout: {
37951            center: { },
37952            west: { }   
37953         },
37954         items : [ ... list of content panels or nested layout panels.. ]
37955    }
37956 );
37957 </code></pre>
37958      * @param {Object} cfg Xtype definition of item to add.
37959      */
37960     addxtype : function(cfg) {
37961         return this.layout.addxtype(cfg);
37962     
37963     }
37964 });        /*
37965  * Based on:
37966  * Ext JS Library 1.1.1
37967  * Copyright(c) 2006-2007, Ext JS, LLC.
37968  *
37969  * Originally Released Under LGPL - original licence link has changed is not relivant.
37970  *
37971  * Fork - LGPL
37972  * <script type="text/javascript">
37973  */
37974 /**
37975  * @class Roo.TabPanel
37976  * @extends Roo.util.Observable
37977  * A lightweight tab container.
37978  * <br><br>
37979  * Usage:
37980  * <pre><code>
37981 // basic tabs 1, built from existing content
37982 var tabs = new Roo.TabPanel("tabs1");
37983 tabs.addTab("script", "View Script");
37984 tabs.addTab("markup", "View Markup");
37985 tabs.activate("script");
37986
37987 // more advanced tabs, built from javascript
37988 var jtabs = new Roo.TabPanel("jtabs");
37989 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37990
37991 // set up the UpdateManager
37992 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37993 var updater = tab2.getUpdateManager();
37994 updater.setDefaultUrl("ajax1.htm");
37995 tab2.on('activate', updater.refresh, updater, true);
37996
37997 // Use setUrl for Ajax loading
37998 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37999 tab3.setUrl("ajax2.htm", null, true);
38000
38001 // Disabled tab
38002 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38003 tab4.disable();
38004
38005 jtabs.activate("jtabs-1");
38006  * </code></pre>
38007  * @constructor
38008  * Create a new TabPanel.
38009  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38010  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38011  */
38012 Roo.bootstrap.panel.Tabs = function(config){
38013     /**
38014     * The container element for this TabPanel.
38015     * @type Roo.Element
38016     */
38017     this.el = Roo.get(config.el);
38018     delete config.el;
38019     if(config){
38020         if(typeof config == "boolean"){
38021             this.tabPosition = config ? "bottom" : "top";
38022         }else{
38023             Roo.apply(this, config);
38024         }
38025     }
38026     
38027     if(this.tabPosition == "bottom"){
38028         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38029         this.el.addClass("roo-tabs-bottom");
38030     }
38031     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38032     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38033     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38034     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38035     if(Roo.isIE){
38036         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38037     }
38038     if(this.tabPosition != "bottom"){
38039         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38040          * @type Roo.Element
38041          */
38042         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38043         this.el.addClass("roo-tabs-top");
38044     }
38045     this.items = [];
38046
38047     this.bodyEl.setStyle("position", "relative");
38048
38049     this.active = null;
38050     this.activateDelegate = this.activate.createDelegate(this);
38051
38052     this.addEvents({
38053         /**
38054          * @event tabchange
38055          * Fires when the active tab changes
38056          * @param {Roo.TabPanel} this
38057          * @param {Roo.TabPanelItem} activePanel The new active tab
38058          */
38059         "tabchange": true,
38060         /**
38061          * @event beforetabchange
38062          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38063          * @param {Roo.TabPanel} this
38064          * @param {Object} e Set cancel to true on this object to cancel the tab change
38065          * @param {Roo.TabPanelItem} tab The tab being changed to
38066          */
38067         "beforetabchange" : true
38068     });
38069
38070     Roo.EventManager.onWindowResize(this.onResize, this);
38071     this.cpad = this.el.getPadding("lr");
38072     this.hiddenCount = 0;
38073
38074
38075     // toolbar on the tabbar support...
38076     if (this.toolbar) {
38077         alert("no toolbar support yet");
38078         this.toolbar  = false;
38079         /*
38080         var tcfg = this.toolbar;
38081         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38082         this.toolbar = new Roo.Toolbar(tcfg);
38083         if (Roo.isSafari) {
38084             var tbl = tcfg.container.child('table', true);
38085             tbl.setAttribute('width', '100%');
38086         }
38087         */
38088         
38089     }
38090    
38091
38092
38093     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38094 };
38095
38096 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38097     /*
38098      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38099      */
38100     tabPosition : "top",
38101     /*
38102      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38103      */
38104     currentTabWidth : 0,
38105     /*
38106      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38107      */
38108     minTabWidth : 40,
38109     /*
38110      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38111      */
38112     maxTabWidth : 250,
38113     /*
38114      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38115      */
38116     preferredTabWidth : 175,
38117     /*
38118      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38119      */
38120     resizeTabs : false,
38121     /*
38122      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38123      */
38124     monitorResize : true,
38125     /*
38126      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38127      */
38128     toolbar : false,
38129
38130     /**
38131      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38132      * @param {String} id The id of the div to use <b>or create</b>
38133      * @param {String} text The text for the tab
38134      * @param {String} content (optional) Content to put in the TabPanelItem body
38135      * @param {Boolean} closable (optional) True to create a close icon on the tab
38136      * @return {Roo.TabPanelItem} The created TabPanelItem
38137      */
38138     addTab : function(id, text, content, closable, tpl)
38139     {
38140         var item = new Roo.bootstrap.panel.TabItem({
38141             panel: this,
38142             id : id,
38143             text : text,
38144             closable : closable,
38145             tpl : tpl
38146         });
38147         this.addTabItem(item);
38148         if(content){
38149             item.setContent(content);
38150         }
38151         return item;
38152     },
38153
38154     /**
38155      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38156      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38157      * @return {Roo.TabPanelItem}
38158      */
38159     getTab : function(id){
38160         return this.items[id];
38161     },
38162
38163     /**
38164      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38165      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38166      */
38167     hideTab : function(id){
38168         var t = this.items[id];
38169         if(!t.isHidden()){
38170            t.setHidden(true);
38171            this.hiddenCount++;
38172            this.autoSizeTabs();
38173         }
38174     },
38175
38176     /**
38177      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38178      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38179      */
38180     unhideTab : function(id){
38181         var t = this.items[id];
38182         if(t.isHidden()){
38183            t.setHidden(false);
38184            this.hiddenCount--;
38185            this.autoSizeTabs();
38186         }
38187     },
38188
38189     /**
38190      * Adds an existing {@link Roo.TabPanelItem}.
38191      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38192      */
38193     addTabItem : function(item)
38194     {
38195         this.items[item.id] = item;
38196         this.items.push(item);
38197         this.autoSizeTabs();
38198       //  if(this.resizeTabs){
38199     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38200   //         this.autoSizeTabs();
38201 //        }else{
38202 //            item.autoSize();
38203        // }
38204     },
38205
38206     /**
38207      * Removes a {@link Roo.TabPanelItem}.
38208      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38209      */
38210     removeTab : function(id){
38211         var items = this.items;
38212         var tab = items[id];
38213         if(!tab) { return; }
38214         var index = items.indexOf(tab);
38215         if(this.active == tab && items.length > 1){
38216             var newTab = this.getNextAvailable(index);
38217             if(newTab) {
38218                 newTab.activate();
38219             }
38220         }
38221         this.stripEl.dom.removeChild(tab.pnode.dom);
38222         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38223             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38224         }
38225         items.splice(index, 1);
38226         delete this.items[tab.id];
38227         tab.fireEvent("close", tab);
38228         tab.purgeListeners();
38229         this.autoSizeTabs();
38230     },
38231
38232     getNextAvailable : function(start){
38233         var items = this.items;
38234         var index = start;
38235         // look for a next tab that will slide over to
38236         // replace the one being removed
38237         while(index < items.length){
38238             var item = items[++index];
38239             if(item && !item.isHidden()){
38240                 return item;
38241             }
38242         }
38243         // if one isn't found select the previous tab (on the left)
38244         index = start;
38245         while(index >= 0){
38246             var item = items[--index];
38247             if(item && !item.isHidden()){
38248                 return item;
38249             }
38250         }
38251         return null;
38252     },
38253
38254     /**
38255      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38256      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38257      */
38258     disableTab : function(id){
38259         var tab = this.items[id];
38260         if(tab && this.active != tab){
38261             tab.disable();
38262         }
38263     },
38264
38265     /**
38266      * Enables a {@link Roo.TabPanelItem} that is disabled.
38267      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38268      */
38269     enableTab : function(id){
38270         var tab = this.items[id];
38271         tab.enable();
38272     },
38273
38274     /**
38275      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38276      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38277      * @return {Roo.TabPanelItem} The TabPanelItem.
38278      */
38279     activate : function(id)
38280     {
38281         var tab = this.items[id];
38282         if(!tab){
38283             return null;
38284         }
38285         if(tab == this.active || tab.disabled){
38286             return tab;
38287         }
38288         var e = {};
38289         this.fireEvent("beforetabchange", this, e, tab);
38290         if(e.cancel !== true && !tab.disabled){
38291             if(this.active){
38292                 this.active.hide();
38293             }
38294             this.active = this.items[id];
38295             this.active.show();
38296             this.fireEvent("tabchange", this, this.active);
38297         }
38298         return tab;
38299     },
38300
38301     /**
38302      * Gets the active {@link Roo.TabPanelItem}.
38303      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38304      */
38305     getActiveTab : function(){
38306         return this.active;
38307     },
38308
38309     /**
38310      * Updates the tab body element to fit the height of the container element
38311      * for overflow scrolling
38312      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38313      */
38314     syncHeight : function(targetHeight){
38315         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38316         var bm = this.bodyEl.getMargins();
38317         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38318         this.bodyEl.setHeight(newHeight);
38319         return newHeight;
38320     },
38321
38322     onResize : function(){
38323         if(this.monitorResize){
38324             this.autoSizeTabs();
38325         }
38326     },
38327
38328     /**
38329      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38330      */
38331     beginUpdate : function(){
38332         this.updating = true;
38333     },
38334
38335     /**
38336      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38337      */
38338     endUpdate : function(){
38339         this.updating = false;
38340         this.autoSizeTabs();
38341     },
38342
38343     /**
38344      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38345      */
38346     autoSizeTabs : function()
38347     {
38348         var count = this.items.length;
38349         var vcount = count - this.hiddenCount;
38350         
38351         if (vcount < 2) {
38352             this.stripEl.hide();
38353         } else {
38354             this.stripEl.show();
38355         }
38356         
38357         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38358             return;
38359         }
38360         
38361         
38362         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38363         var availWidth = Math.floor(w / vcount);
38364         var b = this.stripBody;
38365         if(b.getWidth() > w){
38366             var tabs = this.items;
38367             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38368             if(availWidth < this.minTabWidth){
38369                 /*if(!this.sleft){    // incomplete scrolling code
38370                     this.createScrollButtons();
38371                 }
38372                 this.showScroll();
38373                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38374             }
38375         }else{
38376             if(this.currentTabWidth < this.preferredTabWidth){
38377                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38378             }
38379         }
38380     },
38381
38382     /**
38383      * Returns the number of tabs in this TabPanel.
38384      * @return {Number}
38385      */
38386      getCount : function(){
38387          return this.items.length;
38388      },
38389
38390     /**
38391      * Resizes all the tabs to the passed width
38392      * @param {Number} The new width
38393      */
38394     setTabWidth : function(width){
38395         this.currentTabWidth = width;
38396         for(var i = 0, len = this.items.length; i < len; i++) {
38397                 if(!this.items[i].isHidden()) {
38398                 this.items[i].setWidth(width);
38399             }
38400         }
38401     },
38402
38403     /**
38404      * Destroys this TabPanel
38405      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38406      */
38407     destroy : function(removeEl){
38408         Roo.EventManager.removeResizeListener(this.onResize, this);
38409         for(var i = 0, len = this.items.length; i < len; i++){
38410             this.items[i].purgeListeners();
38411         }
38412         if(removeEl === true){
38413             this.el.update("");
38414             this.el.remove();
38415         }
38416     },
38417     
38418     createStrip : function(container)
38419     {
38420         var strip = document.createElement("nav");
38421         strip.className = Roo.bootstrap.version == 4 ?
38422             "navbar-light bg-light" : 
38423             "navbar navbar-default"; //"x-tabs-wrap";
38424         container.appendChild(strip);
38425         return strip;
38426     },
38427     
38428     createStripList : function(strip)
38429     {
38430         // div wrapper for retard IE
38431         // returns the "tr" element.
38432         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38433         //'<div class="x-tabs-strip-wrap">'+
38434           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38435           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38436         return strip.firstChild; //.firstChild.firstChild.firstChild;
38437     },
38438     createBody : function(container)
38439     {
38440         var body = document.createElement("div");
38441         Roo.id(body, "tab-body");
38442         //Roo.fly(body).addClass("x-tabs-body");
38443         Roo.fly(body).addClass("tab-content");
38444         container.appendChild(body);
38445         return body;
38446     },
38447     createItemBody :function(bodyEl, id){
38448         var body = Roo.getDom(id);
38449         if(!body){
38450             body = document.createElement("div");
38451             body.id = id;
38452         }
38453         //Roo.fly(body).addClass("x-tabs-item-body");
38454         Roo.fly(body).addClass("tab-pane");
38455          bodyEl.insertBefore(body, bodyEl.firstChild);
38456         return body;
38457     },
38458     /** @private */
38459     createStripElements :  function(stripEl, text, closable, tpl)
38460     {
38461         var td = document.createElement("li"); // was td..
38462         td.className = 'nav-item';
38463         
38464         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38465         
38466         
38467         stripEl.appendChild(td);
38468         /*if(closable){
38469             td.className = "x-tabs-closable";
38470             if(!this.closeTpl){
38471                 this.closeTpl = new Roo.Template(
38472                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38473                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38474                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38475                 );
38476             }
38477             var el = this.closeTpl.overwrite(td, {"text": text});
38478             var close = el.getElementsByTagName("div")[0];
38479             var inner = el.getElementsByTagName("em")[0];
38480             return {"el": el, "close": close, "inner": inner};
38481         } else {
38482         */
38483         // not sure what this is..
38484 //            if(!this.tabTpl){
38485                 //this.tabTpl = new Roo.Template(
38486                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38487                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38488                 //);
38489 //                this.tabTpl = new Roo.Template(
38490 //                   '<a href="#">' +
38491 //                   '<span unselectable="on"' +
38492 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38493 //                            ' >{text}</span></a>'
38494 //                );
38495 //                
38496 //            }
38497
38498
38499             var template = tpl || this.tabTpl || false;
38500             
38501             if(!template){
38502                 template =  new Roo.Template(
38503                         Roo.bootstrap.version == 4 ? 
38504                             (
38505                                 '<a class="nav-link" href="#" unselectable="on"' +
38506                                      (this.disableTooltips ? '' : ' title="{text}"') +
38507                                      ' >{text}</a>'
38508                             ) : (
38509                                 '<a class="nav-link" href="#">' +
38510                                 '<span unselectable="on"' +
38511                                          (this.disableTooltips ? '' : ' title="{text}"') +
38512                                     ' >{text}</span></a>'
38513                             )
38514                 );
38515             }
38516             
38517             switch (typeof(template)) {
38518                 case 'object' :
38519                     break;
38520                 case 'string' :
38521                     template = new Roo.Template(template);
38522                     break;
38523                 default :
38524                     break;
38525             }
38526             
38527             var el = template.overwrite(td, {"text": text});
38528             
38529             var inner = el.getElementsByTagName("span")[0];
38530             
38531             return {"el": el, "inner": inner};
38532             
38533     }
38534         
38535     
38536 });
38537
38538 /**
38539  * @class Roo.TabPanelItem
38540  * @extends Roo.util.Observable
38541  * Represents an individual item (tab plus body) in a TabPanel.
38542  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38543  * @param {String} id The id of this TabPanelItem
38544  * @param {String} text The text for the tab of this TabPanelItem
38545  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38546  */
38547 Roo.bootstrap.panel.TabItem = function(config){
38548     /**
38549      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38550      * @type Roo.TabPanel
38551      */
38552     this.tabPanel = config.panel;
38553     /**
38554      * The id for this TabPanelItem
38555      * @type String
38556      */
38557     this.id = config.id;
38558     /** @private */
38559     this.disabled = false;
38560     /** @private */
38561     this.text = config.text;
38562     /** @private */
38563     this.loaded = false;
38564     this.closable = config.closable;
38565
38566     /**
38567      * The body element for this TabPanelItem.
38568      * @type Roo.Element
38569      */
38570     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38571     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38572     this.bodyEl.setStyle("display", "block");
38573     this.bodyEl.setStyle("zoom", "1");
38574     //this.hideAction();
38575
38576     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38577     /** @private */
38578     this.el = Roo.get(els.el);
38579     this.inner = Roo.get(els.inner, true);
38580      this.textEl = Roo.bootstrap.version == 4 ?
38581         this.el : Roo.get(this.el.dom.firstChild, true);
38582
38583     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38584     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38585
38586     
38587 //    this.el.on("mousedown", this.onTabMouseDown, this);
38588     this.el.on("click", this.onTabClick, this);
38589     /** @private */
38590     if(config.closable){
38591         var c = Roo.get(els.close, true);
38592         c.dom.title = this.closeText;
38593         c.addClassOnOver("close-over");
38594         c.on("click", this.closeClick, this);
38595      }
38596
38597     this.addEvents({
38598          /**
38599          * @event activate
38600          * Fires when this tab becomes the active tab.
38601          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38602          * @param {Roo.TabPanelItem} this
38603          */
38604         "activate": true,
38605         /**
38606          * @event beforeclose
38607          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38608          * @param {Roo.TabPanelItem} this
38609          * @param {Object} e Set cancel to true on this object to cancel the close.
38610          */
38611         "beforeclose": true,
38612         /**
38613          * @event close
38614          * Fires when this tab is closed.
38615          * @param {Roo.TabPanelItem} this
38616          */
38617          "close": true,
38618         /**
38619          * @event deactivate
38620          * Fires when this tab is no longer the active tab.
38621          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38622          * @param {Roo.TabPanelItem} this
38623          */
38624          "deactivate" : true
38625     });
38626     this.hidden = false;
38627
38628     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38629 };
38630
38631 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38632            {
38633     purgeListeners : function(){
38634        Roo.util.Observable.prototype.purgeListeners.call(this);
38635        this.el.removeAllListeners();
38636     },
38637     /**
38638      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38639      */
38640     show : function(){
38641         this.status_node.addClass("active");
38642         this.showAction();
38643         if(Roo.isOpera){
38644             this.tabPanel.stripWrap.repaint();
38645         }
38646         this.fireEvent("activate", this.tabPanel, this);
38647     },
38648
38649     /**
38650      * Returns true if this tab is the active tab.
38651      * @return {Boolean}
38652      */
38653     isActive : function(){
38654         return this.tabPanel.getActiveTab() == this;
38655     },
38656
38657     /**
38658      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38659      */
38660     hide : function(){
38661         this.status_node.removeClass("active");
38662         this.hideAction();
38663         this.fireEvent("deactivate", this.tabPanel, this);
38664     },
38665
38666     hideAction : function(){
38667         this.bodyEl.hide();
38668         this.bodyEl.setStyle("position", "absolute");
38669         this.bodyEl.setLeft("-20000px");
38670         this.bodyEl.setTop("-20000px");
38671     },
38672
38673     showAction : function(){
38674         this.bodyEl.setStyle("position", "relative");
38675         this.bodyEl.setTop("");
38676         this.bodyEl.setLeft("");
38677         this.bodyEl.show();
38678     },
38679
38680     /**
38681      * Set the tooltip for the tab.
38682      * @param {String} tooltip The tab's tooltip
38683      */
38684     setTooltip : function(text){
38685         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38686             this.textEl.dom.qtip = text;
38687             this.textEl.dom.removeAttribute('title');
38688         }else{
38689             this.textEl.dom.title = text;
38690         }
38691     },
38692
38693     onTabClick : function(e){
38694         e.preventDefault();
38695         this.tabPanel.activate(this.id);
38696     },
38697
38698     onTabMouseDown : function(e){
38699         e.preventDefault();
38700         this.tabPanel.activate(this.id);
38701     },
38702 /*
38703     getWidth : function(){
38704         return this.inner.getWidth();
38705     },
38706
38707     setWidth : function(width){
38708         var iwidth = width - this.linode.getPadding("lr");
38709         this.inner.setWidth(iwidth);
38710         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38711         this.linode.setWidth(width);
38712     },
38713 */
38714     /**
38715      * Show or hide the tab
38716      * @param {Boolean} hidden True to hide or false to show.
38717      */
38718     setHidden : function(hidden){
38719         this.hidden = hidden;
38720         this.linode.setStyle("display", hidden ? "none" : "");
38721     },
38722
38723     /**
38724      * Returns true if this tab is "hidden"
38725      * @return {Boolean}
38726      */
38727     isHidden : function(){
38728         return this.hidden;
38729     },
38730
38731     /**
38732      * Returns the text for this tab
38733      * @return {String}
38734      */
38735     getText : function(){
38736         return this.text;
38737     },
38738     /*
38739     autoSize : function(){
38740         //this.el.beginMeasure();
38741         this.textEl.setWidth(1);
38742         /*
38743          *  #2804 [new] Tabs in Roojs
38744          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38745          */
38746         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38747         //this.el.endMeasure();
38748     //},
38749
38750     /**
38751      * Sets the text for the tab (Note: this also sets the tooltip text)
38752      * @param {String} text The tab's text and tooltip
38753      */
38754     setText : function(text){
38755         this.text = text;
38756         this.textEl.update(text);
38757         this.setTooltip(text);
38758         //if(!this.tabPanel.resizeTabs){
38759         //    this.autoSize();
38760         //}
38761     },
38762     /**
38763      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38764      */
38765     activate : function(){
38766         this.tabPanel.activate(this.id);
38767     },
38768
38769     /**
38770      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38771      */
38772     disable : function(){
38773         if(this.tabPanel.active != this){
38774             this.disabled = true;
38775             this.status_node.addClass("disabled");
38776         }
38777     },
38778
38779     /**
38780      * Enables this TabPanelItem if it was previously disabled.
38781      */
38782     enable : function(){
38783         this.disabled = false;
38784         this.status_node.removeClass("disabled");
38785     },
38786
38787     /**
38788      * Sets the content for this TabPanelItem.
38789      * @param {String} content The content
38790      * @param {Boolean} loadScripts true to look for and load scripts
38791      */
38792     setContent : function(content, loadScripts){
38793         this.bodyEl.update(content, loadScripts);
38794     },
38795
38796     /**
38797      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38798      * @return {Roo.UpdateManager} The UpdateManager
38799      */
38800     getUpdateManager : function(){
38801         return this.bodyEl.getUpdateManager();
38802     },
38803
38804     /**
38805      * Set a URL to be used to load the content for this TabPanelItem.
38806      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38807      * @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)
38808      * @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)
38809      * @return {Roo.UpdateManager} The UpdateManager
38810      */
38811     setUrl : function(url, params, loadOnce){
38812         if(this.refreshDelegate){
38813             this.un('activate', this.refreshDelegate);
38814         }
38815         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38816         this.on("activate", this.refreshDelegate);
38817         return this.bodyEl.getUpdateManager();
38818     },
38819
38820     /** @private */
38821     _handleRefresh : function(url, params, loadOnce){
38822         if(!loadOnce || !this.loaded){
38823             var updater = this.bodyEl.getUpdateManager();
38824             updater.update(url, params, this._setLoaded.createDelegate(this));
38825         }
38826     },
38827
38828     /**
38829      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38830      *   Will fail silently if the setUrl method has not been called.
38831      *   This does not activate the panel, just updates its content.
38832      */
38833     refresh : function(){
38834         if(this.refreshDelegate){
38835            this.loaded = false;
38836            this.refreshDelegate();
38837         }
38838     },
38839
38840     /** @private */
38841     _setLoaded : function(){
38842         this.loaded = true;
38843     },
38844
38845     /** @private */
38846     closeClick : function(e){
38847         var o = {};
38848         e.stopEvent();
38849         this.fireEvent("beforeclose", this, o);
38850         if(o.cancel !== true){
38851             this.tabPanel.removeTab(this.id);
38852         }
38853     },
38854     /**
38855      * The text displayed in the tooltip for the close icon.
38856      * @type String
38857      */
38858     closeText : "Close this tab"
38859 });
38860 /**
38861 *    This script refer to:
38862 *    Title: International Telephone Input
38863 *    Author: Jack O'Connor
38864 *    Code version:  v12.1.12
38865 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38866 **/
38867
38868 Roo.bootstrap.PhoneInputData = function() {
38869     var d = [
38870       [
38871         "Afghanistan (‫افغانستان‬‎)",
38872         "af",
38873         "93"
38874       ],
38875       [
38876         "Albania (Shqipëri)",
38877         "al",
38878         "355"
38879       ],
38880       [
38881         "Algeria (‫الجزائر‬‎)",
38882         "dz",
38883         "213"
38884       ],
38885       [
38886         "American Samoa",
38887         "as",
38888         "1684"
38889       ],
38890       [
38891         "Andorra",
38892         "ad",
38893         "376"
38894       ],
38895       [
38896         "Angola",
38897         "ao",
38898         "244"
38899       ],
38900       [
38901         "Anguilla",
38902         "ai",
38903         "1264"
38904       ],
38905       [
38906         "Antigua and Barbuda",
38907         "ag",
38908         "1268"
38909       ],
38910       [
38911         "Argentina",
38912         "ar",
38913         "54"
38914       ],
38915       [
38916         "Armenia (Հայաստան)",
38917         "am",
38918         "374"
38919       ],
38920       [
38921         "Aruba",
38922         "aw",
38923         "297"
38924       ],
38925       [
38926         "Australia",
38927         "au",
38928         "61",
38929         0
38930       ],
38931       [
38932         "Austria (Österreich)",
38933         "at",
38934         "43"
38935       ],
38936       [
38937         "Azerbaijan (Azərbaycan)",
38938         "az",
38939         "994"
38940       ],
38941       [
38942         "Bahamas",
38943         "bs",
38944         "1242"
38945       ],
38946       [
38947         "Bahrain (‫البحرين‬‎)",
38948         "bh",
38949         "973"
38950       ],
38951       [
38952         "Bangladesh (বাংলাদেশ)",
38953         "bd",
38954         "880"
38955       ],
38956       [
38957         "Barbados",
38958         "bb",
38959         "1246"
38960       ],
38961       [
38962         "Belarus (Беларусь)",
38963         "by",
38964         "375"
38965       ],
38966       [
38967         "Belgium (België)",
38968         "be",
38969         "32"
38970       ],
38971       [
38972         "Belize",
38973         "bz",
38974         "501"
38975       ],
38976       [
38977         "Benin (Bénin)",
38978         "bj",
38979         "229"
38980       ],
38981       [
38982         "Bermuda",
38983         "bm",
38984         "1441"
38985       ],
38986       [
38987         "Bhutan (འབྲུག)",
38988         "bt",
38989         "975"
38990       ],
38991       [
38992         "Bolivia",
38993         "bo",
38994         "591"
38995       ],
38996       [
38997         "Bosnia and Herzegovina (Босна и Херцеговина)",
38998         "ba",
38999         "387"
39000       ],
39001       [
39002         "Botswana",
39003         "bw",
39004         "267"
39005       ],
39006       [
39007         "Brazil (Brasil)",
39008         "br",
39009         "55"
39010       ],
39011       [
39012         "British Indian Ocean Territory",
39013         "io",
39014         "246"
39015       ],
39016       [
39017         "British Virgin Islands",
39018         "vg",
39019         "1284"
39020       ],
39021       [
39022         "Brunei",
39023         "bn",
39024         "673"
39025       ],
39026       [
39027         "Bulgaria (България)",
39028         "bg",
39029         "359"
39030       ],
39031       [
39032         "Burkina Faso",
39033         "bf",
39034         "226"
39035       ],
39036       [
39037         "Burundi (Uburundi)",
39038         "bi",
39039         "257"
39040       ],
39041       [
39042         "Cambodia (កម្ពុជា)",
39043         "kh",
39044         "855"
39045       ],
39046       [
39047         "Cameroon (Cameroun)",
39048         "cm",
39049         "237"
39050       ],
39051       [
39052         "Canada",
39053         "ca",
39054         "1",
39055         1,
39056         ["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"]
39057       ],
39058       [
39059         "Cape Verde (Kabu Verdi)",
39060         "cv",
39061         "238"
39062       ],
39063       [
39064         "Caribbean Netherlands",
39065         "bq",
39066         "599",
39067         1
39068       ],
39069       [
39070         "Cayman Islands",
39071         "ky",
39072         "1345"
39073       ],
39074       [
39075         "Central African Republic (République centrafricaine)",
39076         "cf",
39077         "236"
39078       ],
39079       [
39080         "Chad (Tchad)",
39081         "td",
39082         "235"
39083       ],
39084       [
39085         "Chile",
39086         "cl",
39087         "56"
39088       ],
39089       [
39090         "China (中国)",
39091         "cn",
39092         "86"
39093       ],
39094       [
39095         "Christmas Island",
39096         "cx",
39097         "61",
39098         2
39099       ],
39100       [
39101         "Cocos (Keeling) Islands",
39102         "cc",
39103         "61",
39104         1
39105       ],
39106       [
39107         "Colombia",
39108         "co",
39109         "57"
39110       ],
39111       [
39112         "Comoros (‫جزر القمر‬‎)",
39113         "km",
39114         "269"
39115       ],
39116       [
39117         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39118         "cd",
39119         "243"
39120       ],
39121       [
39122         "Congo (Republic) (Congo-Brazzaville)",
39123         "cg",
39124         "242"
39125       ],
39126       [
39127         "Cook Islands",
39128         "ck",
39129         "682"
39130       ],
39131       [
39132         "Costa Rica",
39133         "cr",
39134         "506"
39135       ],
39136       [
39137         "Côte d’Ivoire",
39138         "ci",
39139         "225"
39140       ],
39141       [
39142         "Croatia (Hrvatska)",
39143         "hr",
39144         "385"
39145       ],
39146       [
39147         "Cuba",
39148         "cu",
39149         "53"
39150       ],
39151       [
39152         "Curaçao",
39153         "cw",
39154         "599",
39155         0
39156       ],
39157       [
39158         "Cyprus (Κύπρος)",
39159         "cy",
39160         "357"
39161       ],
39162       [
39163         "Czech Republic (Česká republika)",
39164         "cz",
39165         "420"
39166       ],
39167       [
39168         "Denmark (Danmark)",
39169         "dk",
39170         "45"
39171       ],
39172       [
39173         "Djibouti",
39174         "dj",
39175         "253"
39176       ],
39177       [
39178         "Dominica",
39179         "dm",
39180         "1767"
39181       ],
39182       [
39183         "Dominican Republic (República Dominicana)",
39184         "do",
39185         "1",
39186         2,
39187         ["809", "829", "849"]
39188       ],
39189       [
39190         "Ecuador",
39191         "ec",
39192         "593"
39193       ],
39194       [
39195         "Egypt (‫مصر‬‎)",
39196         "eg",
39197         "20"
39198       ],
39199       [
39200         "El Salvador",
39201         "sv",
39202         "503"
39203       ],
39204       [
39205         "Equatorial Guinea (Guinea Ecuatorial)",
39206         "gq",
39207         "240"
39208       ],
39209       [
39210         "Eritrea",
39211         "er",
39212         "291"
39213       ],
39214       [
39215         "Estonia (Eesti)",
39216         "ee",
39217         "372"
39218       ],
39219       [
39220         "Ethiopia",
39221         "et",
39222         "251"
39223       ],
39224       [
39225         "Falkland Islands (Islas Malvinas)",
39226         "fk",
39227         "500"
39228       ],
39229       [
39230         "Faroe Islands (Føroyar)",
39231         "fo",
39232         "298"
39233       ],
39234       [
39235         "Fiji",
39236         "fj",
39237         "679"
39238       ],
39239       [
39240         "Finland (Suomi)",
39241         "fi",
39242         "358",
39243         0
39244       ],
39245       [
39246         "France",
39247         "fr",
39248         "33"
39249       ],
39250       [
39251         "French Guiana (Guyane française)",
39252         "gf",
39253         "594"
39254       ],
39255       [
39256         "French Polynesia (Polynésie française)",
39257         "pf",
39258         "689"
39259       ],
39260       [
39261         "Gabon",
39262         "ga",
39263         "241"
39264       ],
39265       [
39266         "Gambia",
39267         "gm",
39268         "220"
39269       ],
39270       [
39271         "Georgia (საქართველო)",
39272         "ge",
39273         "995"
39274       ],
39275       [
39276         "Germany (Deutschland)",
39277         "de",
39278         "49"
39279       ],
39280       [
39281         "Ghana (Gaana)",
39282         "gh",
39283         "233"
39284       ],
39285       [
39286         "Gibraltar",
39287         "gi",
39288         "350"
39289       ],
39290       [
39291         "Greece (Ελλάδα)",
39292         "gr",
39293         "30"
39294       ],
39295       [
39296         "Greenland (Kalaallit Nunaat)",
39297         "gl",
39298         "299"
39299       ],
39300       [
39301         "Grenada",
39302         "gd",
39303         "1473"
39304       ],
39305       [
39306         "Guadeloupe",
39307         "gp",
39308         "590",
39309         0
39310       ],
39311       [
39312         "Guam",
39313         "gu",
39314         "1671"
39315       ],
39316       [
39317         "Guatemala",
39318         "gt",
39319         "502"
39320       ],
39321       [
39322         "Guernsey",
39323         "gg",
39324         "44",
39325         1
39326       ],
39327       [
39328         "Guinea (Guinée)",
39329         "gn",
39330         "224"
39331       ],
39332       [
39333         "Guinea-Bissau (Guiné Bissau)",
39334         "gw",
39335         "245"
39336       ],
39337       [
39338         "Guyana",
39339         "gy",
39340         "592"
39341       ],
39342       [
39343         "Haiti",
39344         "ht",
39345         "509"
39346       ],
39347       [
39348         "Honduras",
39349         "hn",
39350         "504"
39351       ],
39352       [
39353         "Hong Kong (香港)",
39354         "hk",
39355         "852"
39356       ],
39357       [
39358         "Hungary (Magyarország)",
39359         "hu",
39360         "36"
39361       ],
39362       [
39363         "Iceland (Ísland)",
39364         "is",
39365         "354"
39366       ],
39367       [
39368         "India (भारत)",
39369         "in",
39370         "91"
39371       ],
39372       [
39373         "Indonesia",
39374         "id",
39375         "62"
39376       ],
39377       [
39378         "Iran (‫ایران‬‎)",
39379         "ir",
39380         "98"
39381       ],
39382       [
39383         "Iraq (‫العراق‬‎)",
39384         "iq",
39385         "964"
39386       ],
39387       [
39388         "Ireland",
39389         "ie",
39390         "353"
39391       ],
39392       [
39393         "Isle of Man",
39394         "im",
39395         "44",
39396         2
39397       ],
39398       [
39399         "Israel (‫ישראל‬‎)",
39400         "il",
39401         "972"
39402       ],
39403       [
39404         "Italy (Italia)",
39405         "it",
39406         "39",
39407         0
39408       ],
39409       [
39410         "Jamaica",
39411         "jm",
39412         "1876"
39413       ],
39414       [
39415         "Japan (日本)",
39416         "jp",
39417         "81"
39418       ],
39419       [
39420         "Jersey",
39421         "je",
39422         "44",
39423         3
39424       ],
39425       [
39426         "Jordan (‫الأردن‬‎)",
39427         "jo",
39428         "962"
39429       ],
39430       [
39431         "Kazakhstan (Казахстан)",
39432         "kz",
39433         "7",
39434         1
39435       ],
39436       [
39437         "Kenya",
39438         "ke",
39439         "254"
39440       ],
39441       [
39442         "Kiribati",
39443         "ki",
39444         "686"
39445       ],
39446       [
39447         "Kosovo",
39448         "xk",
39449         "383"
39450       ],
39451       [
39452         "Kuwait (‫الكويت‬‎)",
39453         "kw",
39454         "965"
39455       ],
39456       [
39457         "Kyrgyzstan (Кыргызстан)",
39458         "kg",
39459         "996"
39460       ],
39461       [
39462         "Laos (ລາວ)",
39463         "la",
39464         "856"
39465       ],
39466       [
39467         "Latvia (Latvija)",
39468         "lv",
39469         "371"
39470       ],
39471       [
39472         "Lebanon (‫لبنان‬‎)",
39473         "lb",
39474         "961"
39475       ],
39476       [
39477         "Lesotho",
39478         "ls",
39479         "266"
39480       ],
39481       [
39482         "Liberia",
39483         "lr",
39484         "231"
39485       ],
39486       [
39487         "Libya (‫ليبيا‬‎)",
39488         "ly",
39489         "218"
39490       ],
39491       [
39492         "Liechtenstein",
39493         "li",
39494         "423"
39495       ],
39496       [
39497         "Lithuania (Lietuva)",
39498         "lt",
39499         "370"
39500       ],
39501       [
39502         "Luxembourg",
39503         "lu",
39504         "352"
39505       ],
39506       [
39507         "Macau (澳門)",
39508         "mo",
39509         "853"
39510       ],
39511       [
39512         "Macedonia (FYROM) (Македонија)",
39513         "mk",
39514         "389"
39515       ],
39516       [
39517         "Madagascar (Madagasikara)",
39518         "mg",
39519         "261"
39520       ],
39521       [
39522         "Malawi",
39523         "mw",
39524         "265"
39525       ],
39526       [
39527         "Malaysia",
39528         "my",
39529         "60"
39530       ],
39531       [
39532         "Maldives",
39533         "mv",
39534         "960"
39535       ],
39536       [
39537         "Mali",
39538         "ml",
39539         "223"
39540       ],
39541       [
39542         "Malta",
39543         "mt",
39544         "356"
39545       ],
39546       [
39547         "Marshall Islands",
39548         "mh",
39549         "692"
39550       ],
39551       [
39552         "Martinique",
39553         "mq",
39554         "596"
39555       ],
39556       [
39557         "Mauritania (‫موريتانيا‬‎)",
39558         "mr",
39559         "222"
39560       ],
39561       [
39562         "Mauritius (Moris)",
39563         "mu",
39564         "230"
39565       ],
39566       [
39567         "Mayotte",
39568         "yt",
39569         "262",
39570         1
39571       ],
39572       [
39573         "Mexico (México)",
39574         "mx",
39575         "52"
39576       ],
39577       [
39578         "Micronesia",
39579         "fm",
39580         "691"
39581       ],
39582       [
39583         "Moldova (Republica Moldova)",
39584         "md",
39585         "373"
39586       ],
39587       [
39588         "Monaco",
39589         "mc",
39590         "377"
39591       ],
39592       [
39593         "Mongolia (Монгол)",
39594         "mn",
39595         "976"
39596       ],
39597       [
39598         "Montenegro (Crna Gora)",
39599         "me",
39600         "382"
39601       ],
39602       [
39603         "Montserrat",
39604         "ms",
39605         "1664"
39606       ],
39607       [
39608         "Morocco (‫المغرب‬‎)",
39609         "ma",
39610         "212",
39611         0
39612       ],
39613       [
39614         "Mozambique (Moçambique)",
39615         "mz",
39616         "258"
39617       ],
39618       [
39619         "Myanmar (Burma) (မြန်မာ)",
39620         "mm",
39621         "95"
39622       ],
39623       [
39624         "Namibia (Namibië)",
39625         "na",
39626         "264"
39627       ],
39628       [
39629         "Nauru",
39630         "nr",
39631         "674"
39632       ],
39633       [
39634         "Nepal (नेपाल)",
39635         "np",
39636         "977"
39637       ],
39638       [
39639         "Netherlands (Nederland)",
39640         "nl",
39641         "31"
39642       ],
39643       [
39644         "New Caledonia (Nouvelle-Calédonie)",
39645         "nc",
39646         "687"
39647       ],
39648       [
39649         "New Zealand",
39650         "nz",
39651         "64"
39652       ],
39653       [
39654         "Nicaragua",
39655         "ni",
39656         "505"
39657       ],
39658       [
39659         "Niger (Nijar)",
39660         "ne",
39661         "227"
39662       ],
39663       [
39664         "Nigeria",
39665         "ng",
39666         "234"
39667       ],
39668       [
39669         "Niue",
39670         "nu",
39671         "683"
39672       ],
39673       [
39674         "Norfolk Island",
39675         "nf",
39676         "672"
39677       ],
39678       [
39679         "North Korea (조선 민주주의 인민 공화국)",
39680         "kp",
39681         "850"
39682       ],
39683       [
39684         "Northern Mariana Islands",
39685         "mp",
39686         "1670"
39687       ],
39688       [
39689         "Norway (Norge)",
39690         "no",
39691         "47",
39692         0
39693       ],
39694       [
39695         "Oman (‫عُمان‬‎)",
39696         "om",
39697         "968"
39698       ],
39699       [
39700         "Pakistan (‫پاکستان‬‎)",
39701         "pk",
39702         "92"
39703       ],
39704       [
39705         "Palau",
39706         "pw",
39707         "680"
39708       ],
39709       [
39710         "Palestine (‫فلسطين‬‎)",
39711         "ps",
39712         "970"
39713       ],
39714       [
39715         "Panama (Panamá)",
39716         "pa",
39717         "507"
39718       ],
39719       [
39720         "Papua New Guinea",
39721         "pg",
39722         "675"
39723       ],
39724       [
39725         "Paraguay",
39726         "py",
39727         "595"
39728       ],
39729       [
39730         "Peru (Perú)",
39731         "pe",
39732         "51"
39733       ],
39734       [
39735         "Philippines",
39736         "ph",
39737         "63"
39738       ],
39739       [
39740         "Poland (Polska)",
39741         "pl",
39742         "48"
39743       ],
39744       [
39745         "Portugal",
39746         "pt",
39747         "351"
39748       ],
39749       [
39750         "Puerto Rico",
39751         "pr",
39752         "1",
39753         3,
39754         ["787", "939"]
39755       ],
39756       [
39757         "Qatar (‫قطر‬‎)",
39758         "qa",
39759         "974"
39760       ],
39761       [
39762         "Réunion (La Réunion)",
39763         "re",
39764         "262",
39765         0
39766       ],
39767       [
39768         "Romania (România)",
39769         "ro",
39770         "40"
39771       ],
39772       [
39773         "Russia (Россия)",
39774         "ru",
39775         "7",
39776         0
39777       ],
39778       [
39779         "Rwanda",
39780         "rw",
39781         "250"
39782       ],
39783       [
39784         "Saint Barthélemy",
39785         "bl",
39786         "590",
39787         1
39788       ],
39789       [
39790         "Saint Helena",
39791         "sh",
39792         "290"
39793       ],
39794       [
39795         "Saint Kitts and Nevis",
39796         "kn",
39797         "1869"
39798       ],
39799       [
39800         "Saint Lucia",
39801         "lc",
39802         "1758"
39803       ],
39804       [
39805         "Saint Martin (Saint-Martin (partie française))",
39806         "mf",
39807         "590",
39808         2
39809       ],
39810       [
39811         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39812         "pm",
39813         "508"
39814       ],
39815       [
39816         "Saint Vincent and the Grenadines",
39817         "vc",
39818         "1784"
39819       ],
39820       [
39821         "Samoa",
39822         "ws",
39823         "685"
39824       ],
39825       [
39826         "San Marino",
39827         "sm",
39828         "378"
39829       ],
39830       [
39831         "São Tomé and Príncipe (São Tomé e Príncipe)",
39832         "st",
39833         "239"
39834       ],
39835       [
39836         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39837         "sa",
39838         "966"
39839       ],
39840       [
39841         "Senegal (Sénégal)",
39842         "sn",
39843         "221"
39844       ],
39845       [
39846         "Serbia (Србија)",
39847         "rs",
39848         "381"
39849       ],
39850       [
39851         "Seychelles",
39852         "sc",
39853         "248"
39854       ],
39855       [
39856         "Sierra Leone",
39857         "sl",
39858         "232"
39859       ],
39860       [
39861         "Singapore",
39862         "sg",
39863         "65"
39864       ],
39865       [
39866         "Sint Maarten",
39867         "sx",
39868         "1721"
39869       ],
39870       [
39871         "Slovakia (Slovensko)",
39872         "sk",
39873         "421"
39874       ],
39875       [
39876         "Slovenia (Slovenija)",
39877         "si",
39878         "386"
39879       ],
39880       [
39881         "Solomon Islands",
39882         "sb",
39883         "677"
39884       ],
39885       [
39886         "Somalia (Soomaaliya)",
39887         "so",
39888         "252"
39889       ],
39890       [
39891         "South Africa",
39892         "za",
39893         "27"
39894       ],
39895       [
39896         "South Korea (대한민국)",
39897         "kr",
39898         "82"
39899       ],
39900       [
39901         "South Sudan (‫جنوب السودان‬‎)",
39902         "ss",
39903         "211"
39904       ],
39905       [
39906         "Spain (España)",
39907         "es",
39908         "34"
39909       ],
39910       [
39911         "Sri Lanka (ශ්‍රී ලංකාව)",
39912         "lk",
39913         "94"
39914       ],
39915       [
39916         "Sudan (‫السودان‬‎)",
39917         "sd",
39918         "249"
39919       ],
39920       [
39921         "Suriname",
39922         "sr",
39923         "597"
39924       ],
39925       [
39926         "Svalbard and Jan Mayen",
39927         "sj",
39928         "47",
39929         1
39930       ],
39931       [
39932         "Swaziland",
39933         "sz",
39934         "268"
39935       ],
39936       [
39937         "Sweden (Sverige)",
39938         "se",
39939         "46"
39940       ],
39941       [
39942         "Switzerland (Schweiz)",
39943         "ch",
39944         "41"
39945       ],
39946       [
39947         "Syria (‫سوريا‬‎)",
39948         "sy",
39949         "963"
39950       ],
39951       [
39952         "Taiwan (台灣)",
39953         "tw",
39954         "886"
39955       ],
39956       [
39957         "Tajikistan",
39958         "tj",
39959         "992"
39960       ],
39961       [
39962         "Tanzania",
39963         "tz",
39964         "255"
39965       ],
39966       [
39967         "Thailand (ไทย)",
39968         "th",
39969         "66"
39970       ],
39971       [
39972         "Timor-Leste",
39973         "tl",
39974         "670"
39975       ],
39976       [
39977         "Togo",
39978         "tg",
39979         "228"
39980       ],
39981       [
39982         "Tokelau",
39983         "tk",
39984         "690"
39985       ],
39986       [
39987         "Tonga",
39988         "to",
39989         "676"
39990       ],
39991       [
39992         "Trinidad and Tobago",
39993         "tt",
39994         "1868"
39995       ],
39996       [
39997         "Tunisia (‫تونس‬‎)",
39998         "tn",
39999         "216"
40000       ],
40001       [
40002         "Turkey (Türkiye)",
40003         "tr",
40004         "90"
40005       ],
40006       [
40007         "Turkmenistan",
40008         "tm",
40009         "993"
40010       ],
40011       [
40012         "Turks and Caicos Islands",
40013         "tc",
40014         "1649"
40015       ],
40016       [
40017         "Tuvalu",
40018         "tv",
40019         "688"
40020       ],
40021       [
40022         "U.S. Virgin Islands",
40023         "vi",
40024         "1340"
40025       ],
40026       [
40027         "Uganda",
40028         "ug",
40029         "256"
40030       ],
40031       [
40032         "Ukraine (Україна)",
40033         "ua",
40034         "380"
40035       ],
40036       [
40037         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40038         "ae",
40039         "971"
40040       ],
40041       [
40042         "United Kingdom",
40043         "gb",
40044         "44",
40045         0
40046       ],
40047       [
40048         "United States",
40049         "us",
40050         "1",
40051         0
40052       ],
40053       [
40054         "Uruguay",
40055         "uy",
40056         "598"
40057       ],
40058       [
40059         "Uzbekistan (Oʻzbekiston)",
40060         "uz",
40061         "998"
40062       ],
40063       [
40064         "Vanuatu",
40065         "vu",
40066         "678"
40067       ],
40068       [
40069         "Vatican City (Città del Vaticano)",
40070         "va",
40071         "39",
40072         1
40073       ],
40074       [
40075         "Venezuela",
40076         "ve",
40077         "58"
40078       ],
40079       [
40080         "Vietnam (Việt Nam)",
40081         "vn",
40082         "84"
40083       ],
40084       [
40085         "Wallis and Futuna (Wallis-et-Futuna)",
40086         "wf",
40087         "681"
40088       ],
40089       [
40090         "Western Sahara (‫الصحراء الغربية‬‎)",
40091         "eh",
40092         "212",
40093         1
40094       ],
40095       [
40096         "Yemen (‫اليمن‬‎)",
40097         "ye",
40098         "967"
40099       ],
40100       [
40101         "Zambia",
40102         "zm",
40103         "260"
40104       ],
40105       [
40106         "Zimbabwe",
40107         "zw",
40108         "263"
40109       ],
40110       [
40111         "Åland Islands",
40112         "ax",
40113         "358",
40114         1
40115       ]
40116   ];
40117   
40118   return d;
40119 }/**
40120 *    This script refer to:
40121 *    Title: International Telephone Input
40122 *    Author: Jack O'Connor
40123 *    Code version:  v12.1.12
40124 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40125 **/
40126
40127 /**
40128  * @class Roo.bootstrap.PhoneInput
40129  * @extends Roo.bootstrap.TriggerField
40130  * An input with International dial-code selection
40131  
40132  * @cfg {String} defaultDialCode default '+852'
40133  * @cfg {Array} preferedCountries default []
40134   
40135  * @constructor
40136  * Create a new PhoneInput.
40137  * @param {Object} config Configuration options
40138  */
40139
40140 Roo.bootstrap.PhoneInput = function(config) {
40141     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40142 };
40143
40144 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40145         
40146         listWidth: undefined,
40147         
40148         selectedClass: 'active',
40149         
40150         invalidClass : "has-warning",
40151         
40152         validClass: 'has-success',
40153         
40154         allowed: '0123456789',
40155         
40156         max_length: 15,
40157         
40158         /**
40159          * @cfg {String} defaultDialCode The default dial code when initializing the input
40160          */
40161         defaultDialCode: '+852',
40162         
40163         /**
40164          * @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
40165          */
40166         preferedCountries: false,
40167         
40168         getAutoCreate : function()
40169         {
40170             var data = Roo.bootstrap.PhoneInputData();
40171             var align = this.labelAlign || this.parentLabelAlign();
40172             var id = Roo.id();
40173             
40174             this.allCountries = [];
40175             this.dialCodeMapping = [];
40176             
40177             for (var i = 0; i < data.length; i++) {
40178               var c = data[i];
40179               this.allCountries[i] = {
40180                 name: c[0],
40181                 iso2: c[1],
40182                 dialCode: c[2],
40183                 priority: c[3] || 0,
40184                 areaCodes: c[4] || null
40185               };
40186               this.dialCodeMapping[c[2]] = {
40187                   name: c[0],
40188                   iso2: c[1],
40189                   priority: c[3] || 0,
40190                   areaCodes: c[4] || null
40191               };
40192             }
40193             
40194             var cfg = {
40195                 cls: 'form-group',
40196                 cn: []
40197             };
40198             
40199             var input =  {
40200                 tag: 'input',
40201                 id : id,
40202                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40203                 maxlength: this.max_length,
40204                 cls : 'form-control tel-input',
40205                 autocomplete: 'new-password'
40206             };
40207             
40208             var hiddenInput = {
40209                 tag: 'input',
40210                 type: 'hidden',
40211                 cls: 'hidden-tel-input'
40212             };
40213             
40214             if (this.name) {
40215                 hiddenInput.name = this.name;
40216             }
40217             
40218             if (this.disabled) {
40219                 input.disabled = true;
40220             }
40221             
40222             var flag_container = {
40223                 tag: 'div',
40224                 cls: 'flag-box',
40225                 cn: [
40226                     {
40227                         tag: 'div',
40228                         cls: 'flag'
40229                     },
40230                     {
40231                         tag: 'div',
40232                         cls: 'caret'
40233                     }
40234                 ]
40235             };
40236             
40237             var box = {
40238                 tag: 'div',
40239                 cls: this.hasFeedback ? 'has-feedback' : '',
40240                 cn: [
40241                     hiddenInput,
40242                     input,
40243                     {
40244                         tag: 'input',
40245                         cls: 'dial-code-holder',
40246                         disabled: true
40247                     }
40248                 ]
40249             };
40250             
40251             var container = {
40252                 cls: 'roo-select2-container input-group',
40253                 cn: [
40254                     flag_container,
40255                     box
40256                 ]
40257             };
40258             
40259             if (this.fieldLabel.length) {
40260                 var indicator = {
40261                     tag: 'i',
40262                     tooltip: 'This field is required'
40263                 };
40264                 
40265                 var label = {
40266                     tag: 'label',
40267                     'for':  id,
40268                     cls: 'control-label',
40269                     cn: []
40270                 };
40271                 
40272                 var label_text = {
40273                     tag: 'span',
40274                     html: this.fieldLabel
40275                 };
40276                 
40277                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40278                 label.cn = [
40279                     indicator,
40280                     label_text
40281                 ];
40282                 
40283                 if(this.indicatorpos == 'right') {
40284                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40285                     label.cn = [
40286                         label_text,
40287                         indicator
40288                     ];
40289                 }
40290                 
40291                 if(align == 'left') {
40292                     container = {
40293                         tag: 'div',
40294                         cn: [
40295                             container
40296                         ]
40297                     };
40298                     
40299                     if(this.labelWidth > 12){
40300                         label.style = "width: " + this.labelWidth + 'px';
40301                     }
40302                     if(this.labelWidth < 13 && this.labelmd == 0){
40303                         this.labelmd = this.labelWidth;
40304                     }
40305                     if(this.labellg > 0){
40306                         label.cls += ' col-lg-' + this.labellg;
40307                         input.cls += ' col-lg-' + (12 - this.labellg);
40308                     }
40309                     if(this.labelmd > 0){
40310                         label.cls += ' col-md-' + this.labelmd;
40311                         container.cls += ' col-md-' + (12 - this.labelmd);
40312                     }
40313                     if(this.labelsm > 0){
40314                         label.cls += ' col-sm-' + this.labelsm;
40315                         container.cls += ' col-sm-' + (12 - this.labelsm);
40316                     }
40317                     if(this.labelxs > 0){
40318                         label.cls += ' col-xs-' + this.labelxs;
40319                         container.cls += ' col-xs-' + (12 - this.labelxs);
40320                     }
40321                 }
40322             }
40323             
40324             cfg.cn = [
40325                 label,
40326                 container
40327             ];
40328             
40329             var settings = this;
40330             
40331             ['xs','sm','md','lg'].map(function(size){
40332                 if (settings[size]) {
40333                     cfg.cls += ' col-' + size + '-' + settings[size];
40334                 }
40335             });
40336             
40337             this.store = new Roo.data.Store({
40338                 proxy : new Roo.data.MemoryProxy({}),
40339                 reader : new Roo.data.JsonReader({
40340                     fields : [
40341                         {
40342                             'name' : 'name',
40343                             'type' : 'string'
40344                         },
40345                         {
40346                             'name' : 'iso2',
40347                             'type' : 'string'
40348                         },
40349                         {
40350                             'name' : 'dialCode',
40351                             'type' : 'string'
40352                         },
40353                         {
40354                             'name' : 'priority',
40355                             'type' : 'string'
40356                         },
40357                         {
40358                             'name' : 'areaCodes',
40359                             'type' : 'string'
40360                         }
40361                     ]
40362                 })
40363             });
40364             
40365             if(!this.preferedCountries) {
40366                 this.preferedCountries = [
40367                     'hk',
40368                     'gb',
40369                     'us'
40370                 ];
40371             }
40372             
40373             var p = this.preferedCountries.reverse();
40374             
40375             if(p) {
40376                 for (var i = 0; i < p.length; i++) {
40377                     for (var j = 0; j < this.allCountries.length; j++) {
40378                         if(this.allCountries[j].iso2 == p[i]) {
40379                             var t = this.allCountries[j];
40380                             this.allCountries.splice(j,1);
40381                             this.allCountries.unshift(t);
40382                         }
40383                     } 
40384                 }
40385             }
40386             
40387             this.store.proxy.data = {
40388                 success: true,
40389                 data: this.allCountries
40390             };
40391             
40392             return cfg;
40393         },
40394         
40395         initEvents : function()
40396         {
40397             this.createList();
40398             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40399             
40400             this.indicator = this.indicatorEl();
40401             this.flag = this.flagEl();
40402             this.dialCodeHolder = this.dialCodeHolderEl();
40403             
40404             this.trigger = this.el.select('div.flag-box',true).first();
40405             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40406             
40407             var _this = this;
40408             
40409             (function(){
40410                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40411                 _this.list.setWidth(lw);
40412             }).defer(100);
40413             
40414             this.list.on('mouseover', this.onViewOver, this);
40415             this.list.on('mousemove', this.onViewMove, this);
40416             this.inputEl().on("keyup", this.onKeyUp, this);
40417             this.inputEl().on("keypress", this.onKeyPress, this);
40418             
40419             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40420
40421             this.view = new Roo.View(this.list, this.tpl, {
40422                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40423             });
40424             
40425             this.view.on('click', this.onViewClick, this);
40426             this.setValue(this.defaultDialCode);
40427         },
40428         
40429         onTriggerClick : function(e)
40430         {
40431             Roo.log('trigger click');
40432             if(this.disabled){
40433                 return;
40434             }
40435             
40436             if(this.isExpanded()){
40437                 this.collapse();
40438                 this.hasFocus = false;
40439             }else {
40440                 this.store.load({});
40441                 this.hasFocus = true;
40442                 this.expand();
40443             }
40444         },
40445         
40446         isExpanded : function()
40447         {
40448             return this.list.isVisible();
40449         },
40450         
40451         collapse : function()
40452         {
40453             if(!this.isExpanded()){
40454                 return;
40455             }
40456             this.list.hide();
40457             Roo.get(document).un('mousedown', this.collapseIf, this);
40458             Roo.get(document).un('mousewheel', this.collapseIf, this);
40459             this.fireEvent('collapse', this);
40460             this.validate();
40461         },
40462         
40463         expand : function()
40464         {
40465             Roo.log('expand');
40466
40467             if(this.isExpanded() || !this.hasFocus){
40468                 return;
40469             }
40470             
40471             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40472             this.list.setWidth(lw);
40473             
40474             this.list.show();
40475             this.restrictHeight();
40476             
40477             Roo.get(document).on('mousedown', this.collapseIf, this);
40478             Roo.get(document).on('mousewheel', this.collapseIf, this);
40479             
40480             this.fireEvent('expand', this);
40481         },
40482         
40483         restrictHeight : function()
40484         {
40485             this.list.alignTo(this.inputEl(), this.listAlign);
40486             this.list.alignTo(this.inputEl(), this.listAlign);
40487         },
40488         
40489         onViewOver : function(e, t)
40490         {
40491             if(this.inKeyMode){
40492                 return;
40493             }
40494             var item = this.view.findItemFromChild(t);
40495             
40496             if(item){
40497                 var index = this.view.indexOf(item);
40498                 this.select(index, false);
40499             }
40500         },
40501
40502         // private
40503         onViewClick : function(view, doFocus, el, e)
40504         {
40505             var index = this.view.getSelectedIndexes()[0];
40506             
40507             var r = this.store.getAt(index);
40508             
40509             if(r){
40510                 this.onSelect(r, index);
40511             }
40512             if(doFocus !== false && !this.blockFocus){
40513                 this.inputEl().focus();
40514             }
40515         },
40516         
40517         onViewMove : function(e, t)
40518         {
40519             this.inKeyMode = false;
40520         },
40521         
40522         select : function(index, scrollIntoView)
40523         {
40524             this.selectedIndex = index;
40525             this.view.select(index);
40526             if(scrollIntoView !== false){
40527                 var el = this.view.getNode(index);
40528                 if(el){
40529                     this.list.scrollChildIntoView(el, false);
40530                 }
40531             }
40532         },
40533         
40534         createList : function()
40535         {
40536             this.list = Roo.get(document.body).createChild({
40537                 tag: 'ul',
40538                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40539                 style: 'display:none'
40540             });
40541             
40542             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40543         },
40544         
40545         collapseIf : function(e)
40546         {
40547             var in_combo  = e.within(this.el);
40548             var in_list =  e.within(this.list);
40549             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40550             
40551             if (in_combo || in_list || is_list) {
40552                 return;
40553             }
40554             this.collapse();
40555         },
40556         
40557         onSelect : function(record, index)
40558         {
40559             if(this.fireEvent('beforeselect', this, record, index) !== false){
40560                 
40561                 this.setFlagClass(record.data.iso2);
40562                 this.setDialCode(record.data.dialCode);
40563                 this.hasFocus = false;
40564                 this.collapse();
40565                 this.fireEvent('select', this, record, index);
40566             }
40567         },
40568         
40569         flagEl : function()
40570         {
40571             var flag = this.el.select('div.flag',true).first();
40572             if(!flag){
40573                 return false;
40574             }
40575             return flag;
40576         },
40577         
40578         dialCodeHolderEl : function()
40579         {
40580             var d = this.el.select('input.dial-code-holder',true).first();
40581             if(!d){
40582                 return false;
40583             }
40584             return d;
40585         },
40586         
40587         setDialCode : function(v)
40588         {
40589             this.dialCodeHolder.dom.value = '+'+v;
40590         },
40591         
40592         setFlagClass : function(n)
40593         {
40594             this.flag.dom.className = 'flag '+n;
40595         },
40596         
40597         getValue : function()
40598         {
40599             var v = this.inputEl().getValue();
40600             if(this.dialCodeHolder) {
40601                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40602             }
40603             return v;
40604         },
40605         
40606         setValue : function(v)
40607         {
40608             var d = this.getDialCode(v);
40609             
40610             //invalid dial code
40611             if(v.length == 0 || !d || d.length == 0) {
40612                 if(this.rendered){
40613                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40614                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40615                 }
40616                 return;
40617             }
40618             
40619             //valid dial code
40620             this.setFlagClass(this.dialCodeMapping[d].iso2);
40621             this.setDialCode(d);
40622             this.inputEl().dom.value = v.replace('+'+d,'');
40623             this.hiddenEl().dom.value = this.getValue();
40624             
40625             this.validate();
40626         },
40627         
40628         getDialCode : function(v)
40629         {
40630             v = v ||  '';
40631             
40632             if (v.length == 0) {
40633                 return this.dialCodeHolder.dom.value;
40634             }
40635             
40636             var dialCode = "";
40637             if (v.charAt(0) != "+") {
40638                 return false;
40639             }
40640             var numericChars = "";
40641             for (var i = 1; i < v.length; i++) {
40642               var c = v.charAt(i);
40643               if (!isNaN(c)) {
40644                 numericChars += c;
40645                 if (this.dialCodeMapping[numericChars]) {
40646                   dialCode = v.substr(1, i);
40647                 }
40648                 if (numericChars.length == 4) {
40649                   break;
40650                 }
40651               }
40652             }
40653             return dialCode;
40654         },
40655         
40656         reset : function()
40657         {
40658             this.setValue(this.defaultDialCode);
40659             this.validate();
40660         },
40661         
40662         hiddenEl : function()
40663         {
40664             return this.el.select('input.hidden-tel-input',true).first();
40665         },
40666         
40667         // after setting val
40668         onKeyUp : function(e){
40669             this.setValue(this.getValue());
40670         },
40671         
40672         onKeyPress : function(e){
40673             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40674                 e.stopEvent();
40675             }
40676         }
40677         
40678 });
40679 /**
40680  * @class Roo.bootstrap.MoneyField
40681  * @extends Roo.bootstrap.ComboBox
40682  * Bootstrap MoneyField class
40683  * 
40684  * @constructor
40685  * Create a new MoneyField.
40686  * @param {Object} config Configuration options
40687  */
40688
40689 Roo.bootstrap.MoneyField = function(config) {
40690     
40691     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40692     
40693 };
40694
40695 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40696     
40697     /**
40698      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40699      */
40700     allowDecimals : true,
40701     /**
40702      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40703      */
40704     decimalSeparator : ".",
40705     /**
40706      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40707      */
40708     decimalPrecision : 0,
40709     /**
40710      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40711      */
40712     allowNegative : true,
40713     /**
40714      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40715      */
40716     allowZero: true,
40717     /**
40718      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40719      */
40720     minValue : Number.NEGATIVE_INFINITY,
40721     /**
40722      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40723      */
40724     maxValue : Number.MAX_VALUE,
40725     /**
40726      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40727      */
40728     minText : "The minimum value for this field is {0}",
40729     /**
40730      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40731      */
40732     maxText : "The maximum value for this field is {0}",
40733     /**
40734      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40735      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40736      */
40737     nanText : "{0} is not a valid number",
40738     /**
40739      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40740      */
40741     castInt : true,
40742     /**
40743      * @cfg {String} defaults currency of the MoneyField
40744      * value should be in lkey
40745      */
40746     defaultCurrency : false,
40747     /**
40748      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40749      */
40750     thousandsDelimiter : false,
40751     /**
40752      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40753      */
40754     max_length: false,
40755     
40756     inputlg : 9,
40757     inputmd : 9,
40758     inputsm : 9,
40759     inputxs : 6,
40760     
40761     store : false,
40762     
40763     getAutoCreate : function()
40764     {
40765         var align = this.labelAlign || this.parentLabelAlign();
40766         
40767         var id = Roo.id();
40768
40769         var cfg = {
40770             cls: 'form-group',
40771             cn: []
40772         };
40773
40774         var input =  {
40775             tag: 'input',
40776             id : id,
40777             cls : 'form-control roo-money-amount-input',
40778             autocomplete: 'new-password'
40779         };
40780         
40781         var hiddenInput = {
40782             tag: 'input',
40783             type: 'hidden',
40784             id: Roo.id(),
40785             cls: 'hidden-number-input'
40786         };
40787         
40788         if(this.max_length) {
40789             input.maxlength = this.max_length; 
40790         }
40791         
40792         if (this.name) {
40793             hiddenInput.name = this.name;
40794         }
40795
40796         if (this.disabled) {
40797             input.disabled = true;
40798         }
40799
40800         var clg = 12 - this.inputlg;
40801         var cmd = 12 - this.inputmd;
40802         var csm = 12 - this.inputsm;
40803         var cxs = 12 - this.inputxs;
40804         
40805         var container = {
40806             tag : 'div',
40807             cls : 'row roo-money-field',
40808             cn : [
40809                 {
40810                     tag : 'div',
40811                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40812                     cn : [
40813                         {
40814                             tag : 'div',
40815                             cls: 'roo-select2-container input-group',
40816                             cn: [
40817                                 {
40818                                     tag : 'input',
40819                                     cls : 'form-control roo-money-currency-input',
40820                                     autocomplete: 'new-password',
40821                                     readOnly : 1,
40822                                     name : this.currencyName
40823                                 },
40824                                 {
40825                                     tag :'span',
40826                                     cls : 'input-group-addon',
40827                                     cn : [
40828                                         {
40829                                             tag: 'span',
40830                                             cls: 'caret'
40831                                         }
40832                                     ]
40833                                 }
40834                             ]
40835                         }
40836                     ]
40837                 },
40838                 {
40839                     tag : 'div',
40840                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40841                     cn : [
40842                         {
40843                             tag: 'div',
40844                             cls: this.hasFeedback ? 'has-feedback' : '',
40845                             cn: [
40846                                 input
40847                             ]
40848                         }
40849                     ]
40850                 }
40851             ]
40852             
40853         };
40854         
40855         if (this.fieldLabel.length) {
40856             var indicator = {
40857                 tag: 'i',
40858                 tooltip: 'This field is required'
40859             };
40860
40861             var label = {
40862                 tag: 'label',
40863                 'for':  id,
40864                 cls: 'control-label',
40865                 cn: []
40866             };
40867
40868             var label_text = {
40869                 tag: 'span',
40870                 html: this.fieldLabel
40871             };
40872
40873             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40874             label.cn = [
40875                 indicator,
40876                 label_text
40877             ];
40878
40879             if(this.indicatorpos == 'right') {
40880                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40881                 label.cn = [
40882                     label_text,
40883                     indicator
40884                 ];
40885             }
40886
40887             if(align == 'left') {
40888                 container = {
40889                     tag: 'div',
40890                     cn: [
40891                         container
40892                     ]
40893                 };
40894
40895                 if(this.labelWidth > 12){
40896                     label.style = "width: " + this.labelWidth + 'px';
40897                 }
40898                 if(this.labelWidth < 13 && this.labelmd == 0){
40899                     this.labelmd = this.labelWidth;
40900                 }
40901                 if(this.labellg > 0){
40902                     label.cls += ' col-lg-' + this.labellg;
40903                     input.cls += ' col-lg-' + (12 - this.labellg);
40904                 }
40905                 if(this.labelmd > 0){
40906                     label.cls += ' col-md-' + this.labelmd;
40907                     container.cls += ' col-md-' + (12 - this.labelmd);
40908                 }
40909                 if(this.labelsm > 0){
40910                     label.cls += ' col-sm-' + this.labelsm;
40911                     container.cls += ' col-sm-' + (12 - this.labelsm);
40912                 }
40913                 if(this.labelxs > 0){
40914                     label.cls += ' col-xs-' + this.labelxs;
40915                     container.cls += ' col-xs-' + (12 - this.labelxs);
40916                 }
40917             }
40918         }
40919
40920         cfg.cn = [
40921             label,
40922             container,
40923             hiddenInput
40924         ];
40925         
40926         var settings = this;
40927
40928         ['xs','sm','md','lg'].map(function(size){
40929             if (settings[size]) {
40930                 cfg.cls += ' col-' + size + '-' + settings[size];
40931             }
40932         });
40933         
40934         return cfg;
40935     },
40936     
40937     initEvents : function()
40938     {
40939         this.indicator = this.indicatorEl();
40940         
40941         this.initCurrencyEvent();
40942         
40943         this.initNumberEvent();
40944     },
40945     
40946     initCurrencyEvent : function()
40947     {
40948         if (!this.store) {
40949             throw "can not find store for combo";
40950         }
40951         
40952         this.store = Roo.factory(this.store, Roo.data);
40953         this.store.parent = this;
40954         
40955         this.createList();
40956         
40957         this.triggerEl = this.el.select('.input-group-addon', true).first();
40958         
40959         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40960         
40961         var _this = this;
40962         
40963         (function(){
40964             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40965             _this.list.setWidth(lw);
40966         }).defer(100);
40967         
40968         this.list.on('mouseover', this.onViewOver, this);
40969         this.list.on('mousemove', this.onViewMove, this);
40970         this.list.on('scroll', this.onViewScroll, this);
40971         
40972         if(!this.tpl){
40973             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40974         }
40975         
40976         this.view = new Roo.View(this.list, this.tpl, {
40977             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40978         });
40979         
40980         this.view.on('click', this.onViewClick, this);
40981         
40982         this.store.on('beforeload', this.onBeforeLoad, this);
40983         this.store.on('load', this.onLoad, this);
40984         this.store.on('loadexception', this.onLoadException, this);
40985         
40986         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40987             "up" : function(e){
40988                 this.inKeyMode = true;
40989                 this.selectPrev();
40990             },
40991
40992             "down" : function(e){
40993                 if(!this.isExpanded()){
40994                     this.onTriggerClick();
40995                 }else{
40996                     this.inKeyMode = true;
40997                     this.selectNext();
40998                 }
40999             },
41000
41001             "enter" : function(e){
41002                 this.collapse();
41003                 
41004                 if(this.fireEvent("specialkey", this, e)){
41005                     this.onViewClick(false);
41006                 }
41007                 
41008                 return true;
41009             },
41010
41011             "esc" : function(e){
41012                 this.collapse();
41013             },
41014
41015             "tab" : function(e){
41016                 this.collapse();
41017                 
41018                 if(this.fireEvent("specialkey", this, e)){
41019                     this.onViewClick(false);
41020                 }
41021                 
41022                 return true;
41023             },
41024
41025             scope : this,
41026
41027             doRelay : function(foo, bar, hname){
41028                 if(hname == 'down' || this.scope.isExpanded()){
41029                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41030                 }
41031                 return true;
41032             },
41033
41034             forceKeyDown: true
41035         });
41036         
41037         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41038         
41039     },
41040     
41041     initNumberEvent : function(e)
41042     {
41043         this.inputEl().on("keydown" , this.fireKey,  this);
41044         this.inputEl().on("focus", this.onFocus,  this);
41045         this.inputEl().on("blur", this.onBlur,  this);
41046         
41047         this.inputEl().relayEvent('keyup', this);
41048         
41049         if(this.indicator){
41050             this.indicator.addClass('invisible');
41051         }
41052  
41053         this.originalValue = this.getValue();
41054         
41055         if(this.validationEvent == 'keyup'){
41056             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41057             this.inputEl().on('keyup', this.filterValidation, this);
41058         }
41059         else if(this.validationEvent !== false){
41060             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41061         }
41062         
41063         if(this.selectOnFocus){
41064             this.on("focus", this.preFocus, this);
41065             
41066         }
41067         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41068             this.inputEl().on("keypress", this.filterKeys, this);
41069         } else {
41070             this.inputEl().relayEvent('keypress', this);
41071         }
41072         
41073         var allowed = "0123456789";
41074         
41075         if(this.allowDecimals){
41076             allowed += this.decimalSeparator;
41077         }
41078         
41079         if(this.allowNegative){
41080             allowed += "-";
41081         }
41082         
41083         if(this.thousandsDelimiter) {
41084             allowed += ",";
41085         }
41086         
41087         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41088         
41089         var keyPress = function(e){
41090             
41091             var k = e.getKey();
41092             
41093             var c = e.getCharCode();
41094             
41095             if(
41096                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41097                     allowed.indexOf(String.fromCharCode(c)) === -1
41098             ){
41099                 e.stopEvent();
41100                 return;
41101             }
41102             
41103             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41104                 return;
41105             }
41106             
41107             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41108                 e.stopEvent();
41109             }
41110         };
41111         
41112         this.inputEl().on("keypress", keyPress, this);
41113         
41114     },
41115     
41116     onTriggerClick : function(e)
41117     {   
41118         if(this.disabled){
41119             return;
41120         }
41121         
41122         this.page = 0;
41123         this.loadNext = false;
41124         
41125         if(this.isExpanded()){
41126             this.collapse();
41127             return;
41128         }
41129         
41130         this.hasFocus = true;
41131         
41132         if(this.triggerAction == 'all') {
41133             this.doQuery(this.allQuery, true);
41134             return;
41135         }
41136         
41137         this.doQuery(this.getRawValue());
41138     },
41139     
41140     getCurrency : function()
41141     {   
41142         var v = this.currencyEl().getValue();
41143         
41144         return v;
41145     },
41146     
41147     restrictHeight : function()
41148     {
41149         this.list.alignTo(this.currencyEl(), this.listAlign);
41150         this.list.alignTo(this.currencyEl(), this.listAlign);
41151     },
41152     
41153     onViewClick : function(view, doFocus, el, e)
41154     {
41155         var index = this.view.getSelectedIndexes()[0];
41156         
41157         var r = this.store.getAt(index);
41158         
41159         if(r){
41160             this.onSelect(r, index);
41161         }
41162     },
41163     
41164     onSelect : function(record, index){
41165         
41166         if(this.fireEvent('beforeselect', this, record, index) !== false){
41167         
41168             this.setFromCurrencyData(index > -1 ? record.data : false);
41169             
41170             this.collapse();
41171             
41172             this.fireEvent('select', this, record, index);
41173         }
41174     },
41175     
41176     setFromCurrencyData : function(o)
41177     {
41178         var currency = '';
41179         
41180         this.lastCurrency = o;
41181         
41182         if (this.currencyField) {
41183             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41184         } else {
41185             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41186         }
41187         
41188         this.lastSelectionText = currency;
41189         
41190         //setting default currency
41191         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41192             this.setCurrency(this.defaultCurrency);
41193             return;
41194         }
41195         
41196         this.setCurrency(currency);
41197     },
41198     
41199     setFromData : function(o)
41200     {
41201         var c = {};
41202         
41203         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41204         
41205         this.setFromCurrencyData(c);
41206         
41207         var value = '';
41208         
41209         if (this.name) {
41210             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41211         } else {
41212             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41213         }
41214         
41215         this.setValue(value);
41216         
41217     },
41218     
41219     setCurrency : function(v)
41220     {   
41221         this.currencyValue = v;
41222         
41223         if(this.rendered){
41224             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41225             this.validate();
41226         }
41227     },
41228     
41229     setValue : function(v)
41230     {
41231         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41232         
41233         this.value = v;
41234         
41235         if(this.rendered){
41236             
41237             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41238             
41239             this.inputEl().dom.value = (v == '') ? '' :
41240                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41241             
41242             if(!this.allowZero && v === '0') {
41243                 this.hiddenEl().dom.value = '';
41244                 this.inputEl().dom.value = '';
41245             }
41246             
41247             this.validate();
41248         }
41249     },
41250     
41251     getRawValue : function()
41252     {
41253         var v = this.inputEl().getValue();
41254         
41255         return v;
41256     },
41257     
41258     getValue : function()
41259     {
41260         return this.fixPrecision(this.parseValue(this.getRawValue()));
41261     },
41262     
41263     parseValue : function(value)
41264     {
41265         if(this.thousandsDelimiter) {
41266             value += "";
41267             r = new RegExp(",", "g");
41268             value = value.replace(r, "");
41269         }
41270         
41271         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41272         return isNaN(value) ? '' : value;
41273         
41274     },
41275     
41276     fixPrecision : function(value)
41277     {
41278         if(this.thousandsDelimiter) {
41279             value += "";
41280             r = new RegExp(",", "g");
41281             value = value.replace(r, "");
41282         }
41283         
41284         var nan = isNaN(value);
41285         
41286         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41287             return nan ? '' : value;
41288         }
41289         return parseFloat(value).toFixed(this.decimalPrecision);
41290     },
41291     
41292     decimalPrecisionFcn : function(v)
41293     {
41294         return Math.floor(v);
41295     },
41296     
41297     validateValue : function(value)
41298     {
41299         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41300             return false;
41301         }
41302         
41303         var num = this.parseValue(value);
41304         
41305         if(isNaN(num)){
41306             this.markInvalid(String.format(this.nanText, value));
41307             return false;
41308         }
41309         
41310         if(num < this.minValue){
41311             this.markInvalid(String.format(this.minText, this.minValue));
41312             return false;
41313         }
41314         
41315         if(num > this.maxValue){
41316             this.markInvalid(String.format(this.maxText, this.maxValue));
41317             return false;
41318         }
41319         
41320         return true;
41321     },
41322     
41323     validate : function()
41324     {
41325         if(this.disabled || this.allowBlank){
41326             this.markValid();
41327             return true;
41328         }
41329         
41330         var currency = this.getCurrency();
41331         
41332         if(this.validateValue(this.getRawValue()) && currency.length){
41333             this.markValid();
41334             return true;
41335         }
41336         
41337         this.markInvalid();
41338         return false;
41339     },
41340     
41341     getName: function()
41342     {
41343         return this.name;
41344     },
41345     
41346     beforeBlur : function()
41347     {
41348         if(!this.castInt){
41349             return;
41350         }
41351         
41352         var v = this.parseValue(this.getRawValue());
41353         
41354         if(v || v == 0){
41355             this.setValue(v);
41356         }
41357     },
41358     
41359     onBlur : function()
41360     {
41361         this.beforeBlur();
41362         
41363         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41364             //this.el.removeClass(this.focusClass);
41365         }
41366         
41367         this.hasFocus = false;
41368         
41369         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41370             this.validate();
41371         }
41372         
41373         var v = this.getValue();
41374         
41375         if(String(v) !== String(this.startValue)){
41376             this.fireEvent('change', this, v, this.startValue);
41377         }
41378         
41379         this.fireEvent("blur", this);
41380     },
41381     
41382     inputEl : function()
41383     {
41384         return this.el.select('.roo-money-amount-input', true).first();
41385     },
41386     
41387     currencyEl : function()
41388     {
41389         return this.el.select('.roo-money-currency-input', true).first();
41390     },
41391     
41392     hiddenEl : function()
41393     {
41394         return this.el.select('input.hidden-number-input',true).first();
41395     }
41396     
41397 });