Roo/bootstrap/NavGroup.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 roo-navbar-simple'
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 (['light','white'].indexOf(this.weight) > -1) {
4192             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4193         }
4194         cfg.cls += ' bg-' + this.weight;
4195         
4196         
4197         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4198             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4199             
4200             // tag can override this..
4201             
4202             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4203         }
4204         
4205         if (this.brand !== '') {
4206             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4207             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4208                 tag: 'a',
4209                 href: this.brand_href ? this.brand_href : '#',
4210                 cls: 'navbar-brand',
4211                 cn: [
4212                 this.brand
4213                 ]
4214             });
4215         }
4216         
4217         if(this.main){
4218             cfg.cls += ' main-nav';
4219         }
4220         
4221         
4222         return cfg;
4223
4224         
4225     },
4226     getHeaderChildContainer : function()
4227     {
4228         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4229             return this.el.select('.navbar-header',true).first();
4230         }
4231         
4232         return this.getChildContainer();
4233     },
4234     
4235     
4236     initEvents : function()
4237     {
4238         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4239         
4240         if (this.autohide) {
4241             
4242             var prevScroll = 0;
4243             var ft = this.el;
4244             
4245             Roo.get(document).on('scroll',function(e) {
4246                 var ns = Roo.get(document).getScroll().top;
4247                 var os = prevScroll;
4248                 prevScroll = ns;
4249                 
4250                 if(ns > os){
4251                     ft.removeClass('slideDown');
4252                     ft.addClass('slideUp');
4253                     return;
4254                 }
4255                 ft.removeClass('slideUp');
4256                 ft.addClass('slideDown');
4257                  
4258               
4259           },this);
4260         }
4261     }    
4262     
4263 });
4264
4265
4266
4267  
4268
4269  /*
4270  * - LGPL
4271  *
4272  * navbar
4273  * 
4274  */
4275
4276 /**
4277  * @class Roo.bootstrap.NavSidebar
4278  * @extends Roo.bootstrap.Navbar
4279  * Bootstrap Sidebar class
4280  * 
4281  * @constructor
4282  * Create a new Sidebar
4283  * @param {Object} config The config object
4284  */
4285
4286
4287 Roo.bootstrap.NavSidebar = function(config){
4288     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4289 };
4290
4291 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4292     
4293     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4294     
4295     getAutoCreate : function(){
4296         
4297         
4298         return  {
4299             tag: 'div',
4300             cls: 'sidebar sidebar-nav'
4301         };
4302     
4303         
4304     }
4305     
4306     
4307     
4308 });
4309
4310
4311
4312  
4313
4314  /*
4315  * - LGPL
4316  *
4317  * nav group
4318  * 
4319  */
4320
4321 /**
4322  * @class Roo.bootstrap.NavGroup
4323  * @extends Roo.bootstrap.Component
4324  * Bootstrap NavGroup class
4325  * @cfg {String} align (left|right)
4326  * @cfg {Boolean} inverse
4327  * @cfg {String} type (nav|pills|tab) default nav
4328  * @cfg {String} navId - reference Id for navbar.
4329
4330  * 
4331  * @constructor
4332  * Create a new nav group
4333  * @param {Object} config The config object
4334  */
4335
4336 Roo.bootstrap.NavGroup = function(config){
4337     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4338     this.navItems = [];
4339    
4340     Roo.bootstrap.NavGroup.register(this);
4341      this.addEvents({
4342         /**
4343              * @event changed
4344              * Fires when the active item changes
4345              * @param {Roo.bootstrap.NavGroup} this
4346              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4347              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4348          */
4349         'changed': true
4350      });
4351     
4352 };
4353
4354 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4355     
4356     align: '',
4357     inverse: false,
4358     form: false,
4359     type: 'nav',
4360     navId : '',
4361     // private
4362     
4363     navItems : false, 
4364     
4365     getAutoCreate : function()
4366     {
4367         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4368         
4369         cfg = {
4370             tag : 'ul',
4371             cls: 'nav' 
4372         };
4373         if (Roo.bootstrap.version == 4) {
4374             if (['tabs','pills'].indexOf(this.type) != -1) {
4375                 cfg.cls = ' nav-' + this.type; 
4376             } else {
4377                 cfg.cls += ' navbar-nav';
4378             }
4379         } else {
4380             if (['tabs','pills'].indexOf(this.type) != -1) {
4381                 cfg.cls += ' nav-' + this.type
4382             } else {
4383                 if (this.type !== 'nav') {
4384                     Roo.log('nav type must be nav/tabs/pills')
4385                 }
4386                 cfg.cls += ' navbar-nav'
4387             }
4388         }
4389         
4390         if (this.parent() && this.parent().sidebar) {
4391             cfg = {
4392                 tag: 'ul',
4393                 cls: 'dashboard-menu sidebar-menu'
4394             };
4395             
4396             return cfg;
4397         }
4398         
4399         if (this.form === true) {
4400             cfg = {
4401                 tag: 'form',
4402                 cls: 'navbar-form form-inline'
4403             };
4404             
4405             if (this.align === 'right') {
4406                 cfg.cls += ' navbar-right ml-md-auto';
4407             } else {
4408                 cfg.cls += ' navbar-left';
4409             }
4410         }
4411         
4412         if (this.align === 'right') {
4413             cfg.cls += ' navbar-right ml-md-auto';
4414         } else {
4415             cfg.cls += ' mr-auto';
4416         }
4417         
4418         if (this.inverse) {
4419             cfg.cls += ' navbar-inverse';
4420             
4421         }
4422         
4423         
4424         return cfg;
4425     },
4426     /**
4427     * sets the active Navigation item
4428     * @param {Roo.bootstrap.NavItem} the new current navitem
4429     */
4430     setActiveItem : function(item)
4431     {
4432         var prev = false;
4433         Roo.each(this.navItems, function(v){
4434             if (v == item) {
4435                 return ;
4436             }
4437             if (v.isActive()) {
4438                 v.setActive(false, true);
4439                 prev = v;
4440                 
4441             }
4442             
4443         });
4444
4445         item.setActive(true, true);
4446         this.fireEvent('changed', this, item, prev);
4447         
4448         
4449     },
4450     /**
4451     * gets the active Navigation item
4452     * @return {Roo.bootstrap.NavItem} the current navitem
4453     */
4454     getActive : function()
4455     {
4456         
4457         var prev = false;
4458         Roo.each(this.navItems, function(v){
4459             
4460             if (v.isActive()) {
4461                 prev = v;
4462                 
4463             }
4464             
4465         });
4466         return prev;
4467     },
4468     
4469     indexOfNav : function()
4470     {
4471         
4472         var prev = false;
4473         Roo.each(this.navItems, function(v,i){
4474             
4475             if (v.isActive()) {
4476                 prev = i;
4477                 
4478             }
4479             
4480         });
4481         return prev;
4482     },
4483     /**
4484     * adds a Navigation item
4485     * @param {Roo.bootstrap.NavItem} the navitem to add
4486     */
4487     addItem : function(cfg)
4488     {
4489         if (this.form && Roo.bootstrap.version == 4) {
4490             cfg.tag = 'div';
4491         }
4492         var cn = new Roo.bootstrap.NavItem(cfg);
4493         this.register(cn);
4494         cn.parentId = this.id;
4495         cn.onRender(this.el, null);
4496         return cn;
4497     },
4498     /**
4499     * register a Navigation item
4500     * @param {Roo.bootstrap.NavItem} the navitem to add
4501     */
4502     register : function(item)
4503     {
4504         this.navItems.push( item);
4505         item.navId = this.navId;
4506     
4507     },
4508     
4509     /**
4510     * clear all the Navigation item
4511     */
4512    
4513     clearAll : function()
4514     {
4515         this.navItems = [];
4516         this.el.dom.innerHTML = '';
4517     },
4518     
4519     getNavItem: function(tabId)
4520     {
4521         var ret = false;
4522         Roo.each(this.navItems, function(e) {
4523             if (e.tabId == tabId) {
4524                ret =  e;
4525                return false;
4526             }
4527             return true;
4528             
4529         });
4530         return ret;
4531     },
4532     
4533     setActiveNext : function()
4534     {
4535         var i = this.indexOfNav(this.getActive());
4536         if (i > this.navItems.length) {
4537             return;
4538         }
4539         this.setActiveItem(this.navItems[i+1]);
4540     },
4541     setActivePrev : function()
4542     {
4543         var i = this.indexOfNav(this.getActive());
4544         if (i  < 1) {
4545             return;
4546         }
4547         this.setActiveItem(this.navItems[i-1]);
4548     },
4549     clearWasActive : function(except) {
4550         Roo.each(this.navItems, function(e) {
4551             if (e.tabId != except.tabId && e.was_active) {
4552                e.was_active = false;
4553                return false;
4554             }
4555             return true;
4556             
4557         });
4558     },
4559     getWasActive : function ()
4560     {
4561         var r = false;
4562         Roo.each(this.navItems, function(e) {
4563             if (e.was_active) {
4564                r = e;
4565                return false;
4566             }
4567             return true;
4568             
4569         });
4570         return r;
4571     }
4572     
4573     
4574 });
4575
4576  
4577 Roo.apply(Roo.bootstrap.NavGroup, {
4578     
4579     groups: {},
4580      /**
4581     * register a Navigation Group
4582     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4583     */
4584     register : function(navgrp)
4585     {
4586         this.groups[navgrp.navId] = navgrp;
4587         
4588     },
4589     /**
4590     * fetch a Navigation Group based on the navigation ID
4591     * @param {string} the navgroup to add
4592     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4593     */
4594     get: function(navId) {
4595         if (typeof(this.groups[navId]) == 'undefined') {
4596             return false;
4597             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4598         }
4599         return this.groups[navId] ;
4600     }
4601     
4602     
4603     
4604 });
4605
4606  /*
4607  * - LGPL
4608  *
4609  * row
4610  * 
4611  */
4612
4613 /**
4614  * @class Roo.bootstrap.NavItem
4615  * @extends Roo.bootstrap.Component
4616  * Bootstrap Navbar.NavItem class
4617  * @cfg {String} href  link to
4618  * @cfg {String} html content of button
4619  * @cfg {String} badge text inside badge
4620  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4621  * @cfg {String} glyphicon DEPRICATED - use fa
4622  * @cfg {String} icon DEPRICATED - use fa
4623  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4624  * @cfg {Boolean} active Is item active
4625  * @cfg {Boolean} disabled Is item disabled
4626  
4627  * @cfg {Boolean} preventDefault (true | false) default false
4628  * @cfg {String} tabId the tab that this item activates.
4629  * @cfg {String} tagtype (a|span) render as a href or span?
4630  * @cfg {Boolean} animateRef (true|false) link to element default false  
4631   
4632  * @constructor
4633  * Create a new Navbar Item
4634  * @param {Object} config The config object
4635  */
4636 Roo.bootstrap.NavItem = function(config){
4637     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4638     this.addEvents({
4639         // raw events
4640         /**
4641          * @event click
4642          * The raw click event for the entire grid.
4643          * @param {Roo.EventObject} e
4644          */
4645         "click" : true,
4646          /**
4647             * @event changed
4648             * Fires when the active item active state changes
4649             * @param {Roo.bootstrap.NavItem} this
4650             * @param {boolean} state the new state
4651              
4652          */
4653         'changed': true,
4654         /**
4655             * @event scrollto
4656             * Fires when scroll to element
4657             * @param {Roo.bootstrap.NavItem} this
4658             * @param {Object} options
4659             * @param {Roo.EventObject} e
4660              
4661          */
4662         'scrollto': true
4663     });
4664    
4665 };
4666
4667 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4668     
4669     href: false,
4670     html: '',
4671     badge: '',
4672     icon: false,
4673     fa : false,
4674     glyphicon: false,
4675     active: false,
4676     preventDefault : false,
4677     tabId : false,
4678     tagtype : 'a',
4679     tag: 'li',
4680     disabled : false,
4681     animateRef : false,
4682     was_active : false,
4683     
4684     getAutoCreate : function(){
4685          
4686         var cfg = {
4687             tag: this.tag,
4688             cls: 'nav-item'
4689             
4690         };
4691         
4692         if (this.active) {
4693             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4694         }
4695         if (this.disabled) {
4696             cfg.cls += ' disabled';
4697         }
4698         
4699         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4700             cfg.cn = [
4701                 {
4702                     tag: this.tagtype,
4703                     href : this.href || "#",
4704                     html: this.html || ''
4705                 }
4706             ];
4707             if (this.tagtype == 'a') {
4708                 cfg.cn[0].cls = 'nav-link';
4709             }
4710             if (this.icon) {
4711                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4712             }
4713             if (this.fa) {
4714                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4715             }
4716             if(this.glyphicon) {
4717                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4718             }
4719             
4720             if (this.menu) {
4721                 
4722                 cfg.cn[0].html += " <span class='caret'></span>";
4723              
4724             }
4725             
4726             if (this.badge !== '') {
4727                  
4728                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4729             }
4730         }
4731         
4732         
4733         
4734         return cfg;
4735     },
4736     onRender : function(ct, position)
4737     {
4738        // Roo.log("Call onRender: " + this.xtype);
4739         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4740             this.tag = 'div';
4741         }
4742         
4743         return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4744     },
4745       
4746     
4747     initEvents: function() 
4748     {
4749         if (typeof (this.menu) != 'undefined') {
4750             this.menu.parentType = this.xtype;
4751             this.menu.triggerEl = this.el;
4752             this.menu = this.addxtype(Roo.apply({}, this.menu));
4753         }
4754         
4755         this.el.select('a',true).on('click', this.onClick, this);
4756         
4757         if(this.tagtype == 'span'){
4758             this.el.select('span',true).on('click', this.onClick, this);
4759         }
4760        
4761         // at this point parent should be available..
4762         this.parent().register(this);
4763     },
4764     
4765     onClick : function(e)
4766     {
4767         if (e.getTarget('.dropdown-menu-item')) {
4768             // did you click on a menu itemm.... - then don't trigger onclick..
4769             return;
4770         }
4771         
4772         if(
4773                 this.preventDefault || 
4774                 this.href == '#' 
4775         ){
4776             Roo.log("NavItem - prevent Default?");
4777             e.preventDefault();
4778         }
4779         
4780         if (this.disabled) {
4781             return;
4782         }
4783         
4784         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4785         if (tg && tg.transition) {
4786             Roo.log("waiting for the transitionend");
4787             return;
4788         }
4789         
4790         
4791         
4792         //Roo.log("fire event clicked");
4793         if(this.fireEvent('click', this, e) === false){
4794             return;
4795         };
4796         
4797         if(this.tagtype == 'span'){
4798             return;
4799         }
4800         
4801         //Roo.log(this.href);
4802         var ael = this.el.select('a',true).first();
4803         //Roo.log(ael);
4804         
4805         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4806             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4807             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4808                 return; // ignore... - it's a 'hash' to another page.
4809             }
4810             Roo.log("NavItem - prevent Default?");
4811             e.preventDefault();
4812             this.scrollToElement(e);
4813         }
4814         
4815         
4816         var p =  this.parent();
4817    
4818         if (['tabs','pills'].indexOf(p.type)!==-1) {
4819             if (typeof(p.setActiveItem) !== 'undefined') {
4820                 p.setActiveItem(this);
4821             }
4822         }
4823         
4824         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4825         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4826             // remove the collapsed menu expand...
4827             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4828         }
4829     },
4830     
4831     isActive: function () {
4832         return this.active
4833     },
4834     setActive : function(state, fire, is_was_active)
4835     {
4836         if (this.active && !state && this.navId) {
4837             this.was_active = true;
4838             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4839             if (nv) {
4840                 nv.clearWasActive(this);
4841             }
4842             
4843         }
4844         this.active = state;
4845         
4846         if (!state ) {
4847             this.el.removeClass('active');
4848         } else if (!this.el.hasClass('active')) {
4849             this.el.addClass('active');
4850         }
4851         if (fire) {
4852             this.fireEvent('changed', this, state);
4853         }
4854         
4855         // show a panel if it's registered and related..
4856         
4857         if (!this.navId || !this.tabId || !state || is_was_active) {
4858             return;
4859         }
4860         
4861         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4862         if (!tg) {
4863             return;
4864         }
4865         var pan = tg.getPanelByName(this.tabId);
4866         if (!pan) {
4867             return;
4868         }
4869         // if we can not flip to new panel - go back to old nav highlight..
4870         if (false == tg.showPanel(pan)) {
4871             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4872             if (nv) {
4873                 var onav = nv.getWasActive();
4874                 if (onav) {
4875                     onav.setActive(true, false, true);
4876                 }
4877             }
4878             
4879         }
4880         
4881         
4882         
4883     },
4884      // this should not be here...
4885     setDisabled : function(state)
4886     {
4887         this.disabled = state;
4888         if (!state ) {
4889             this.el.removeClass('disabled');
4890         } else if (!this.el.hasClass('disabled')) {
4891             this.el.addClass('disabled');
4892         }
4893         
4894     },
4895     
4896     /**
4897      * Fetch the element to display the tooltip on.
4898      * @return {Roo.Element} defaults to this.el
4899      */
4900     tooltipEl : function()
4901     {
4902         return this.el.select('' + this.tagtype + '', true).first();
4903     },
4904     
4905     scrollToElement : function(e)
4906     {
4907         var c = document.body;
4908         
4909         /*
4910          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4911          */
4912         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4913             c = document.documentElement;
4914         }
4915         
4916         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4917         
4918         if(!target){
4919             return;
4920         }
4921
4922         var o = target.calcOffsetsTo(c);
4923         
4924         var options = {
4925             target : target,
4926             value : o[1]
4927         };
4928         
4929         this.fireEvent('scrollto', this, options, e);
4930         
4931         Roo.get(c).scrollTo('top', options.value, true);
4932         
4933         return;
4934     }
4935 });
4936  
4937
4938  /*
4939  * - LGPL
4940  *
4941  * sidebar item
4942  *
4943  *  li
4944  *    <span> icon </span>
4945  *    <span> text </span>
4946  *    <span>badge </span>
4947  */
4948
4949 /**
4950  * @class Roo.bootstrap.NavSidebarItem
4951  * @extends Roo.bootstrap.NavItem
4952  * Bootstrap Navbar.NavSidebarItem class
4953  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4954  * {Boolean} open is the menu open
4955  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4956  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4957  * {String} buttonSize (sm|md|lg)the extra classes for the button
4958  * {Boolean} showArrow show arrow next to the text (default true)
4959  * @constructor
4960  * Create a new Navbar Button
4961  * @param {Object} config The config object
4962  */
4963 Roo.bootstrap.NavSidebarItem = function(config){
4964     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4965     this.addEvents({
4966         // raw events
4967         /**
4968          * @event click
4969          * The raw click event for the entire grid.
4970          * @param {Roo.EventObject} e
4971          */
4972         "click" : true,
4973          /**
4974             * @event changed
4975             * Fires when the active item active state changes
4976             * @param {Roo.bootstrap.NavSidebarItem} this
4977             * @param {boolean} state the new state
4978              
4979          */
4980         'changed': true
4981     });
4982    
4983 };
4984
4985 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4986     
4987     badgeWeight : 'default',
4988     
4989     open: false,
4990     
4991     buttonView : false,
4992     
4993     buttonWeight : 'default',
4994     
4995     buttonSize : 'md',
4996     
4997     showArrow : true,
4998     
4999     getAutoCreate : function(){
5000         
5001         
5002         var a = {
5003                 tag: 'a',
5004                 href : this.href || '#',
5005                 cls: '',
5006                 html : '',
5007                 cn : []
5008         };
5009         
5010         if(this.buttonView){
5011             a = {
5012                 tag: 'button',
5013                 href : this.href || '#',
5014                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5015                 html : this.html,
5016                 cn : []
5017             };
5018         }
5019         
5020         var cfg = {
5021             tag: 'li',
5022             cls: '',
5023             cn: [ a ]
5024         };
5025         
5026         if (this.active) {
5027             cfg.cls += ' active';
5028         }
5029         
5030         if (this.disabled) {
5031             cfg.cls += ' disabled';
5032         }
5033         if (this.open) {
5034             cfg.cls += ' open x-open';
5035         }
5036         // left icon..
5037         if (this.glyphicon || this.icon) {
5038             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5039             a.cn.push({ tag : 'i', cls : c }) ;
5040         }
5041         
5042         if(!this.buttonView){
5043             var span = {
5044                 tag: 'span',
5045                 html : this.html || ''
5046             };
5047
5048             a.cn.push(span);
5049             
5050         }
5051         
5052         if (this.badge !== '') {
5053             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5054         }
5055         
5056         if (this.menu) {
5057             
5058             if(this.showArrow){
5059                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5060             }
5061             
5062             a.cls += ' dropdown-toggle treeview' ;
5063         }
5064         
5065         return cfg;
5066     },
5067     
5068     initEvents : function()
5069     { 
5070         if (typeof (this.menu) != 'undefined') {
5071             this.menu.parentType = this.xtype;
5072             this.menu.triggerEl = this.el;
5073             this.menu = this.addxtype(Roo.apply({}, this.menu));
5074         }
5075         
5076         this.el.on('click', this.onClick, this);
5077         
5078         if(this.badge !== ''){
5079             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5080         }
5081         
5082     },
5083     
5084     onClick : function(e)
5085     {
5086         if(this.disabled){
5087             e.preventDefault();
5088             return;
5089         }
5090         
5091         if(this.preventDefault){
5092             e.preventDefault();
5093         }
5094         
5095         this.fireEvent('click', this);
5096     },
5097     
5098     disable : function()
5099     {
5100         this.setDisabled(true);
5101     },
5102     
5103     enable : function()
5104     {
5105         this.setDisabled(false);
5106     },
5107     
5108     setDisabled : function(state)
5109     {
5110         if(this.disabled == state){
5111             return;
5112         }
5113         
5114         this.disabled = state;
5115         
5116         if (state) {
5117             this.el.addClass('disabled');
5118             return;
5119         }
5120         
5121         this.el.removeClass('disabled');
5122         
5123         return;
5124     },
5125     
5126     setActive : function(state)
5127     {
5128         if(this.active == state){
5129             return;
5130         }
5131         
5132         this.active = state;
5133         
5134         if (state) {
5135             this.el.addClass('active');
5136             return;
5137         }
5138         
5139         this.el.removeClass('active');
5140         
5141         return;
5142     },
5143     
5144     isActive: function () 
5145     {
5146         return this.active;
5147     },
5148     
5149     setBadge : function(str)
5150     {
5151         if(!this.badgeEl){
5152             return;
5153         }
5154         
5155         this.badgeEl.dom.innerHTML = str;
5156     }
5157     
5158    
5159      
5160  
5161 });
5162  
5163
5164  /*
5165  * - LGPL
5166  *
5167  * row
5168  * 
5169  */
5170
5171 /**
5172  * @class Roo.bootstrap.Row
5173  * @extends Roo.bootstrap.Component
5174  * Bootstrap Row class (contains columns...)
5175  * 
5176  * @constructor
5177  * Create a new Row
5178  * @param {Object} config The config object
5179  */
5180
5181 Roo.bootstrap.Row = function(config){
5182     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5183 };
5184
5185 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5186     
5187     getAutoCreate : function(){
5188        return {
5189             cls: 'row clearfix'
5190        };
5191     }
5192     
5193     
5194 });
5195
5196  
5197
5198  /*
5199  * - LGPL
5200  *
5201  * element
5202  * 
5203  */
5204
5205 /**
5206  * @class Roo.bootstrap.Element
5207  * @extends Roo.bootstrap.Component
5208  * Bootstrap Element class
5209  * @cfg {String} html contents of the element
5210  * @cfg {String} tag tag of the element
5211  * @cfg {String} cls class of the element
5212  * @cfg {Boolean} preventDefault (true|false) default false
5213  * @cfg {Boolean} clickable (true|false) default false
5214  * 
5215  * @constructor
5216  * Create a new Element
5217  * @param {Object} config The config object
5218  */
5219
5220 Roo.bootstrap.Element = function(config){
5221     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5222     
5223     this.addEvents({
5224         // raw events
5225         /**
5226          * @event click
5227          * When a element is chick
5228          * @param {Roo.bootstrap.Element} this
5229          * @param {Roo.EventObject} e
5230          */
5231         "click" : true
5232     });
5233 };
5234
5235 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5236     
5237     tag: 'div',
5238     cls: '',
5239     html: '',
5240     preventDefault: false, 
5241     clickable: false,
5242     
5243     getAutoCreate : function(){
5244         
5245         var cfg = {
5246             tag: this.tag,
5247             // cls: this.cls, double assign in parent class Component.js :: onRender
5248             html: this.html
5249         };
5250         
5251         return cfg;
5252     },
5253     
5254     initEvents: function() 
5255     {
5256         Roo.bootstrap.Element.superclass.initEvents.call(this);
5257         
5258         if(this.clickable){
5259             this.el.on('click', this.onClick, this);
5260         }
5261         
5262     },
5263     
5264     onClick : function(e)
5265     {
5266         if(this.preventDefault){
5267             e.preventDefault();
5268         }
5269         
5270         this.fireEvent('click', this, e);
5271     },
5272     
5273     getValue : function()
5274     {
5275         return this.el.dom.innerHTML;
5276     },
5277     
5278     setValue : function(value)
5279     {
5280         this.el.dom.innerHTML = value;
5281     }
5282    
5283 });
5284
5285  
5286
5287  /*
5288  * - LGPL
5289  *
5290  * pagination
5291  * 
5292  */
5293
5294 /**
5295  * @class Roo.bootstrap.Pagination
5296  * @extends Roo.bootstrap.Component
5297  * Bootstrap Pagination class
5298  * @cfg {String} size xs | sm | md | lg
5299  * @cfg {Boolean} inverse false | true
5300  * 
5301  * @constructor
5302  * Create a new Pagination
5303  * @param {Object} config The config object
5304  */
5305
5306 Roo.bootstrap.Pagination = function(config){
5307     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5308 };
5309
5310 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5311     
5312     cls: false,
5313     size: false,
5314     inverse: false,
5315     
5316     getAutoCreate : function(){
5317         var cfg = {
5318             tag: 'ul',
5319                 cls: 'pagination'
5320         };
5321         if (this.inverse) {
5322             cfg.cls += ' inverse';
5323         }
5324         if (this.html) {
5325             cfg.html=this.html;
5326         }
5327         if (this.cls) {
5328             cfg.cls += " " + this.cls;
5329         }
5330         return cfg;
5331     }
5332    
5333 });
5334
5335  
5336
5337  /*
5338  * - LGPL
5339  *
5340  * Pagination item
5341  * 
5342  */
5343
5344
5345 /**
5346  * @class Roo.bootstrap.PaginationItem
5347  * @extends Roo.bootstrap.Component
5348  * Bootstrap PaginationItem class
5349  * @cfg {String} html text
5350  * @cfg {String} href the link
5351  * @cfg {Boolean} preventDefault (true | false) default true
5352  * @cfg {Boolean} active (true | false) default false
5353  * @cfg {Boolean} disabled default false
5354  * 
5355  * 
5356  * @constructor
5357  * Create a new PaginationItem
5358  * @param {Object} config The config object
5359  */
5360
5361
5362 Roo.bootstrap.PaginationItem = function(config){
5363     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5364     this.addEvents({
5365         // raw events
5366         /**
5367          * @event click
5368          * The raw click event for the entire grid.
5369          * @param {Roo.EventObject} e
5370          */
5371         "click" : true
5372     });
5373 };
5374
5375 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5376     
5377     href : false,
5378     html : false,
5379     preventDefault: true,
5380     active : false,
5381     cls : false,
5382     disabled: false,
5383     
5384     getAutoCreate : function(){
5385         var cfg= {
5386             tag: 'li',
5387             cn: [
5388                 {
5389                     tag : 'a',
5390                     href : this.href ? this.href : '#',
5391                     html : this.html ? this.html : ''
5392                 }
5393             ]
5394         };
5395         
5396         if(this.cls){
5397             cfg.cls = this.cls;
5398         }
5399         
5400         if(this.disabled){
5401             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5402         }
5403         
5404         if(this.active){
5405             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5406         }
5407         
5408         return cfg;
5409     },
5410     
5411     initEvents: function() {
5412         
5413         this.el.on('click', this.onClick, this);
5414         
5415     },
5416     onClick : function(e)
5417     {
5418         Roo.log('PaginationItem on click ');
5419         if(this.preventDefault){
5420             e.preventDefault();
5421         }
5422         
5423         if(this.disabled){
5424             return;
5425         }
5426         
5427         this.fireEvent('click', this, e);
5428     }
5429    
5430 });
5431
5432  
5433
5434  /*
5435  * - LGPL
5436  *
5437  * slider
5438  * 
5439  */
5440
5441
5442 /**
5443  * @class Roo.bootstrap.Slider
5444  * @extends Roo.bootstrap.Component
5445  * Bootstrap Slider class
5446  *    
5447  * @constructor
5448  * Create a new Slider
5449  * @param {Object} config The config object
5450  */
5451
5452 Roo.bootstrap.Slider = function(config){
5453     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5454 };
5455
5456 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5457     
5458     getAutoCreate : function(){
5459         
5460         var cfg = {
5461             tag: 'div',
5462             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5463             cn: [
5464                 {
5465                     tag: 'a',
5466                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5467                 }
5468             ]
5469         };
5470         
5471         return cfg;
5472     }
5473    
5474 });
5475
5476  /*
5477  * Based on:
5478  * Ext JS Library 1.1.1
5479  * Copyright(c) 2006-2007, Ext JS, LLC.
5480  *
5481  * Originally Released Under LGPL - original licence link has changed is not relivant.
5482  *
5483  * Fork - LGPL
5484  * <script type="text/javascript">
5485  */
5486  
5487
5488 /**
5489  * @class Roo.grid.ColumnModel
5490  * @extends Roo.util.Observable
5491  * This is the default implementation of a ColumnModel used by the Grid. It defines
5492  * the columns in the grid.
5493  * <br>Usage:<br>
5494  <pre><code>
5495  var colModel = new Roo.grid.ColumnModel([
5496         {header: "Ticker", width: 60, sortable: true, locked: true},
5497         {header: "Company Name", width: 150, sortable: true},
5498         {header: "Market Cap.", width: 100, sortable: true},
5499         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5500         {header: "Employees", width: 100, sortable: true, resizable: false}
5501  ]);
5502  </code></pre>
5503  * <p>
5504  
5505  * The config options listed for this class are options which may appear in each
5506  * individual column definition.
5507  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5508  * @constructor
5509  * @param {Object} config An Array of column config objects. See this class's
5510  * config objects for details.
5511 */
5512 Roo.grid.ColumnModel = function(config){
5513         /**
5514      * The config passed into the constructor
5515      */
5516     this.config = config;
5517     this.lookup = {};
5518
5519     // if no id, create one
5520     // if the column does not have a dataIndex mapping,
5521     // map it to the order it is in the config
5522     for(var i = 0, len = config.length; i < len; i++){
5523         var c = config[i];
5524         if(typeof c.dataIndex == "undefined"){
5525             c.dataIndex = i;
5526         }
5527         if(typeof c.renderer == "string"){
5528             c.renderer = Roo.util.Format[c.renderer];
5529         }
5530         if(typeof c.id == "undefined"){
5531             c.id = Roo.id();
5532         }
5533         if(c.editor && c.editor.xtype){
5534             c.editor  = Roo.factory(c.editor, Roo.grid);
5535         }
5536         if(c.editor && c.editor.isFormField){
5537             c.editor = new Roo.grid.GridEditor(c.editor);
5538         }
5539         this.lookup[c.id] = c;
5540     }
5541
5542     /**
5543      * The width of columns which have no width specified (defaults to 100)
5544      * @type Number
5545      */
5546     this.defaultWidth = 100;
5547
5548     /**
5549      * Default sortable of columns which have no sortable specified (defaults to false)
5550      * @type Boolean
5551      */
5552     this.defaultSortable = false;
5553
5554     this.addEvents({
5555         /**
5556              * @event widthchange
5557              * Fires when the width of a column changes.
5558              * @param {ColumnModel} this
5559              * @param {Number} columnIndex The column index
5560              * @param {Number} newWidth The new width
5561              */
5562             "widthchange": true,
5563         /**
5564              * @event headerchange
5565              * Fires when the text of a header changes.
5566              * @param {ColumnModel} this
5567              * @param {Number} columnIndex The column index
5568              * @param {Number} newText The new header text
5569              */
5570             "headerchange": true,
5571         /**
5572              * @event hiddenchange
5573              * Fires when a column is hidden or "unhidden".
5574              * @param {ColumnModel} this
5575              * @param {Number} columnIndex The column index
5576              * @param {Boolean} hidden true if hidden, false otherwise
5577              */
5578             "hiddenchange": true,
5579             /**
5580          * @event columnmoved
5581          * Fires when a column is moved.
5582          * @param {ColumnModel} this
5583          * @param {Number} oldIndex
5584          * @param {Number} newIndex
5585          */
5586         "columnmoved" : true,
5587         /**
5588          * @event columlockchange
5589          * Fires when a column's locked state is changed
5590          * @param {ColumnModel} this
5591          * @param {Number} colIndex
5592          * @param {Boolean} locked true if locked
5593          */
5594         "columnlockchange" : true
5595     });
5596     Roo.grid.ColumnModel.superclass.constructor.call(this);
5597 };
5598 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5599     /**
5600      * @cfg {String} header The header text to display in the Grid view.
5601      */
5602     /**
5603      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5604      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5605      * specified, the column's index is used as an index into the Record's data Array.
5606      */
5607     /**
5608      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5609      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5610      */
5611     /**
5612      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5613      * Defaults to the value of the {@link #defaultSortable} property.
5614      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5615      */
5616     /**
5617      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5618      */
5619     /**
5620      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5621      */
5622     /**
5623      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5624      */
5625     /**
5626      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5627      */
5628     /**
5629      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5630      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5631      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5632      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5633      */
5634        /**
5635      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5636      */
5637     /**
5638      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5639      */
5640     /**
5641      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5642      */
5643     /**
5644      * @cfg {String} cursor (Optional)
5645      */
5646     /**
5647      * @cfg {String} tooltip (Optional)
5648      */
5649     /**
5650      * @cfg {Number} xs (Optional)
5651      */
5652     /**
5653      * @cfg {Number} sm (Optional)
5654      */
5655     /**
5656      * @cfg {Number} md (Optional)
5657      */
5658     /**
5659      * @cfg {Number} lg (Optional)
5660      */
5661     /**
5662      * Returns the id of the column at the specified index.
5663      * @param {Number} index The column index
5664      * @return {String} the id
5665      */
5666     getColumnId : function(index){
5667         return this.config[index].id;
5668     },
5669
5670     /**
5671      * Returns the column for a specified id.
5672      * @param {String} id The column id
5673      * @return {Object} the column
5674      */
5675     getColumnById : function(id){
5676         return this.lookup[id];
5677     },
5678
5679     
5680     /**
5681      * Returns the column for a specified dataIndex.
5682      * @param {String} dataIndex The column dataIndex
5683      * @return {Object|Boolean} the column or false if not found
5684      */
5685     getColumnByDataIndex: function(dataIndex){
5686         var index = this.findColumnIndex(dataIndex);
5687         return index > -1 ? this.config[index] : false;
5688     },
5689     
5690     /**
5691      * Returns the index for a specified column id.
5692      * @param {String} id The column id
5693      * @return {Number} the index, or -1 if not found
5694      */
5695     getIndexById : function(id){
5696         for(var i = 0, len = this.config.length; i < len; i++){
5697             if(this.config[i].id == id){
5698                 return i;
5699             }
5700         }
5701         return -1;
5702     },
5703     
5704     /**
5705      * Returns the index for a specified column dataIndex.
5706      * @param {String} dataIndex The column dataIndex
5707      * @return {Number} the index, or -1 if not found
5708      */
5709     
5710     findColumnIndex : function(dataIndex){
5711         for(var i = 0, len = this.config.length; i < len; i++){
5712             if(this.config[i].dataIndex == dataIndex){
5713                 return i;
5714             }
5715         }
5716         return -1;
5717     },
5718     
5719     
5720     moveColumn : function(oldIndex, newIndex){
5721         var c = this.config[oldIndex];
5722         this.config.splice(oldIndex, 1);
5723         this.config.splice(newIndex, 0, c);
5724         this.dataMap = null;
5725         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5726     },
5727
5728     isLocked : function(colIndex){
5729         return this.config[colIndex].locked === true;
5730     },
5731
5732     setLocked : function(colIndex, value, suppressEvent){
5733         if(this.isLocked(colIndex) == value){
5734             return;
5735         }
5736         this.config[colIndex].locked = value;
5737         if(!suppressEvent){
5738             this.fireEvent("columnlockchange", this, colIndex, value);
5739         }
5740     },
5741
5742     getTotalLockedWidth : function(){
5743         var totalWidth = 0;
5744         for(var i = 0; i < this.config.length; i++){
5745             if(this.isLocked(i) && !this.isHidden(i)){
5746                 this.totalWidth += this.getColumnWidth(i);
5747             }
5748         }
5749         return totalWidth;
5750     },
5751
5752     getLockedCount : function(){
5753         for(var i = 0, len = this.config.length; i < len; i++){
5754             if(!this.isLocked(i)){
5755                 return i;
5756             }
5757         }
5758         
5759         return this.config.length;
5760     },
5761
5762     /**
5763      * Returns the number of columns.
5764      * @return {Number}
5765      */
5766     getColumnCount : function(visibleOnly){
5767         if(visibleOnly === true){
5768             var c = 0;
5769             for(var i = 0, len = this.config.length; i < len; i++){
5770                 if(!this.isHidden(i)){
5771                     c++;
5772                 }
5773             }
5774             return c;
5775         }
5776         return this.config.length;
5777     },
5778
5779     /**
5780      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5781      * @param {Function} fn
5782      * @param {Object} scope (optional)
5783      * @return {Array} result
5784      */
5785     getColumnsBy : function(fn, scope){
5786         var r = [];
5787         for(var i = 0, len = this.config.length; i < len; i++){
5788             var c = this.config[i];
5789             if(fn.call(scope||this, c, i) === true){
5790                 r[r.length] = c;
5791             }
5792         }
5793         return r;
5794     },
5795
5796     /**
5797      * Returns true if the specified column is sortable.
5798      * @param {Number} col The column index
5799      * @return {Boolean}
5800      */
5801     isSortable : function(col){
5802         if(typeof this.config[col].sortable == "undefined"){
5803             return this.defaultSortable;
5804         }
5805         return this.config[col].sortable;
5806     },
5807
5808     /**
5809      * Returns the rendering (formatting) function defined for the column.
5810      * @param {Number} col The column index.
5811      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5812      */
5813     getRenderer : function(col){
5814         if(!this.config[col].renderer){
5815             return Roo.grid.ColumnModel.defaultRenderer;
5816         }
5817         return this.config[col].renderer;
5818     },
5819
5820     /**
5821      * Sets the rendering (formatting) function for a column.
5822      * @param {Number} col The column index
5823      * @param {Function} fn The function to use to process the cell's raw data
5824      * to return HTML markup for the grid view. The render function is called with
5825      * the following parameters:<ul>
5826      * <li>Data value.</li>
5827      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5828      * <li>css A CSS style string to apply to the table cell.</li>
5829      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5830      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5831      * <li>Row index</li>
5832      * <li>Column index</li>
5833      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5834      */
5835     setRenderer : function(col, fn){
5836         this.config[col].renderer = fn;
5837     },
5838
5839     /**
5840      * Returns the width for the specified column.
5841      * @param {Number} col The column index
5842      * @return {Number}
5843      */
5844     getColumnWidth : function(col){
5845         return this.config[col].width * 1 || this.defaultWidth;
5846     },
5847
5848     /**
5849      * Sets the width for a column.
5850      * @param {Number} col The column index
5851      * @param {Number} width The new width
5852      */
5853     setColumnWidth : function(col, width, suppressEvent){
5854         this.config[col].width = width;
5855         this.totalWidth = null;
5856         if(!suppressEvent){
5857              this.fireEvent("widthchange", this, col, width);
5858         }
5859     },
5860
5861     /**
5862      * Returns the total width of all columns.
5863      * @param {Boolean} includeHidden True to include hidden column widths
5864      * @return {Number}
5865      */
5866     getTotalWidth : function(includeHidden){
5867         if(!this.totalWidth){
5868             this.totalWidth = 0;
5869             for(var i = 0, len = this.config.length; i < len; i++){
5870                 if(includeHidden || !this.isHidden(i)){
5871                     this.totalWidth += this.getColumnWidth(i);
5872                 }
5873             }
5874         }
5875         return this.totalWidth;
5876     },
5877
5878     /**
5879      * Returns the header for the specified column.
5880      * @param {Number} col The column index
5881      * @return {String}
5882      */
5883     getColumnHeader : function(col){
5884         return this.config[col].header;
5885     },
5886
5887     /**
5888      * Sets the header for a column.
5889      * @param {Number} col The column index
5890      * @param {String} header The new header
5891      */
5892     setColumnHeader : function(col, header){
5893         this.config[col].header = header;
5894         this.fireEvent("headerchange", this, col, header);
5895     },
5896
5897     /**
5898      * Returns the tooltip for the specified column.
5899      * @param {Number} col The column index
5900      * @return {String}
5901      */
5902     getColumnTooltip : function(col){
5903             return this.config[col].tooltip;
5904     },
5905     /**
5906      * Sets the tooltip for a column.
5907      * @param {Number} col The column index
5908      * @param {String} tooltip The new tooltip
5909      */
5910     setColumnTooltip : function(col, tooltip){
5911             this.config[col].tooltip = tooltip;
5912     },
5913
5914     /**
5915      * Returns the dataIndex for the specified column.
5916      * @param {Number} col The column index
5917      * @return {Number}
5918      */
5919     getDataIndex : function(col){
5920         return this.config[col].dataIndex;
5921     },
5922
5923     /**
5924      * Sets the dataIndex for a column.
5925      * @param {Number} col The column index
5926      * @param {Number} dataIndex The new dataIndex
5927      */
5928     setDataIndex : function(col, dataIndex){
5929         this.config[col].dataIndex = dataIndex;
5930     },
5931
5932     
5933     
5934     /**
5935      * Returns true if the cell is editable.
5936      * @param {Number} colIndex The column index
5937      * @param {Number} rowIndex The row index - this is nto actually used..?
5938      * @return {Boolean}
5939      */
5940     isCellEditable : function(colIndex, rowIndex){
5941         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5942     },
5943
5944     /**
5945      * Returns the editor defined for the cell/column.
5946      * return false or null to disable editing.
5947      * @param {Number} colIndex The column index
5948      * @param {Number} rowIndex The row index
5949      * @return {Object}
5950      */
5951     getCellEditor : function(colIndex, rowIndex){
5952         return this.config[colIndex].editor;
5953     },
5954
5955     /**
5956      * Sets if a column is editable.
5957      * @param {Number} col The column index
5958      * @param {Boolean} editable True if the column is editable
5959      */
5960     setEditable : function(col, editable){
5961         this.config[col].editable = editable;
5962     },
5963
5964
5965     /**
5966      * Returns true if the column is hidden.
5967      * @param {Number} colIndex The column index
5968      * @return {Boolean}
5969      */
5970     isHidden : function(colIndex){
5971         return this.config[colIndex].hidden;
5972     },
5973
5974
5975     /**
5976      * Returns true if the column width cannot be changed
5977      */
5978     isFixed : function(colIndex){
5979         return this.config[colIndex].fixed;
5980     },
5981
5982     /**
5983      * Returns true if the column can be resized
5984      * @return {Boolean}
5985      */
5986     isResizable : function(colIndex){
5987         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5988     },
5989     /**
5990      * Sets if a column is hidden.
5991      * @param {Number} colIndex The column index
5992      * @param {Boolean} hidden True if the column is hidden
5993      */
5994     setHidden : function(colIndex, hidden){
5995         this.config[colIndex].hidden = hidden;
5996         this.totalWidth = null;
5997         this.fireEvent("hiddenchange", this, colIndex, hidden);
5998     },
5999
6000     /**
6001      * Sets the editor for a column.
6002      * @param {Number} col The column index
6003      * @param {Object} editor The editor object
6004      */
6005     setEditor : function(col, editor){
6006         this.config[col].editor = editor;
6007     }
6008 });
6009
6010 Roo.grid.ColumnModel.defaultRenderer = function(value)
6011 {
6012     if(typeof value == "object") {
6013         return value;
6014     }
6015         if(typeof value == "string" && value.length < 1){
6016             return "&#160;";
6017         }
6018     
6019         return String.format("{0}", value);
6020 };
6021
6022 // Alias for backwards compatibility
6023 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6024 /*
6025  * Based on:
6026  * Ext JS Library 1.1.1
6027  * Copyright(c) 2006-2007, Ext JS, LLC.
6028  *
6029  * Originally Released Under LGPL - original licence link has changed is not relivant.
6030  *
6031  * Fork - LGPL
6032  * <script type="text/javascript">
6033  */
6034  
6035 /**
6036  * @class Roo.LoadMask
6037  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6038  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6039  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6040  * element's UpdateManager load indicator and will be destroyed after the initial load.
6041  * @constructor
6042  * Create a new LoadMask
6043  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6044  * @param {Object} config The config object
6045  */
6046 Roo.LoadMask = function(el, config){
6047     this.el = Roo.get(el);
6048     Roo.apply(this, config);
6049     if(this.store){
6050         this.store.on('beforeload', this.onBeforeLoad, this);
6051         this.store.on('load', this.onLoad, this);
6052         this.store.on('loadexception', this.onLoadException, this);
6053         this.removeMask = false;
6054     }else{
6055         var um = this.el.getUpdateManager();
6056         um.showLoadIndicator = false; // disable the default indicator
6057         um.on('beforeupdate', this.onBeforeLoad, this);
6058         um.on('update', this.onLoad, this);
6059         um.on('failure', this.onLoad, this);
6060         this.removeMask = true;
6061     }
6062 };
6063
6064 Roo.LoadMask.prototype = {
6065     /**
6066      * @cfg {Boolean} removeMask
6067      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6068      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6069      */
6070     /**
6071      * @cfg {String} msg
6072      * The text to display in a centered loading message box (defaults to 'Loading...')
6073      */
6074     msg : 'Loading...',
6075     /**
6076      * @cfg {String} msgCls
6077      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6078      */
6079     msgCls : 'x-mask-loading',
6080
6081     /**
6082      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6083      * @type Boolean
6084      */
6085     disabled: false,
6086
6087     /**
6088      * Disables the mask to prevent it from being displayed
6089      */
6090     disable : function(){
6091        this.disabled = true;
6092     },
6093
6094     /**
6095      * Enables the mask so that it can be displayed
6096      */
6097     enable : function(){
6098         this.disabled = false;
6099     },
6100     
6101     onLoadException : function()
6102     {
6103         Roo.log(arguments);
6104         
6105         if (typeof(arguments[3]) != 'undefined') {
6106             Roo.MessageBox.alert("Error loading",arguments[3]);
6107         } 
6108         /*
6109         try {
6110             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6111                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6112             }   
6113         } catch(e) {
6114             
6115         }
6116         */
6117     
6118         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6119     },
6120     // private
6121     onLoad : function()
6122     {
6123         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6124     },
6125
6126     // private
6127     onBeforeLoad : function(){
6128         if(!this.disabled){
6129             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6130         }
6131     },
6132
6133     // private
6134     destroy : function(){
6135         if(this.store){
6136             this.store.un('beforeload', this.onBeforeLoad, this);
6137             this.store.un('load', this.onLoad, this);
6138             this.store.un('loadexception', this.onLoadException, this);
6139         }else{
6140             var um = this.el.getUpdateManager();
6141             um.un('beforeupdate', this.onBeforeLoad, this);
6142             um.un('update', this.onLoad, this);
6143             um.un('failure', this.onLoad, this);
6144         }
6145     }
6146 };/*
6147  * - LGPL
6148  *
6149  * table
6150  * 
6151  */
6152
6153 /**
6154  * @class Roo.bootstrap.Table
6155  * @extends Roo.bootstrap.Component
6156  * Bootstrap Table class
6157  * @cfg {String} cls table class
6158  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6159  * @cfg {String} bgcolor Specifies the background color for a table
6160  * @cfg {Number} border Specifies whether the table cells should have borders or not
6161  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6162  * @cfg {Number} cellspacing Specifies the space between cells
6163  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6164  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6165  * @cfg {String} sortable Specifies that the table should be sortable
6166  * @cfg {String} summary Specifies a summary of the content of a table
6167  * @cfg {Number} width Specifies the width of a table
6168  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6169  * 
6170  * @cfg {boolean} striped Should the rows be alternative striped
6171  * @cfg {boolean} bordered Add borders to the table
6172  * @cfg {boolean} hover Add hover highlighting
6173  * @cfg {boolean} condensed Format condensed
6174  * @cfg {boolean} responsive Format condensed
6175  * @cfg {Boolean} loadMask (true|false) default false
6176  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6177  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6178  * @cfg {Boolean} rowSelection (true|false) default false
6179  * @cfg {Boolean} cellSelection (true|false) default false
6180  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6181  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6182  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6183  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6184  
6185  * 
6186  * @constructor
6187  * Create a new Table
6188  * @param {Object} config The config object
6189  */
6190
6191 Roo.bootstrap.Table = function(config){
6192     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6193     
6194   
6195     
6196     // BC...
6197     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6198     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6199     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6200     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6201     
6202     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6203     if (this.sm) {
6204         this.sm.grid = this;
6205         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6206         this.sm = this.selModel;
6207         this.sm.xmodule = this.xmodule || false;
6208     }
6209     
6210     if (this.cm && typeof(this.cm.config) == 'undefined') {
6211         this.colModel = new Roo.grid.ColumnModel(this.cm);
6212         this.cm = this.colModel;
6213         this.cm.xmodule = this.xmodule || false;
6214     }
6215     if (this.store) {
6216         this.store= Roo.factory(this.store, Roo.data);
6217         this.ds = this.store;
6218         this.ds.xmodule = this.xmodule || false;
6219          
6220     }
6221     if (this.footer && this.store) {
6222         this.footer.dataSource = this.ds;
6223         this.footer = Roo.factory(this.footer);
6224     }
6225     
6226     /** @private */
6227     this.addEvents({
6228         /**
6229          * @event cellclick
6230          * Fires when a cell is clicked
6231          * @param {Roo.bootstrap.Table} this
6232          * @param {Roo.Element} el
6233          * @param {Number} rowIndex
6234          * @param {Number} columnIndex
6235          * @param {Roo.EventObject} e
6236          */
6237         "cellclick" : true,
6238         /**
6239          * @event celldblclick
6240          * Fires when a cell is double clicked
6241          * @param {Roo.bootstrap.Table} this
6242          * @param {Roo.Element} el
6243          * @param {Number} rowIndex
6244          * @param {Number} columnIndex
6245          * @param {Roo.EventObject} e
6246          */
6247         "celldblclick" : true,
6248         /**
6249          * @event rowclick
6250          * Fires when a row is clicked
6251          * @param {Roo.bootstrap.Table} this
6252          * @param {Roo.Element} el
6253          * @param {Number} rowIndex
6254          * @param {Roo.EventObject} e
6255          */
6256         "rowclick" : true,
6257         /**
6258          * @event rowdblclick
6259          * Fires when a row is double clicked
6260          * @param {Roo.bootstrap.Table} this
6261          * @param {Roo.Element} el
6262          * @param {Number} rowIndex
6263          * @param {Roo.EventObject} e
6264          */
6265         "rowdblclick" : true,
6266         /**
6267          * @event mouseover
6268          * Fires when a mouseover occur
6269          * @param {Roo.bootstrap.Table} this
6270          * @param {Roo.Element} el
6271          * @param {Number} rowIndex
6272          * @param {Number} columnIndex
6273          * @param {Roo.EventObject} e
6274          */
6275         "mouseover" : true,
6276         /**
6277          * @event mouseout
6278          * Fires when a mouseout occur
6279          * @param {Roo.bootstrap.Table} this
6280          * @param {Roo.Element} el
6281          * @param {Number} rowIndex
6282          * @param {Number} columnIndex
6283          * @param {Roo.EventObject} e
6284          */
6285         "mouseout" : true,
6286         /**
6287          * @event rowclass
6288          * Fires when a row is rendered, so you can change add a style to it.
6289          * @param {Roo.bootstrap.Table} this
6290          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6291          */
6292         'rowclass' : true,
6293           /**
6294          * @event rowsrendered
6295          * Fires when all the  rows have been rendered
6296          * @param {Roo.bootstrap.Table} this
6297          */
6298         'rowsrendered' : true,
6299         /**
6300          * @event contextmenu
6301          * The raw contextmenu event for the entire grid.
6302          * @param {Roo.EventObject} e
6303          */
6304         "contextmenu" : true,
6305         /**
6306          * @event rowcontextmenu
6307          * Fires when a row is right clicked
6308          * @param {Roo.bootstrap.Table} this
6309          * @param {Number} rowIndex
6310          * @param {Roo.EventObject} e
6311          */
6312         "rowcontextmenu" : true,
6313         /**
6314          * @event cellcontextmenu
6315          * Fires when a cell is right clicked
6316          * @param {Roo.bootstrap.Table} this
6317          * @param {Number} rowIndex
6318          * @param {Number} cellIndex
6319          * @param {Roo.EventObject} e
6320          */
6321          "cellcontextmenu" : true,
6322          /**
6323          * @event headercontextmenu
6324          * Fires when a header is right clicked
6325          * @param {Roo.bootstrap.Table} this
6326          * @param {Number} columnIndex
6327          * @param {Roo.EventObject} e
6328          */
6329         "headercontextmenu" : true
6330     });
6331 };
6332
6333 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6334     
6335     cls: false,
6336     align: false,
6337     bgcolor: false,
6338     border: false,
6339     cellpadding: false,
6340     cellspacing: false,
6341     frame: false,
6342     rules: false,
6343     sortable: false,
6344     summary: false,
6345     width: false,
6346     striped : false,
6347     scrollBody : false,
6348     bordered: false,
6349     hover:  false,
6350     condensed : false,
6351     responsive : false,
6352     sm : false,
6353     cm : false,
6354     store : false,
6355     loadMask : false,
6356     footerShow : true,
6357     headerShow : true,
6358   
6359     rowSelection : false,
6360     cellSelection : false,
6361     layout : false,
6362     
6363     // Roo.Element - the tbody
6364     mainBody: false,
6365     // Roo.Element - thead element
6366     mainHead: false,
6367     
6368     container: false, // used by gridpanel...
6369     
6370     lazyLoad : false,
6371     
6372     CSS : Roo.util.CSS,
6373     
6374     auto_hide_footer : false,
6375     
6376     getAutoCreate : function()
6377     {
6378         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6379         
6380         cfg = {
6381             tag: 'table',
6382             cls : 'table',
6383             cn : []
6384         };
6385         if (this.scrollBody) {
6386             cfg.cls += ' table-body-fixed';
6387         }    
6388         if (this.striped) {
6389             cfg.cls += ' table-striped';
6390         }
6391         
6392         if (this.hover) {
6393             cfg.cls += ' table-hover';
6394         }
6395         if (this.bordered) {
6396             cfg.cls += ' table-bordered';
6397         }
6398         if (this.condensed) {
6399             cfg.cls += ' table-condensed';
6400         }
6401         if (this.responsive) {
6402             cfg.cls += ' table-responsive';
6403         }
6404         
6405         if (this.cls) {
6406             cfg.cls+=  ' ' +this.cls;
6407         }
6408         
6409         // this lot should be simplifed...
6410         var _t = this;
6411         var cp = [
6412             'align',
6413             'bgcolor',
6414             'border',
6415             'cellpadding',
6416             'cellspacing',
6417             'frame',
6418             'rules',
6419             'sortable',
6420             'summary',
6421             'width'
6422         ].forEach(function(k) {
6423             if (_t[k]) {
6424                 cfg[k] = _t[k];
6425             }
6426         });
6427         
6428         
6429         if (this.layout) {
6430             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6431         }
6432         
6433         if(this.store || this.cm){
6434             if(this.headerShow){
6435                 cfg.cn.push(this.renderHeader());
6436             }
6437             
6438             cfg.cn.push(this.renderBody());
6439             
6440             if(this.footerShow){
6441                 cfg.cn.push(this.renderFooter());
6442             }
6443             // where does this come from?
6444             //cfg.cls+=  ' TableGrid';
6445         }
6446         
6447         return { cn : [ cfg ] };
6448     },
6449     
6450     initEvents : function()
6451     {   
6452         if(!this.store || !this.cm){
6453             return;
6454         }
6455         if (this.selModel) {
6456             this.selModel.initEvents();
6457         }
6458         
6459         
6460         //Roo.log('initEvents with ds!!!!');
6461         
6462         this.mainBody = this.el.select('tbody', true).first();
6463         this.mainHead = this.el.select('thead', true).first();
6464         this.mainFoot = this.el.select('tfoot', true).first();
6465         
6466         
6467         
6468         var _this = this;
6469         
6470         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6471             e.on('click', _this.sort, _this);
6472         });
6473         
6474         this.mainBody.on("click", this.onClick, this);
6475         this.mainBody.on("dblclick", this.onDblClick, this);
6476         
6477         // why is this done????? = it breaks dialogs??
6478         //this.parent().el.setStyle('position', 'relative');
6479         
6480         
6481         if (this.footer) {
6482             this.footer.parentId = this.id;
6483             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6484             
6485             if(this.lazyLoad){
6486                 this.el.select('tfoot tr td').first().addClass('hide');
6487             }
6488         } 
6489         
6490         if(this.loadMask) {
6491             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6492         }
6493         
6494         this.store.on('load', this.onLoad, this);
6495         this.store.on('beforeload', this.onBeforeLoad, this);
6496         this.store.on('update', this.onUpdate, this);
6497         this.store.on('add', this.onAdd, this);
6498         this.store.on("clear", this.clear, this);
6499         
6500         this.el.on("contextmenu", this.onContextMenu, this);
6501         
6502         this.mainBody.on('scroll', this.onBodyScroll, this);
6503         
6504         this.cm.on("headerchange", this.onHeaderChange, this);
6505         
6506         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6507         
6508     },
6509     
6510     onContextMenu : function(e, t)
6511     {
6512         this.processEvent("contextmenu", e);
6513     },
6514     
6515     processEvent : function(name, e)
6516     {
6517         if (name != 'touchstart' ) {
6518             this.fireEvent(name, e);    
6519         }
6520         
6521         var t = e.getTarget();
6522         
6523         var cell = Roo.get(t);
6524         
6525         if(!cell){
6526             return;
6527         }
6528         
6529         if(cell.findParent('tfoot', false, true)){
6530             return;
6531         }
6532         
6533         if(cell.findParent('thead', false, true)){
6534             
6535             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6536                 cell = Roo.get(t).findParent('th', false, true);
6537                 if (!cell) {
6538                     Roo.log("failed to find th in thead?");
6539                     Roo.log(e.getTarget());
6540                     return;
6541                 }
6542             }
6543             
6544             var cellIndex = cell.dom.cellIndex;
6545             
6546             var ename = name == 'touchstart' ? 'click' : name;
6547             this.fireEvent("header" + ename, this, cellIndex, e);
6548             
6549             return;
6550         }
6551         
6552         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6553             cell = Roo.get(t).findParent('td', false, true);
6554             if (!cell) {
6555                 Roo.log("failed to find th in tbody?");
6556                 Roo.log(e.getTarget());
6557                 return;
6558             }
6559         }
6560         
6561         var row = cell.findParent('tr', false, true);
6562         var cellIndex = cell.dom.cellIndex;
6563         var rowIndex = row.dom.rowIndex - 1;
6564         
6565         if(row !== false){
6566             
6567             this.fireEvent("row" + name, this, rowIndex, e);
6568             
6569             if(cell !== false){
6570             
6571                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6572             }
6573         }
6574         
6575     },
6576     
6577     onMouseover : function(e, el)
6578     {
6579         var cell = Roo.get(el);
6580         
6581         if(!cell){
6582             return;
6583         }
6584         
6585         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6586             cell = cell.findParent('td', false, true);
6587         }
6588         
6589         var row = cell.findParent('tr', false, true);
6590         var cellIndex = cell.dom.cellIndex;
6591         var rowIndex = row.dom.rowIndex - 1; // start from 0
6592         
6593         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6594         
6595     },
6596     
6597     onMouseout : function(e, el)
6598     {
6599         var cell = Roo.get(el);
6600         
6601         if(!cell){
6602             return;
6603         }
6604         
6605         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6606             cell = cell.findParent('td', false, true);
6607         }
6608         
6609         var row = cell.findParent('tr', false, true);
6610         var cellIndex = cell.dom.cellIndex;
6611         var rowIndex = row.dom.rowIndex - 1; // start from 0
6612         
6613         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6614         
6615     },
6616     
6617     onClick : function(e, el)
6618     {
6619         var cell = Roo.get(el);
6620         
6621         if(!cell || (!this.cellSelection && !this.rowSelection)){
6622             return;
6623         }
6624         
6625         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6626             cell = cell.findParent('td', false, true);
6627         }
6628         
6629         if(!cell || typeof(cell) == 'undefined'){
6630             return;
6631         }
6632         
6633         var row = cell.findParent('tr', false, true);
6634         
6635         if(!row || typeof(row) == 'undefined'){
6636             return;
6637         }
6638         
6639         var cellIndex = cell.dom.cellIndex;
6640         var rowIndex = this.getRowIndex(row);
6641         
6642         // why??? - should these not be based on SelectionModel?
6643         if(this.cellSelection){
6644             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6645         }
6646         
6647         if(this.rowSelection){
6648             this.fireEvent('rowclick', this, row, rowIndex, e);
6649         }
6650         
6651         
6652     },
6653         
6654     onDblClick : function(e,el)
6655     {
6656         var cell = Roo.get(el);
6657         
6658         if(!cell || (!this.cellSelection && !this.rowSelection)){
6659             return;
6660         }
6661         
6662         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6663             cell = cell.findParent('td', false, true);
6664         }
6665         
6666         if(!cell || typeof(cell) == 'undefined'){
6667             return;
6668         }
6669         
6670         var row = cell.findParent('tr', false, true);
6671         
6672         if(!row || typeof(row) == 'undefined'){
6673             return;
6674         }
6675         
6676         var cellIndex = cell.dom.cellIndex;
6677         var rowIndex = this.getRowIndex(row);
6678         
6679         if(this.cellSelection){
6680             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6681         }
6682         
6683         if(this.rowSelection){
6684             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6685         }
6686     },
6687     
6688     sort : function(e,el)
6689     {
6690         var col = Roo.get(el);
6691         
6692         if(!col.hasClass('sortable')){
6693             return;
6694         }
6695         
6696         var sort = col.attr('sort');
6697         var dir = 'ASC';
6698         
6699         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6700             dir = 'DESC';
6701         }
6702         
6703         this.store.sortInfo = {field : sort, direction : dir};
6704         
6705         if (this.footer) {
6706             Roo.log("calling footer first");
6707             this.footer.onClick('first');
6708         } else {
6709         
6710             this.store.load({ params : { start : 0 } });
6711         }
6712     },
6713     
6714     renderHeader : function()
6715     {
6716         var header = {
6717             tag: 'thead',
6718             cn : []
6719         };
6720         
6721         var cm = this.cm;
6722         this.totalWidth = 0;
6723         
6724         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6725             
6726             var config = cm.config[i];
6727             
6728             var c = {
6729                 tag: 'th',
6730                 cls : 'x-hcol-' + i,
6731                 style : '',
6732                 html: cm.getColumnHeader(i)
6733             };
6734             
6735             var hh = '';
6736             
6737             if(typeof(config.sortable) != 'undefined' && config.sortable){
6738                 c.cls = 'sortable';
6739                 c.html = '<i class="glyphicon"></i>' + c.html;
6740             }
6741             
6742             if(typeof(config.lgHeader) != 'undefined'){
6743                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6744             }
6745             
6746             if(typeof(config.mdHeader) != 'undefined'){
6747                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6748             }
6749             
6750             if(typeof(config.smHeader) != 'undefined'){
6751                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6752             }
6753             
6754             if(typeof(config.xsHeader) != 'undefined'){
6755                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6756             }
6757             
6758             if(hh.length){
6759                 c.html = hh;
6760             }
6761             
6762             if(typeof(config.tooltip) != 'undefined'){
6763                 c.tooltip = config.tooltip;
6764             }
6765             
6766             if(typeof(config.colspan) != 'undefined'){
6767                 c.colspan = config.colspan;
6768             }
6769             
6770             if(typeof(config.hidden) != 'undefined' && config.hidden){
6771                 c.style += ' display:none;';
6772             }
6773             
6774             if(typeof(config.dataIndex) != 'undefined'){
6775                 c.sort = config.dataIndex;
6776             }
6777             
6778            
6779             
6780             if(typeof(config.align) != 'undefined' && config.align.length){
6781                 c.style += ' text-align:' + config.align + ';';
6782             }
6783             
6784             if(typeof(config.width) != 'undefined'){
6785                 c.style += ' width:' + config.width + 'px;';
6786                 this.totalWidth += config.width;
6787             } else {
6788                 this.totalWidth += 100; // assume minimum of 100 per column?
6789             }
6790             
6791             if(typeof(config.cls) != 'undefined'){
6792                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6793             }
6794             
6795             ['xs','sm','md','lg'].map(function(size){
6796                 
6797                 if(typeof(config[size]) == 'undefined'){
6798                     return;
6799                 }
6800                 
6801                 if (!config[size]) { // 0 = hidden
6802                     c.cls += ' hidden-' + size;
6803                     return;
6804                 }
6805                 
6806                 c.cls += ' col-' + size + '-' + config[size];
6807
6808             });
6809             
6810             header.cn.push(c)
6811         }
6812         
6813         return header;
6814     },
6815     
6816     renderBody : function()
6817     {
6818         var body = {
6819             tag: 'tbody',
6820             cn : [
6821                 {
6822                     tag: 'tr',
6823                     cn : [
6824                         {
6825                             tag : 'td',
6826                             colspan :  this.cm.getColumnCount()
6827                         }
6828                     ]
6829                 }
6830             ]
6831         };
6832         
6833         return body;
6834     },
6835     
6836     renderFooter : function()
6837     {
6838         var footer = {
6839             tag: 'tfoot',
6840             cn : [
6841                 {
6842                     tag: 'tr',
6843                     cn : [
6844                         {
6845                             tag : 'td',
6846                             colspan :  this.cm.getColumnCount()
6847                         }
6848                     ]
6849                 }
6850             ]
6851         };
6852         
6853         return footer;
6854     },
6855     
6856     
6857     
6858     onLoad : function()
6859     {
6860 //        Roo.log('ds onload');
6861         this.clear();
6862         
6863         var _this = this;
6864         var cm = this.cm;
6865         var ds = this.store;
6866         
6867         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6868             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6869             if (_this.store.sortInfo) {
6870                     
6871                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6872                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6873                 }
6874                 
6875                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6876                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6877                 }
6878             }
6879         });
6880         
6881         var tbody =  this.mainBody;
6882               
6883         if(ds.getCount() > 0){
6884             ds.data.each(function(d,rowIndex){
6885                 var row =  this.renderRow(cm, ds, rowIndex);
6886                 
6887                 tbody.createChild(row);
6888                 
6889                 var _this = this;
6890                 
6891                 if(row.cellObjects.length){
6892                     Roo.each(row.cellObjects, function(r){
6893                         _this.renderCellObject(r);
6894                     })
6895                 }
6896                 
6897             }, this);
6898         }
6899         
6900         var tfoot = this.el.select('tfoot', true).first();
6901         
6902         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6903             
6904             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6905             
6906             var total = this.ds.getTotalCount();
6907             
6908             if(this.footer.pageSize < total){
6909                 this.mainFoot.show();
6910             }
6911         }
6912         
6913         Roo.each(this.el.select('tbody td', true).elements, function(e){
6914             e.on('mouseover', _this.onMouseover, _this);
6915         });
6916         
6917         Roo.each(this.el.select('tbody td', true).elements, function(e){
6918             e.on('mouseout', _this.onMouseout, _this);
6919         });
6920         this.fireEvent('rowsrendered', this);
6921         
6922         this.autoSize();
6923     },
6924     
6925     
6926     onUpdate : function(ds,record)
6927     {
6928         this.refreshRow(record);
6929         this.autoSize();
6930     },
6931     
6932     onRemove : function(ds, record, index, isUpdate){
6933         if(isUpdate !== true){
6934             this.fireEvent("beforerowremoved", this, index, record);
6935         }
6936         var bt = this.mainBody.dom;
6937         
6938         var rows = this.el.select('tbody > tr', true).elements;
6939         
6940         if(typeof(rows[index]) != 'undefined'){
6941             bt.removeChild(rows[index].dom);
6942         }
6943         
6944 //        if(bt.rows[index]){
6945 //            bt.removeChild(bt.rows[index]);
6946 //        }
6947         
6948         if(isUpdate !== true){
6949             //this.stripeRows(index);
6950             //this.syncRowHeights(index, index);
6951             //this.layout();
6952             this.fireEvent("rowremoved", this, index, record);
6953         }
6954     },
6955     
6956     onAdd : function(ds, records, rowIndex)
6957     {
6958         //Roo.log('on Add called');
6959         // - note this does not handle multiple adding very well..
6960         var bt = this.mainBody.dom;
6961         for (var i =0 ; i < records.length;i++) {
6962             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6963             //Roo.log(records[i]);
6964             //Roo.log(this.store.getAt(rowIndex+i));
6965             this.insertRow(this.store, rowIndex + i, false);
6966             return;
6967         }
6968         
6969     },
6970     
6971     
6972     refreshRow : function(record){
6973         var ds = this.store, index;
6974         if(typeof record == 'number'){
6975             index = record;
6976             record = ds.getAt(index);
6977         }else{
6978             index = ds.indexOf(record);
6979         }
6980         this.insertRow(ds, index, true);
6981         this.autoSize();
6982         this.onRemove(ds, record, index+1, true);
6983         this.autoSize();
6984         //this.syncRowHeights(index, index);
6985         //this.layout();
6986         this.fireEvent("rowupdated", this, index, record);
6987     },
6988     
6989     insertRow : function(dm, rowIndex, isUpdate){
6990         
6991         if(!isUpdate){
6992             this.fireEvent("beforerowsinserted", this, rowIndex);
6993         }
6994             //var s = this.getScrollState();
6995         var row = this.renderRow(this.cm, this.store, rowIndex);
6996         // insert before rowIndex..
6997         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6998         
6999         var _this = this;
7000                 
7001         if(row.cellObjects.length){
7002             Roo.each(row.cellObjects, function(r){
7003                 _this.renderCellObject(r);
7004             })
7005         }
7006             
7007         if(!isUpdate){
7008             this.fireEvent("rowsinserted", this, rowIndex);
7009             //this.syncRowHeights(firstRow, lastRow);
7010             //this.stripeRows(firstRow);
7011             //this.layout();
7012         }
7013         
7014     },
7015     
7016     
7017     getRowDom : function(rowIndex)
7018     {
7019         var rows = this.el.select('tbody > tr', true).elements;
7020         
7021         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7022         
7023     },
7024     // returns the object tree for a tr..
7025   
7026     
7027     renderRow : function(cm, ds, rowIndex) 
7028     {
7029         var d = ds.getAt(rowIndex);
7030         
7031         var row = {
7032             tag : 'tr',
7033             cls : 'x-row-' + rowIndex,
7034             cn : []
7035         };
7036             
7037         var cellObjects = [];
7038         
7039         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7040             var config = cm.config[i];
7041             
7042             var renderer = cm.getRenderer(i);
7043             var value = '';
7044             var id = false;
7045             
7046             if(typeof(renderer) !== 'undefined'){
7047                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7048             }
7049             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7050             // and are rendered into the cells after the row is rendered - using the id for the element.
7051             
7052             if(typeof(value) === 'object'){
7053                 id = Roo.id();
7054                 cellObjects.push({
7055                     container : id,
7056                     cfg : value 
7057                 })
7058             }
7059             
7060             var rowcfg = {
7061                 record: d,
7062                 rowIndex : rowIndex,
7063                 colIndex : i,
7064                 rowClass : ''
7065             };
7066
7067             this.fireEvent('rowclass', this, rowcfg);
7068             
7069             var td = {
7070                 tag: 'td',
7071                 cls : rowcfg.rowClass + ' x-col-' + i,
7072                 style: '',
7073                 html: (typeof(value) === 'object') ? '' : value
7074             };
7075             
7076             if (id) {
7077                 td.id = id;
7078             }
7079             
7080             if(typeof(config.colspan) != 'undefined'){
7081                 td.colspan = config.colspan;
7082             }
7083             
7084             if(typeof(config.hidden) != 'undefined' && config.hidden){
7085                 td.style += ' display:none;';
7086             }
7087             
7088             if(typeof(config.align) != 'undefined' && config.align.length){
7089                 td.style += ' text-align:' + config.align + ';';
7090             }
7091             if(typeof(config.valign) != 'undefined' && config.valign.length){
7092                 td.style += ' vertical-align:' + config.valign + ';';
7093             }
7094             
7095             if(typeof(config.width) != 'undefined'){
7096                 td.style += ' width:' +  config.width + 'px;';
7097             }
7098             
7099             if(typeof(config.cursor) != 'undefined'){
7100                 td.style += ' cursor:' +  config.cursor + ';';
7101             }
7102             
7103             if(typeof(config.cls) != 'undefined'){
7104                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7105             }
7106             
7107             ['xs','sm','md','lg'].map(function(size){
7108                 
7109                 if(typeof(config[size]) == 'undefined'){
7110                     return;
7111                 }
7112                 
7113                 if (!config[size]) { // 0 = hidden
7114                     td.cls += ' hidden-' + size;
7115                     return;
7116                 }
7117                 
7118                 td.cls += ' col-' + size + '-' + config[size];
7119
7120             });
7121             
7122             row.cn.push(td);
7123            
7124         }
7125         
7126         row.cellObjects = cellObjects;
7127         
7128         return row;
7129           
7130     },
7131     
7132     
7133     
7134     onBeforeLoad : function()
7135     {
7136         
7137     },
7138      /**
7139      * Remove all rows
7140      */
7141     clear : function()
7142     {
7143         this.el.select('tbody', true).first().dom.innerHTML = '';
7144     },
7145     /**
7146      * Show or hide a row.
7147      * @param {Number} rowIndex to show or hide
7148      * @param {Boolean} state hide
7149      */
7150     setRowVisibility : function(rowIndex, state)
7151     {
7152         var bt = this.mainBody.dom;
7153         
7154         var rows = this.el.select('tbody > tr', true).elements;
7155         
7156         if(typeof(rows[rowIndex]) == 'undefined'){
7157             return;
7158         }
7159         rows[rowIndex].dom.style.display = state ? '' : 'none';
7160     },
7161     
7162     
7163     getSelectionModel : function(){
7164         if(!this.selModel){
7165             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7166         }
7167         return this.selModel;
7168     },
7169     /*
7170      * Render the Roo.bootstrap object from renderder
7171      */
7172     renderCellObject : function(r)
7173     {
7174         var _this = this;
7175         
7176         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7177         
7178         var t = r.cfg.render(r.container);
7179         
7180         if(r.cfg.cn){
7181             Roo.each(r.cfg.cn, function(c){
7182                 var child = {
7183                     container: t.getChildContainer(),
7184                     cfg: c
7185                 };
7186                 _this.renderCellObject(child);
7187             })
7188         }
7189     },
7190     
7191     getRowIndex : function(row)
7192     {
7193         var rowIndex = -1;
7194         
7195         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7196             if(el != row){
7197                 return;
7198             }
7199             
7200             rowIndex = index;
7201         });
7202         
7203         return rowIndex;
7204     },
7205      /**
7206      * Returns the grid's underlying element = used by panel.Grid
7207      * @return {Element} The element
7208      */
7209     getGridEl : function(){
7210         return this.el;
7211     },
7212      /**
7213      * Forces a resize - used by panel.Grid
7214      * @return {Element} The element
7215      */
7216     autoSize : function()
7217     {
7218         //var ctr = Roo.get(this.container.dom.parentElement);
7219         var ctr = Roo.get(this.el.dom);
7220         
7221         var thd = this.getGridEl().select('thead',true).first();
7222         var tbd = this.getGridEl().select('tbody', true).first();
7223         var tfd = this.getGridEl().select('tfoot', true).first();
7224         
7225         var cw = ctr.getWidth();
7226         
7227         if (tbd) {
7228             
7229             tbd.setSize(ctr.getWidth(),
7230                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7231             );
7232             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7233             cw -= barsize;
7234         }
7235         cw = Math.max(cw, this.totalWidth);
7236         this.getGridEl().select('tr',true).setWidth(cw);
7237         // resize 'expandable coloumn?
7238         
7239         return; // we doe not have a view in this design..
7240         
7241     },
7242     onBodyScroll: function()
7243     {
7244         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7245         if(this.mainHead){
7246             this.mainHead.setStyle({
7247                 'position' : 'relative',
7248                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7249             });
7250         }
7251         
7252         if(this.lazyLoad){
7253             
7254             var scrollHeight = this.mainBody.dom.scrollHeight;
7255             
7256             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7257             
7258             var height = this.mainBody.getHeight();
7259             
7260             if(scrollHeight - height == scrollTop) {
7261                 
7262                 var total = this.ds.getTotalCount();
7263                 
7264                 if(this.footer.cursor + this.footer.pageSize < total){
7265                     
7266                     this.footer.ds.load({
7267                         params : {
7268                             start : this.footer.cursor + this.footer.pageSize,
7269                             limit : this.footer.pageSize
7270                         },
7271                         add : true
7272                     });
7273                 }
7274             }
7275             
7276         }
7277     },
7278     
7279     onHeaderChange : function()
7280     {
7281         var header = this.renderHeader();
7282         var table = this.el.select('table', true).first();
7283         
7284         this.mainHead.remove();
7285         this.mainHead = table.createChild(header, this.mainBody, false);
7286     },
7287     
7288     onHiddenChange : function(colModel, colIndex, hidden)
7289     {
7290         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7291         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7292         
7293         this.CSS.updateRule(thSelector, "display", "");
7294         this.CSS.updateRule(tdSelector, "display", "");
7295         
7296         if(hidden){
7297             this.CSS.updateRule(thSelector, "display", "none");
7298             this.CSS.updateRule(tdSelector, "display", "none");
7299         }
7300         
7301         this.onHeaderChange();
7302         this.onLoad();
7303     },
7304     
7305     setColumnWidth: function(col_index, width)
7306     {
7307         // width = "md-2 xs-2..."
7308         if(!this.colModel.config[col_index]) {
7309             return;
7310         }
7311         
7312         var w = width.split(" ");
7313         
7314         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7315         
7316         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7317         
7318         
7319         for(var j = 0; j < w.length; j++) {
7320             
7321             if(!w[j]) {
7322                 continue;
7323             }
7324             
7325             var size_cls = w[j].split("-");
7326             
7327             if(!Number.isInteger(size_cls[1] * 1)) {
7328                 continue;
7329             }
7330             
7331             if(!this.colModel.config[col_index][size_cls[0]]) {
7332                 continue;
7333             }
7334             
7335             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7336                 continue;
7337             }
7338             
7339             h_row[0].classList.replace(
7340                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7341                 "col-"+size_cls[0]+"-"+size_cls[1]
7342             );
7343             
7344             for(var i = 0; i < rows.length; i++) {
7345                 
7346                 var size_cls = w[j].split("-");
7347                 
7348                 if(!Number.isInteger(size_cls[1] * 1)) {
7349                     continue;
7350                 }
7351                 
7352                 if(!this.colModel.config[col_index][size_cls[0]]) {
7353                     continue;
7354                 }
7355                 
7356                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7357                     continue;
7358                 }
7359                 
7360                 rows[i].classList.replace(
7361                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7362                     "col-"+size_cls[0]+"-"+size_cls[1]
7363                 );
7364             }
7365             
7366             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7367         }
7368     }
7369 });
7370
7371  
7372
7373  /*
7374  * - LGPL
7375  *
7376  * table cell
7377  * 
7378  */
7379
7380 /**
7381  * @class Roo.bootstrap.TableCell
7382  * @extends Roo.bootstrap.Component
7383  * Bootstrap TableCell class
7384  * @cfg {String} html cell contain text
7385  * @cfg {String} cls cell class
7386  * @cfg {String} tag cell tag (td|th) default td
7387  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7388  * @cfg {String} align Aligns the content in a cell
7389  * @cfg {String} axis Categorizes cells
7390  * @cfg {String} bgcolor Specifies the background color of a cell
7391  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7392  * @cfg {Number} colspan Specifies the number of columns a cell should span
7393  * @cfg {String} headers Specifies one or more header cells a cell is related to
7394  * @cfg {Number} height Sets the height of a cell
7395  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7396  * @cfg {Number} rowspan Sets the number of rows a cell should span
7397  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7398  * @cfg {String} valign Vertical aligns the content in a cell
7399  * @cfg {Number} width Specifies the width of a cell
7400  * 
7401  * @constructor
7402  * Create a new TableCell
7403  * @param {Object} config The config object
7404  */
7405
7406 Roo.bootstrap.TableCell = function(config){
7407     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7408 };
7409
7410 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7411     
7412     html: false,
7413     cls: false,
7414     tag: false,
7415     abbr: false,
7416     align: false,
7417     axis: false,
7418     bgcolor: false,
7419     charoff: false,
7420     colspan: false,
7421     headers: false,
7422     height: false,
7423     nowrap: false,
7424     rowspan: false,
7425     scope: false,
7426     valign: false,
7427     width: false,
7428     
7429     
7430     getAutoCreate : function(){
7431         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7432         
7433         cfg = {
7434             tag: 'td'
7435         };
7436         
7437         if(this.tag){
7438             cfg.tag = this.tag;
7439         }
7440         
7441         if (this.html) {
7442             cfg.html=this.html
7443         }
7444         if (this.cls) {
7445             cfg.cls=this.cls
7446         }
7447         if (this.abbr) {
7448             cfg.abbr=this.abbr
7449         }
7450         if (this.align) {
7451             cfg.align=this.align
7452         }
7453         if (this.axis) {
7454             cfg.axis=this.axis
7455         }
7456         if (this.bgcolor) {
7457             cfg.bgcolor=this.bgcolor
7458         }
7459         if (this.charoff) {
7460             cfg.charoff=this.charoff
7461         }
7462         if (this.colspan) {
7463             cfg.colspan=this.colspan
7464         }
7465         if (this.headers) {
7466             cfg.headers=this.headers
7467         }
7468         if (this.height) {
7469             cfg.height=this.height
7470         }
7471         if (this.nowrap) {
7472             cfg.nowrap=this.nowrap
7473         }
7474         if (this.rowspan) {
7475             cfg.rowspan=this.rowspan
7476         }
7477         if (this.scope) {
7478             cfg.scope=this.scope
7479         }
7480         if (this.valign) {
7481             cfg.valign=this.valign
7482         }
7483         if (this.width) {
7484             cfg.width=this.width
7485         }
7486         
7487         
7488         return cfg;
7489     }
7490    
7491 });
7492
7493  
7494
7495  /*
7496  * - LGPL
7497  *
7498  * table row
7499  * 
7500  */
7501
7502 /**
7503  * @class Roo.bootstrap.TableRow
7504  * @extends Roo.bootstrap.Component
7505  * Bootstrap TableRow class
7506  * @cfg {String} cls row class
7507  * @cfg {String} align Aligns the content in a table row
7508  * @cfg {String} bgcolor Specifies a background color for a table row
7509  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7510  * @cfg {String} valign Vertical aligns the content in a table row
7511  * 
7512  * @constructor
7513  * Create a new TableRow
7514  * @param {Object} config The config object
7515  */
7516
7517 Roo.bootstrap.TableRow = function(config){
7518     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7519 };
7520
7521 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7522     
7523     cls: false,
7524     align: false,
7525     bgcolor: false,
7526     charoff: false,
7527     valign: false,
7528     
7529     getAutoCreate : function(){
7530         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7531         
7532         cfg = {
7533             tag: 'tr'
7534         };
7535             
7536         if(this.cls){
7537             cfg.cls = this.cls;
7538         }
7539         if(this.align){
7540             cfg.align = this.align;
7541         }
7542         if(this.bgcolor){
7543             cfg.bgcolor = this.bgcolor;
7544         }
7545         if(this.charoff){
7546             cfg.charoff = this.charoff;
7547         }
7548         if(this.valign){
7549             cfg.valign = this.valign;
7550         }
7551         
7552         return cfg;
7553     }
7554    
7555 });
7556
7557  
7558
7559  /*
7560  * - LGPL
7561  *
7562  * table body
7563  * 
7564  */
7565
7566 /**
7567  * @class Roo.bootstrap.TableBody
7568  * @extends Roo.bootstrap.Component
7569  * Bootstrap TableBody class
7570  * @cfg {String} cls element class
7571  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7572  * @cfg {String} align Aligns the content inside the element
7573  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7574  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7575  * 
7576  * @constructor
7577  * Create a new TableBody
7578  * @param {Object} config The config object
7579  */
7580
7581 Roo.bootstrap.TableBody = function(config){
7582     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7583 };
7584
7585 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7586     
7587     cls: false,
7588     tag: false,
7589     align: false,
7590     charoff: false,
7591     valign: false,
7592     
7593     getAutoCreate : function(){
7594         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7595         
7596         cfg = {
7597             tag: 'tbody'
7598         };
7599             
7600         if (this.cls) {
7601             cfg.cls=this.cls
7602         }
7603         if(this.tag){
7604             cfg.tag = this.tag;
7605         }
7606         
7607         if(this.align){
7608             cfg.align = this.align;
7609         }
7610         if(this.charoff){
7611             cfg.charoff = this.charoff;
7612         }
7613         if(this.valign){
7614             cfg.valign = this.valign;
7615         }
7616         
7617         return cfg;
7618     }
7619     
7620     
7621 //    initEvents : function()
7622 //    {
7623 //        
7624 //        if(!this.store){
7625 //            return;
7626 //        }
7627 //        
7628 //        this.store = Roo.factory(this.store, Roo.data);
7629 //        this.store.on('load', this.onLoad, this);
7630 //        
7631 //        this.store.load();
7632 //        
7633 //    },
7634 //    
7635 //    onLoad: function () 
7636 //    {   
7637 //        this.fireEvent('load', this);
7638 //    }
7639 //    
7640 //   
7641 });
7642
7643  
7644
7645  /*
7646  * Based on:
7647  * Ext JS Library 1.1.1
7648  * Copyright(c) 2006-2007, Ext JS, LLC.
7649  *
7650  * Originally Released Under LGPL - original licence link has changed is not relivant.
7651  *
7652  * Fork - LGPL
7653  * <script type="text/javascript">
7654  */
7655
7656 // as we use this in bootstrap.
7657 Roo.namespace('Roo.form');
7658  /**
7659  * @class Roo.form.Action
7660  * Internal Class used to handle form actions
7661  * @constructor
7662  * @param {Roo.form.BasicForm} el The form element or its id
7663  * @param {Object} config Configuration options
7664  */
7665
7666  
7667  
7668 // define the action interface
7669 Roo.form.Action = function(form, options){
7670     this.form = form;
7671     this.options = options || {};
7672 };
7673 /**
7674  * Client Validation Failed
7675  * @const 
7676  */
7677 Roo.form.Action.CLIENT_INVALID = 'client';
7678 /**
7679  * Server Validation Failed
7680  * @const 
7681  */
7682 Roo.form.Action.SERVER_INVALID = 'server';
7683  /**
7684  * Connect to Server Failed
7685  * @const 
7686  */
7687 Roo.form.Action.CONNECT_FAILURE = 'connect';
7688 /**
7689  * Reading Data from Server Failed
7690  * @const 
7691  */
7692 Roo.form.Action.LOAD_FAILURE = 'load';
7693
7694 Roo.form.Action.prototype = {
7695     type : 'default',
7696     failureType : undefined,
7697     response : undefined,
7698     result : undefined,
7699
7700     // interface method
7701     run : function(options){
7702
7703     },
7704
7705     // interface method
7706     success : function(response){
7707
7708     },
7709
7710     // interface method
7711     handleResponse : function(response){
7712
7713     },
7714
7715     // default connection failure
7716     failure : function(response){
7717         
7718         this.response = response;
7719         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7720         this.form.afterAction(this, false);
7721     },
7722
7723     processResponse : function(response){
7724         this.response = response;
7725         if(!response.responseText){
7726             return true;
7727         }
7728         this.result = this.handleResponse(response);
7729         return this.result;
7730     },
7731
7732     // utility functions used internally
7733     getUrl : function(appendParams){
7734         var url = this.options.url || this.form.url || this.form.el.dom.action;
7735         if(appendParams){
7736             var p = this.getParams();
7737             if(p){
7738                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7739             }
7740         }
7741         return url;
7742     },
7743
7744     getMethod : function(){
7745         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7746     },
7747
7748     getParams : function(){
7749         var bp = this.form.baseParams;
7750         var p = this.options.params;
7751         if(p){
7752             if(typeof p == "object"){
7753                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7754             }else if(typeof p == 'string' && bp){
7755                 p += '&' + Roo.urlEncode(bp);
7756             }
7757         }else if(bp){
7758             p = Roo.urlEncode(bp);
7759         }
7760         return p;
7761     },
7762
7763     createCallback : function(){
7764         return {
7765             success: this.success,
7766             failure: this.failure,
7767             scope: this,
7768             timeout: (this.form.timeout*1000),
7769             upload: this.form.fileUpload ? this.success : undefined
7770         };
7771     }
7772 };
7773
7774 Roo.form.Action.Submit = function(form, options){
7775     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7776 };
7777
7778 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7779     type : 'submit',
7780
7781     haveProgress : false,
7782     uploadComplete : false,
7783     
7784     // uploadProgress indicator.
7785     uploadProgress : function()
7786     {
7787         if (!this.form.progressUrl) {
7788             return;
7789         }
7790         
7791         if (!this.haveProgress) {
7792             Roo.MessageBox.progress("Uploading", "Uploading");
7793         }
7794         if (this.uploadComplete) {
7795            Roo.MessageBox.hide();
7796            return;
7797         }
7798         
7799         this.haveProgress = true;
7800    
7801         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7802         
7803         var c = new Roo.data.Connection();
7804         c.request({
7805             url : this.form.progressUrl,
7806             params: {
7807                 id : uid
7808             },
7809             method: 'GET',
7810             success : function(req){
7811                //console.log(data);
7812                 var rdata = false;
7813                 var edata;
7814                 try  {
7815                    rdata = Roo.decode(req.responseText)
7816                 } catch (e) {
7817                     Roo.log("Invalid data from server..");
7818                     Roo.log(edata);
7819                     return;
7820                 }
7821                 if (!rdata || !rdata.success) {
7822                     Roo.log(rdata);
7823                     Roo.MessageBox.alert(Roo.encode(rdata));
7824                     return;
7825                 }
7826                 var data = rdata.data;
7827                 
7828                 if (this.uploadComplete) {
7829                    Roo.MessageBox.hide();
7830                    return;
7831                 }
7832                    
7833                 if (data){
7834                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7835                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7836                     );
7837                 }
7838                 this.uploadProgress.defer(2000,this);
7839             },
7840        
7841             failure: function(data) {
7842                 Roo.log('progress url failed ');
7843                 Roo.log(data);
7844             },
7845             scope : this
7846         });
7847            
7848     },
7849     
7850     
7851     run : function()
7852     {
7853         // run get Values on the form, so it syncs any secondary forms.
7854         this.form.getValues();
7855         
7856         var o = this.options;
7857         var method = this.getMethod();
7858         var isPost = method == 'POST';
7859         if(o.clientValidation === false || this.form.isValid()){
7860             
7861             if (this.form.progressUrl) {
7862                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7863                     (new Date() * 1) + '' + Math.random());
7864                     
7865             } 
7866             
7867             
7868             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7869                 form:this.form.el.dom,
7870                 url:this.getUrl(!isPost),
7871                 method: method,
7872                 params:isPost ? this.getParams() : null,
7873                 isUpload: this.form.fileUpload
7874             }));
7875             
7876             this.uploadProgress();
7877
7878         }else if (o.clientValidation !== false){ // client validation failed
7879             this.failureType = Roo.form.Action.CLIENT_INVALID;
7880             this.form.afterAction(this, false);
7881         }
7882     },
7883
7884     success : function(response)
7885     {
7886         this.uploadComplete= true;
7887         if (this.haveProgress) {
7888             Roo.MessageBox.hide();
7889         }
7890         
7891         
7892         var result = this.processResponse(response);
7893         if(result === true || result.success){
7894             this.form.afterAction(this, true);
7895             return;
7896         }
7897         if(result.errors){
7898             this.form.markInvalid(result.errors);
7899             this.failureType = Roo.form.Action.SERVER_INVALID;
7900         }
7901         this.form.afterAction(this, false);
7902     },
7903     failure : function(response)
7904     {
7905         this.uploadComplete= true;
7906         if (this.haveProgress) {
7907             Roo.MessageBox.hide();
7908         }
7909         
7910         this.response = response;
7911         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7912         this.form.afterAction(this, false);
7913     },
7914     
7915     handleResponse : function(response){
7916         if(this.form.errorReader){
7917             var rs = this.form.errorReader.read(response);
7918             var errors = [];
7919             if(rs.records){
7920                 for(var i = 0, len = rs.records.length; i < len; i++) {
7921                     var r = rs.records[i];
7922                     errors[i] = r.data;
7923                 }
7924             }
7925             if(errors.length < 1){
7926                 errors = null;
7927             }
7928             return {
7929                 success : rs.success,
7930                 errors : errors
7931             };
7932         }
7933         var ret = false;
7934         try {
7935             ret = Roo.decode(response.responseText);
7936         } catch (e) {
7937             ret = {
7938                 success: false,
7939                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7940                 errors : []
7941             };
7942         }
7943         return ret;
7944         
7945     }
7946 });
7947
7948
7949 Roo.form.Action.Load = function(form, options){
7950     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7951     this.reader = this.form.reader;
7952 };
7953
7954 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7955     type : 'load',
7956
7957     run : function(){
7958         
7959         Roo.Ajax.request(Roo.apply(
7960                 this.createCallback(), {
7961                     method:this.getMethod(),
7962                     url:this.getUrl(false),
7963                     params:this.getParams()
7964         }));
7965     },
7966
7967     success : function(response){
7968         
7969         var result = this.processResponse(response);
7970         if(result === true || !result.success || !result.data){
7971             this.failureType = Roo.form.Action.LOAD_FAILURE;
7972             this.form.afterAction(this, false);
7973             return;
7974         }
7975         this.form.clearInvalid();
7976         this.form.setValues(result.data);
7977         this.form.afterAction(this, true);
7978     },
7979
7980     handleResponse : function(response){
7981         if(this.form.reader){
7982             var rs = this.form.reader.read(response);
7983             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7984             return {
7985                 success : rs.success,
7986                 data : data
7987             };
7988         }
7989         return Roo.decode(response.responseText);
7990     }
7991 });
7992
7993 Roo.form.Action.ACTION_TYPES = {
7994     'load' : Roo.form.Action.Load,
7995     'submit' : Roo.form.Action.Submit
7996 };/*
7997  * - LGPL
7998  *
7999  * form
8000  *
8001  */
8002
8003 /**
8004  * @class Roo.bootstrap.Form
8005  * @extends Roo.bootstrap.Component
8006  * Bootstrap Form class
8007  * @cfg {String} method  GET | POST (default POST)
8008  * @cfg {String} labelAlign top | left (default top)
8009  * @cfg {String} align left  | right - for navbars
8010  * @cfg {Boolean} loadMask load mask when submit (default true)
8011
8012  *
8013  * @constructor
8014  * Create a new Form
8015  * @param {Object} config The config object
8016  */
8017
8018
8019 Roo.bootstrap.Form = function(config){
8020     
8021     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8022     
8023     Roo.bootstrap.Form.popover.apply();
8024     
8025     this.addEvents({
8026         /**
8027          * @event clientvalidation
8028          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8029          * @param {Form} this
8030          * @param {Boolean} valid true if the form has passed client-side validation
8031          */
8032         clientvalidation: true,
8033         /**
8034          * @event beforeaction
8035          * Fires before any action is performed. Return false to cancel the action.
8036          * @param {Form} this
8037          * @param {Action} action The action to be performed
8038          */
8039         beforeaction: true,
8040         /**
8041          * @event actionfailed
8042          * Fires when an action fails.
8043          * @param {Form} this
8044          * @param {Action} action The action that failed
8045          */
8046         actionfailed : true,
8047         /**
8048          * @event actioncomplete
8049          * Fires when an action is completed.
8050          * @param {Form} this
8051          * @param {Action} action The action that completed
8052          */
8053         actioncomplete : true
8054     });
8055 };
8056
8057 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8058
8059      /**
8060      * @cfg {String} method
8061      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8062      */
8063     method : 'POST',
8064     /**
8065      * @cfg {String} url
8066      * The URL to use for form actions if one isn't supplied in the action options.
8067      */
8068     /**
8069      * @cfg {Boolean} fileUpload
8070      * Set to true if this form is a file upload.
8071      */
8072
8073     /**
8074      * @cfg {Object} baseParams
8075      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8076      */
8077
8078     /**
8079      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8080      */
8081     timeout: 30,
8082     /**
8083      * @cfg {Sting} align (left|right) for navbar forms
8084      */
8085     align : 'left',
8086
8087     // private
8088     activeAction : null,
8089
8090     /**
8091      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8092      * element by passing it or its id or mask the form itself by passing in true.
8093      * @type Mixed
8094      */
8095     waitMsgTarget : false,
8096
8097     loadMask : true,
8098     
8099     /**
8100      * @cfg {Boolean} errorMask (true|false) default false
8101      */
8102     errorMask : false,
8103     
8104     /**
8105      * @cfg {Number} maskOffset Default 100
8106      */
8107     maskOffset : 100,
8108     
8109     /**
8110      * @cfg {Boolean} maskBody
8111      */
8112     maskBody : false,
8113
8114     getAutoCreate : function(){
8115
8116         var cfg = {
8117             tag: 'form',
8118             method : this.method || 'POST',
8119             id : this.id || Roo.id(),
8120             cls : ''
8121         };
8122         if (this.parent().xtype.match(/^Nav/)) {
8123             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8124
8125         }
8126
8127         if (this.labelAlign == 'left' ) {
8128             cfg.cls += ' form-horizontal';
8129         }
8130
8131
8132         return cfg;
8133     },
8134     initEvents : function()
8135     {
8136         this.el.on('submit', this.onSubmit, this);
8137         // this was added as random key presses on the form where triggering form submit.
8138         this.el.on('keypress', function(e) {
8139             if (e.getCharCode() != 13) {
8140                 return true;
8141             }
8142             // we might need to allow it for textareas.. and some other items.
8143             // check e.getTarget().
8144
8145             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8146                 return true;
8147             }
8148
8149             Roo.log("keypress blocked");
8150
8151             e.preventDefault();
8152             return false;
8153         });
8154         
8155     },
8156     // private
8157     onSubmit : function(e){
8158         e.stopEvent();
8159     },
8160
8161      /**
8162      * Returns true if client-side validation on the form is successful.
8163      * @return Boolean
8164      */
8165     isValid : function(){
8166         var items = this.getItems();
8167         var valid = true;
8168         var target = false;
8169         
8170         items.each(function(f){
8171             
8172             if(f.validate()){
8173                 return;
8174             }
8175             
8176             Roo.log('invalid field: ' + f.name);
8177             
8178             valid = false;
8179
8180             if(!target && f.el.isVisible(true)){
8181                 target = f;
8182             }
8183            
8184         });
8185         
8186         if(this.errorMask && !valid){
8187             Roo.bootstrap.Form.popover.mask(this, target);
8188         }
8189         
8190         return valid;
8191     },
8192     
8193     /**
8194      * Returns true if any fields in this form have changed since their original load.
8195      * @return Boolean
8196      */
8197     isDirty : function(){
8198         var dirty = false;
8199         var items = this.getItems();
8200         items.each(function(f){
8201            if(f.isDirty()){
8202                dirty = true;
8203                return false;
8204            }
8205            return true;
8206         });
8207         return dirty;
8208     },
8209      /**
8210      * Performs a predefined action (submit or load) or custom actions you define on this form.
8211      * @param {String} actionName The name of the action type
8212      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8213      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8214      * accept other config options):
8215      * <pre>
8216 Property          Type             Description
8217 ----------------  ---------------  ----------------------------------------------------------------------------------
8218 url               String           The url for the action (defaults to the form's url)
8219 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8220 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8221 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8222                                    validate the form on the client (defaults to false)
8223      * </pre>
8224      * @return {BasicForm} this
8225      */
8226     doAction : function(action, options){
8227         if(typeof action == 'string'){
8228             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8229         }
8230         if(this.fireEvent('beforeaction', this, action) !== false){
8231             this.beforeAction(action);
8232             action.run.defer(100, action);
8233         }
8234         return this;
8235     },
8236
8237     // private
8238     beforeAction : function(action){
8239         var o = action.options;
8240         
8241         if(this.loadMask){
8242             
8243             if(this.maskBody){
8244                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8245             } else {
8246                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8247             }
8248         }
8249         // not really supported yet.. ??
8250
8251         //if(this.waitMsgTarget === true){
8252         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8253         //}else if(this.waitMsgTarget){
8254         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8255         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8256         //}else {
8257         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8258        // }
8259
8260     },
8261
8262     // private
8263     afterAction : function(action, success){
8264         this.activeAction = null;
8265         var o = action.options;
8266
8267         if(this.loadMask){
8268             
8269             if(this.maskBody){
8270                 Roo.get(document.body).unmask();
8271             } else {
8272                 this.el.unmask();
8273             }
8274         }
8275         
8276         //if(this.waitMsgTarget === true){
8277 //            this.el.unmask();
8278         //}else if(this.waitMsgTarget){
8279         //    this.waitMsgTarget.unmask();
8280         //}else{
8281         //    Roo.MessageBox.updateProgress(1);
8282         //    Roo.MessageBox.hide();
8283        // }
8284         //
8285         if(success){
8286             if(o.reset){
8287                 this.reset();
8288             }
8289             Roo.callback(o.success, o.scope, [this, action]);
8290             this.fireEvent('actioncomplete', this, action);
8291
8292         }else{
8293
8294             // failure condition..
8295             // we have a scenario where updates need confirming.
8296             // eg. if a locking scenario exists..
8297             // we look for { errors : { needs_confirm : true }} in the response.
8298             if (
8299                 (typeof(action.result) != 'undefined')  &&
8300                 (typeof(action.result.errors) != 'undefined')  &&
8301                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8302            ){
8303                 var _t = this;
8304                 Roo.log("not supported yet");
8305                  /*
8306
8307                 Roo.MessageBox.confirm(
8308                     "Change requires confirmation",
8309                     action.result.errorMsg,
8310                     function(r) {
8311                         if (r != 'yes') {
8312                             return;
8313                         }
8314                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8315                     }
8316
8317                 );
8318                 */
8319
8320
8321                 return;
8322             }
8323
8324             Roo.callback(o.failure, o.scope, [this, action]);
8325             // show an error message if no failed handler is set..
8326             if (!this.hasListener('actionfailed')) {
8327                 Roo.log("need to add dialog support");
8328                 /*
8329                 Roo.MessageBox.alert("Error",
8330                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8331                         action.result.errorMsg :
8332                         "Saving Failed, please check your entries or try again"
8333                 );
8334                 */
8335             }
8336
8337             this.fireEvent('actionfailed', this, action);
8338         }
8339
8340     },
8341     /**
8342      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8343      * @param {String} id The value to search for
8344      * @return Field
8345      */
8346     findField : function(id){
8347         var items = this.getItems();
8348         var field = items.get(id);
8349         if(!field){
8350              items.each(function(f){
8351                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8352                     field = f;
8353                     return false;
8354                 }
8355                 return true;
8356             });
8357         }
8358         return field || null;
8359     },
8360      /**
8361      * Mark fields in this form invalid in bulk.
8362      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8363      * @return {BasicForm} this
8364      */
8365     markInvalid : function(errors){
8366         if(errors instanceof Array){
8367             for(var i = 0, len = errors.length; i < len; i++){
8368                 var fieldError = errors[i];
8369                 var f = this.findField(fieldError.id);
8370                 if(f){
8371                     f.markInvalid(fieldError.msg);
8372                 }
8373             }
8374         }else{
8375             var field, id;
8376             for(id in errors){
8377                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8378                     field.markInvalid(errors[id]);
8379                 }
8380             }
8381         }
8382         //Roo.each(this.childForms || [], function (f) {
8383         //    f.markInvalid(errors);
8384         //});
8385
8386         return this;
8387     },
8388
8389     /**
8390      * Set values for fields in this form in bulk.
8391      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8392      * @return {BasicForm} this
8393      */
8394     setValues : function(values){
8395         if(values instanceof Array){ // array of objects
8396             for(var i = 0, len = values.length; i < len; i++){
8397                 var v = values[i];
8398                 var f = this.findField(v.id);
8399                 if(f){
8400                     f.setValue(v.value);
8401                     if(this.trackResetOnLoad){
8402                         f.originalValue = f.getValue();
8403                     }
8404                 }
8405             }
8406         }else{ // object hash
8407             var field, id;
8408             for(id in values){
8409                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8410
8411                     if (field.setFromData &&
8412                         field.valueField &&
8413                         field.displayField &&
8414                         // combos' with local stores can
8415                         // be queried via setValue()
8416                         // to set their value..
8417                         (field.store && !field.store.isLocal)
8418                         ) {
8419                         // it's a combo
8420                         var sd = { };
8421                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8422                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8423                         field.setFromData(sd);
8424
8425                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8426                         
8427                         field.setFromData(values);
8428                         
8429                     } else {
8430                         field.setValue(values[id]);
8431                     }
8432
8433
8434                     if(this.trackResetOnLoad){
8435                         field.originalValue = field.getValue();
8436                     }
8437                 }
8438             }
8439         }
8440
8441         //Roo.each(this.childForms || [], function (f) {
8442         //    f.setValues(values);
8443         //});
8444
8445         return this;
8446     },
8447
8448     /**
8449      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8450      * they are returned as an array.
8451      * @param {Boolean} asString
8452      * @return {Object}
8453      */
8454     getValues : function(asString){
8455         //if (this.childForms) {
8456             // copy values from the child forms
8457         //    Roo.each(this.childForms, function (f) {
8458         //        this.setValues(f.getValues());
8459         //    }, this);
8460         //}
8461
8462
8463
8464         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8465         if(asString === true){
8466             return fs;
8467         }
8468         return Roo.urlDecode(fs);
8469     },
8470
8471     /**
8472      * Returns the fields in this form as an object with key/value pairs.
8473      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8474      * @return {Object}
8475      */
8476     getFieldValues : function(with_hidden)
8477     {
8478         var items = this.getItems();
8479         var ret = {};
8480         items.each(function(f){
8481             
8482             if (!f.getName()) {
8483                 return;
8484             }
8485             
8486             var v = f.getValue();
8487             
8488             if (f.inputType =='radio') {
8489                 if (typeof(ret[f.getName()]) == 'undefined') {
8490                     ret[f.getName()] = ''; // empty..
8491                 }
8492
8493                 if (!f.el.dom.checked) {
8494                     return;
8495
8496                 }
8497                 v = f.el.dom.value;
8498
8499             }
8500             
8501             if(f.xtype == 'MoneyField'){
8502                 ret[f.currencyName] = f.getCurrency();
8503             }
8504
8505             // not sure if this supported any more..
8506             if ((typeof(v) == 'object') && f.getRawValue) {
8507                 v = f.getRawValue() ; // dates..
8508             }
8509             // combo boxes where name != hiddenName...
8510             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8511                 ret[f.name] = f.getRawValue();
8512             }
8513             ret[f.getName()] = v;
8514         });
8515
8516         return ret;
8517     },
8518
8519     /**
8520      * Clears all invalid messages in this form.
8521      * @return {BasicForm} this
8522      */
8523     clearInvalid : function(){
8524         var items = this.getItems();
8525
8526         items.each(function(f){
8527            f.clearInvalid();
8528         });
8529
8530         return this;
8531     },
8532
8533     /**
8534      * Resets this form.
8535      * @return {BasicForm} this
8536      */
8537     reset : function(){
8538         var items = this.getItems();
8539         items.each(function(f){
8540             f.reset();
8541         });
8542
8543         Roo.each(this.childForms || [], function (f) {
8544             f.reset();
8545         });
8546
8547
8548         return this;
8549     },
8550     
8551     getItems : function()
8552     {
8553         var r=new Roo.util.MixedCollection(false, function(o){
8554             return o.id || (o.id = Roo.id());
8555         });
8556         var iter = function(el) {
8557             if (el.inputEl) {
8558                 r.add(el);
8559             }
8560             if (!el.items) {
8561                 return;
8562             }
8563             Roo.each(el.items,function(e) {
8564                 iter(e);
8565             });
8566         };
8567
8568         iter(this);
8569         return r;
8570     },
8571     
8572     hideFields : function(items)
8573     {
8574         Roo.each(items, function(i){
8575             
8576             var f = this.findField(i);
8577             
8578             if(!f){
8579                 return;
8580             }
8581             
8582             f.hide();
8583             
8584         }, this);
8585     },
8586     
8587     showFields : function(items)
8588     {
8589         Roo.each(items, function(i){
8590             
8591             var f = this.findField(i);
8592             
8593             if(!f){
8594                 return;
8595             }
8596             
8597             f.show();
8598             
8599         }, this);
8600     }
8601
8602 });
8603
8604 Roo.apply(Roo.bootstrap.Form, {
8605     
8606     popover : {
8607         
8608         padding : 5,
8609         
8610         isApplied : false,
8611         
8612         isMasked : false,
8613         
8614         form : false,
8615         
8616         target : false,
8617         
8618         toolTip : false,
8619         
8620         intervalID : false,
8621         
8622         maskEl : false,
8623         
8624         apply : function()
8625         {
8626             if(this.isApplied){
8627                 return;
8628             }
8629             
8630             this.maskEl = {
8631                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8632                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8633                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8634                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8635             };
8636             
8637             this.maskEl.top.enableDisplayMode("block");
8638             this.maskEl.left.enableDisplayMode("block");
8639             this.maskEl.bottom.enableDisplayMode("block");
8640             this.maskEl.right.enableDisplayMode("block");
8641             
8642             this.toolTip = new Roo.bootstrap.Tooltip({
8643                 cls : 'roo-form-error-popover',
8644                 alignment : {
8645                     'left' : ['r-l', [-2,0], 'right'],
8646                     'right' : ['l-r', [2,0], 'left'],
8647                     'bottom' : ['tl-bl', [0,2], 'top'],
8648                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8649                 }
8650             });
8651             
8652             this.toolTip.render(Roo.get(document.body));
8653
8654             this.toolTip.el.enableDisplayMode("block");
8655             
8656             Roo.get(document.body).on('click', function(){
8657                 this.unmask();
8658             }, this);
8659             
8660             Roo.get(document.body).on('touchstart', function(){
8661                 this.unmask();
8662             }, this);
8663             
8664             this.isApplied = true
8665         },
8666         
8667         mask : function(form, target)
8668         {
8669             this.form = form;
8670             
8671             this.target = target;
8672             
8673             if(!this.form.errorMask || !target.el){
8674                 return;
8675             }
8676             
8677             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8678             
8679             Roo.log(scrollable);
8680             
8681             var ot = this.target.el.calcOffsetsTo(scrollable);
8682             
8683             var scrollTo = ot[1] - this.form.maskOffset;
8684             
8685             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8686             
8687             scrollable.scrollTo('top', scrollTo);
8688             
8689             var box = this.target.el.getBox();
8690             Roo.log(box);
8691             var zIndex = Roo.bootstrap.Modal.zIndex++;
8692
8693             
8694             this.maskEl.top.setStyle('position', 'absolute');
8695             this.maskEl.top.setStyle('z-index', zIndex);
8696             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8697             this.maskEl.top.setLeft(0);
8698             this.maskEl.top.setTop(0);
8699             this.maskEl.top.show();
8700             
8701             this.maskEl.left.setStyle('position', 'absolute');
8702             this.maskEl.left.setStyle('z-index', zIndex);
8703             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8704             this.maskEl.left.setLeft(0);
8705             this.maskEl.left.setTop(box.y - this.padding);
8706             this.maskEl.left.show();
8707
8708             this.maskEl.bottom.setStyle('position', 'absolute');
8709             this.maskEl.bottom.setStyle('z-index', zIndex);
8710             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8711             this.maskEl.bottom.setLeft(0);
8712             this.maskEl.bottom.setTop(box.bottom + this.padding);
8713             this.maskEl.bottom.show();
8714
8715             this.maskEl.right.setStyle('position', 'absolute');
8716             this.maskEl.right.setStyle('z-index', zIndex);
8717             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8718             this.maskEl.right.setLeft(box.right + this.padding);
8719             this.maskEl.right.setTop(box.y - this.padding);
8720             this.maskEl.right.show();
8721
8722             this.toolTip.bindEl = this.target.el;
8723
8724             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8725
8726             var tip = this.target.blankText;
8727
8728             if(this.target.getValue() !== '' ) {
8729                 
8730                 if (this.target.invalidText.length) {
8731                     tip = this.target.invalidText;
8732                 } else if (this.target.regexText.length){
8733                     tip = this.target.regexText;
8734                 }
8735             }
8736
8737             this.toolTip.show(tip);
8738
8739             this.intervalID = window.setInterval(function() {
8740                 Roo.bootstrap.Form.popover.unmask();
8741             }, 10000);
8742
8743             window.onwheel = function(){ return false;};
8744             
8745             (function(){ this.isMasked = true; }).defer(500, this);
8746             
8747         },
8748         
8749         unmask : function()
8750         {
8751             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8752                 return;
8753             }
8754             
8755             this.maskEl.top.setStyle('position', 'absolute');
8756             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8757             this.maskEl.top.hide();
8758
8759             this.maskEl.left.setStyle('position', 'absolute');
8760             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8761             this.maskEl.left.hide();
8762
8763             this.maskEl.bottom.setStyle('position', 'absolute');
8764             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8765             this.maskEl.bottom.hide();
8766
8767             this.maskEl.right.setStyle('position', 'absolute');
8768             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8769             this.maskEl.right.hide();
8770             
8771             this.toolTip.hide();
8772             
8773             this.toolTip.el.hide();
8774             
8775             window.onwheel = function(){ return true;};
8776             
8777             if(this.intervalID){
8778                 window.clearInterval(this.intervalID);
8779                 this.intervalID = false;
8780             }
8781             
8782             this.isMasked = false;
8783             
8784         }
8785         
8786     }
8787     
8788 });
8789
8790 /*
8791  * Based on:
8792  * Ext JS Library 1.1.1
8793  * Copyright(c) 2006-2007, Ext JS, LLC.
8794  *
8795  * Originally Released Under LGPL - original licence link has changed is not relivant.
8796  *
8797  * Fork - LGPL
8798  * <script type="text/javascript">
8799  */
8800 /**
8801  * @class Roo.form.VTypes
8802  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8803  * @singleton
8804  */
8805 Roo.form.VTypes = function(){
8806     // closure these in so they are only created once.
8807     var alpha = /^[a-zA-Z_]+$/;
8808     var alphanum = /^[a-zA-Z0-9_]+$/;
8809     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8810     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8811
8812     // All these messages and functions are configurable
8813     return {
8814         /**
8815          * The function used to validate email addresses
8816          * @param {String} value The email address
8817          */
8818         'email' : function(v){
8819             return email.test(v);
8820         },
8821         /**
8822          * The error text to display when the email validation function returns false
8823          * @type String
8824          */
8825         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8826         /**
8827          * The keystroke filter mask to be applied on email input
8828          * @type RegExp
8829          */
8830         'emailMask' : /[a-z0-9_\.\-@]/i,
8831
8832         /**
8833          * The function used to validate URLs
8834          * @param {String} value The URL
8835          */
8836         'url' : function(v){
8837             return url.test(v);
8838         },
8839         /**
8840          * The error text to display when the url validation function returns false
8841          * @type String
8842          */
8843         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8844         
8845         /**
8846          * The function used to validate alpha values
8847          * @param {String} value The value
8848          */
8849         'alpha' : function(v){
8850             return alpha.test(v);
8851         },
8852         /**
8853          * The error text to display when the alpha validation function returns false
8854          * @type String
8855          */
8856         'alphaText' : 'This field should only contain letters and _',
8857         /**
8858          * The keystroke filter mask to be applied on alpha input
8859          * @type RegExp
8860          */
8861         'alphaMask' : /[a-z_]/i,
8862
8863         /**
8864          * The function used to validate alphanumeric values
8865          * @param {String} value The value
8866          */
8867         'alphanum' : function(v){
8868             return alphanum.test(v);
8869         },
8870         /**
8871          * The error text to display when the alphanumeric validation function returns false
8872          * @type String
8873          */
8874         'alphanumText' : 'This field should only contain letters, numbers and _',
8875         /**
8876          * The keystroke filter mask to be applied on alphanumeric input
8877          * @type RegExp
8878          */
8879         'alphanumMask' : /[a-z0-9_]/i
8880     };
8881 }();/*
8882  * - LGPL
8883  *
8884  * Input
8885  * 
8886  */
8887
8888 /**
8889  * @class Roo.bootstrap.Input
8890  * @extends Roo.bootstrap.Component
8891  * Bootstrap Input class
8892  * @cfg {Boolean} disabled is it disabled
8893  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8894  * @cfg {String} name name of the input
8895  * @cfg {string} fieldLabel - the label associated
8896  * @cfg {string} placeholder - placeholder to put in text.
8897  * @cfg {string}  before - input group add on before
8898  * @cfg {string} after - input group add on after
8899  * @cfg {string} size - (lg|sm) or leave empty..
8900  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8901  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8902  * @cfg {Number} md colspan out of 12 for computer-sized screens
8903  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8904  * @cfg {string} value default value of the input
8905  * @cfg {Number} labelWidth set the width of label 
8906  * @cfg {Number} labellg set the width of label (1-12)
8907  * @cfg {Number} labelmd set the width of label (1-12)
8908  * @cfg {Number} labelsm set the width of label (1-12)
8909  * @cfg {Number} labelxs set the width of label (1-12)
8910  * @cfg {String} labelAlign (top|left)
8911  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8912  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8913  * @cfg {String} indicatorpos (left|right) default left
8914  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8915  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8916
8917  * @cfg {String} align (left|center|right) Default left
8918  * @cfg {Boolean} forceFeedback (true|false) Default false
8919  * 
8920  * @constructor
8921  * Create a new Input
8922  * @param {Object} config The config object
8923  */
8924
8925 Roo.bootstrap.Input = function(config){
8926     
8927     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8928     
8929     this.addEvents({
8930         /**
8931          * @event focus
8932          * Fires when this field receives input focus.
8933          * @param {Roo.form.Field} this
8934          */
8935         focus : true,
8936         /**
8937          * @event blur
8938          * Fires when this field loses input focus.
8939          * @param {Roo.form.Field} this
8940          */
8941         blur : true,
8942         /**
8943          * @event specialkey
8944          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8945          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8946          * @param {Roo.form.Field} this
8947          * @param {Roo.EventObject} e The event object
8948          */
8949         specialkey : true,
8950         /**
8951          * @event change
8952          * Fires just before the field blurs if the field value has changed.
8953          * @param {Roo.form.Field} this
8954          * @param {Mixed} newValue The new value
8955          * @param {Mixed} oldValue The original value
8956          */
8957         change : true,
8958         /**
8959          * @event invalid
8960          * Fires after the field has been marked as invalid.
8961          * @param {Roo.form.Field} this
8962          * @param {String} msg The validation message
8963          */
8964         invalid : true,
8965         /**
8966          * @event valid
8967          * Fires after the field has been validated with no errors.
8968          * @param {Roo.form.Field} this
8969          */
8970         valid : true,
8971          /**
8972          * @event keyup
8973          * Fires after the key up
8974          * @param {Roo.form.Field} this
8975          * @param {Roo.EventObject}  e The event Object
8976          */
8977         keyup : true
8978     });
8979 };
8980
8981 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8982      /**
8983      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8984       automatic validation (defaults to "keyup").
8985      */
8986     validationEvent : "keyup",
8987      /**
8988      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8989      */
8990     validateOnBlur : true,
8991     /**
8992      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8993      */
8994     validationDelay : 250,
8995      /**
8996      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8997      */
8998     focusClass : "x-form-focus",  // not needed???
8999     
9000        
9001     /**
9002      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9003      */
9004     invalidClass : "has-warning",
9005     
9006     /**
9007      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9008      */
9009     validClass : "has-success",
9010     
9011     /**
9012      * @cfg {Boolean} hasFeedback (true|false) default true
9013      */
9014     hasFeedback : true,
9015     
9016     /**
9017      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9018      */
9019     invalidFeedbackClass : "glyphicon-warning-sign",
9020     
9021     /**
9022      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9023      */
9024     validFeedbackClass : "glyphicon-ok",
9025     
9026     /**
9027      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9028      */
9029     selectOnFocus : false,
9030     
9031      /**
9032      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9033      */
9034     maskRe : null,
9035        /**
9036      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9037      */
9038     vtype : null,
9039     
9040       /**
9041      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9042      */
9043     disableKeyFilter : false,
9044     
9045        /**
9046      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9047      */
9048     disabled : false,
9049      /**
9050      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9051      */
9052     allowBlank : true,
9053     /**
9054      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9055      */
9056     blankText : "Please complete this mandatory field",
9057     
9058      /**
9059      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9060      */
9061     minLength : 0,
9062     /**
9063      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9064      */
9065     maxLength : Number.MAX_VALUE,
9066     /**
9067      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9068      */
9069     minLengthText : "The minimum length for this field is {0}",
9070     /**
9071      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9072      */
9073     maxLengthText : "The maximum length for this field is {0}",
9074   
9075     
9076     /**
9077      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9078      * If available, this function will be called only after the basic validators all return true, and will be passed the
9079      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9080      */
9081     validator : null,
9082     /**
9083      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9084      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9085      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9086      */
9087     regex : null,
9088     /**
9089      * @cfg {String} regexText -- Depricated - use Invalid Text
9090      */
9091     regexText : "",
9092     
9093     /**
9094      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9095      */
9096     invalidText : "",
9097     
9098     
9099     
9100     autocomplete: false,
9101     
9102     
9103     fieldLabel : '',
9104     inputType : 'text',
9105     
9106     name : false,
9107     placeholder: false,
9108     before : false,
9109     after : false,
9110     size : false,
9111     hasFocus : false,
9112     preventMark: false,
9113     isFormField : true,
9114     value : '',
9115     labelWidth : 2,
9116     labelAlign : false,
9117     readOnly : false,
9118     align : false,
9119     formatedValue : false,
9120     forceFeedback : false,
9121     
9122     indicatorpos : 'left',
9123     
9124     labellg : 0,
9125     labelmd : 0,
9126     labelsm : 0,
9127     labelxs : 0,
9128     
9129     capture : '',
9130     accept : '',
9131     
9132     parentLabelAlign : function()
9133     {
9134         var parent = this;
9135         while (parent.parent()) {
9136             parent = parent.parent();
9137             if (typeof(parent.labelAlign) !='undefined') {
9138                 return parent.labelAlign;
9139             }
9140         }
9141         return 'left';
9142         
9143     },
9144     
9145     getAutoCreate : function()
9146     {
9147         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9148         
9149         var id = Roo.id();
9150         
9151         var cfg = {};
9152         
9153         if(this.inputType != 'hidden'){
9154             cfg.cls = 'form-group' //input-group
9155         }
9156         
9157         var input =  {
9158             tag: 'input',
9159             id : id,
9160             type : this.inputType,
9161             value : this.value,
9162             cls : 'form-control',
9163             placeholder : this.placeholder || '',
9164             autocomplete : this.autocomplete || 'new-password'
9165         };
9166         
9167         if(this.capture.length){
9168             input.capture = this.capture;
9169         }
9170         
9171         if(this.accept.length){
9172             input.accept = this.accept + "/*";
9173         }
9174         
9175         if(this.align){
9176             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9177         }
9178         
9179         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9180             input.maxLength = this.maxLength;
9181         }
9182         
9183         if (this.disabled) {
9184             input.disabled=true;
9185         }
9186         
9187         if (this.readOnly) {
9188             input.readonly=true;
9189         }
9190         
9191         if (this.name) {
9192             input.name = this.name;
9193         }
9194         
9195         if (this.size) {
9196             input.cls += ' input-' + this.size;
9197         }
9198         
9199         var settings=this;
9200         ['xs','sm','md','lg'].map(function(size){
9201             if (settings[size]) {
9202                 cfg.cls += ' col-' + size + '-' + settings[size];
9203             }
9204         });
9205         
9206         var inputblock = input;
9207         
9208         var feedback = {
9209             tag: 'span',
9210             cls: 'glyphicon form-control-feedback'
9211         };
9212             
9213         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9214             
9215             inputblock = {
9216                 cls : 'has-feedback',
9217                 cn :  [
9218                     input,
9219                     feedback
9220                 ] 
9221             };  
9222         }
9223         
9224         if (this.before || this.after) {
9225             
9226             inputblock = {
9227                 cls : 'input-group',
9228                 cn :  [] 
9229             };
9230             
9231             if (this.before && typeof(this.before) == 'string') {
9232                 
9233                 inputblock.cn.push({
9234                     tag :'span',
9235                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9236                     html : this.before
9237                 });
9238             }
9239             if (this.before && typeof(this.before) == 'object') {
9240                 this.before = Roo.factory(this.before);
9241                 
9242                 inputblock.cn.push({
9243                     tag :'span',
9244                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9245                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9246                 });
9247             }
9248             
9249             inputblock.cn.push(input);
9250             
9251             if (this.after && typeof(this.after) == 'string') {
9252                 inputblock.cn.push({
9253                     tag :'span',
9254                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9255                     html : this.after
9256                 });
9257             }
9258             if (this.after && typeof(this.after) == 'object') {
9259                 this.after = Roo.factory(this.after);
9260                 
9261                 inputblock.cn.push({
9262                     tag :'span',
9263                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9264                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9265                 });
9266             }
9267             
9268             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9269                 inputblock.cls += ' has-feedback';
9270                 inputblock.cn.push(feedback);
9271             }
9272         };
9273         var indicator = {
9274             tag : 'i',
9275             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9276             tooltip : 'This field is required'
9277         };
9278         if (Roo.bootstrap.version == 4) {
9279             indicator = {
9280                 tag : 'i',
9281                 style : 'display-none'
9282             };
9283         }
9284         if (align ==='left' && this.fieldLabel.length) {
9285             
9286             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9287             
9288             cfg.cn = [
9289                 indicator,
9290                 {
9291                     tag: 'label',
9292                     'for' :  id,
9293                     cls : 'control-label col-form-label',
9294                     html : this.fieldLabel
9295
9296                 },
9297                 {
9298                     cls : "", 
9299                     cn: [
9300                         inputblock
9301                     ]
9302                 }
9303             ];
9304             
9305             var labelCfg = cfg.cn[1];
9306             var contentCfg = cfg.cn[2];
9307             
9308             if(this.indicatorpos == 'right'){
9309                 cfg.cn = [
9310                     {
9311                         tag: 'label',
9312                         'for' :  id,
9313                         cls : 'control-label col-form-label',
9314                         cn : [
9315                             {
9316                                 tag : 'span',
9317                                 html : this.fieldLabel
9318                             },
9319                             indicator
9320                         ]
9321                     },
9322                     {
9323                         cls : "",
9324                         cn: [
9325                             inputblock
9326                         ]
9327                     }
9328
9329                 ];
9330                 
9331                 labelCfg = cfg.cn[0];
9332                 contentCfg = cfg.cn[1];
9333             
9334             }
9335             
9336             if(this.labelWidth > 12){
9337                 labelCfg.style = "width: " + this.labelWidth + 'px';
9338             }
9339             
9340             if(this.labelWidth < 13 && this.labelmd == 0){
9341                 this.labelmd = this.labelWidth;
9342             }
9343             
9344             if(this.labellg > 0){
9345                 labelCfg.cls += ' col-lg-' + this.labellg;
9346                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9347             }
9348             
9349             if(this.labelmd > 0){
9350                 labelCfg.cls += ' col-md-' + this.labelmd;
9351                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9352             }
9353             
9354             if(this.labelsm > 0){
9355                 labelCfg.cls += ' col-sm-' + this.labelsm;
9356                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9357             }
9358             
9359             if(this.labelxs > 0){
9360                 labelCfg.cls += ' col-xs-' + this.labelxs;
9361                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9362             }
9363             
9364             
9365         } else if ( this.fieldLabel.length) {
9366                 
9367             cfg.cn = [
9368                 {
9369                     tag : 'i',
9370                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9371                     tooltip : 'This field is required'
9372                 },
9373                 {
9374                     tag: 'label',
9375                    //cls : 'input-group-addon',
9376                     html : this.fieldLabel
9377
9378                 },
9379
9380                inputblock
9381
9382            ];
9383            
9384            if(this.indicatorpos == 'right'){
9385                 
9386                 cfg.cn = [
9387                     {
9388                         tag: 'label',
9389                        //cls : 'input-group-addon',
9390                         html : this.fieldLabel
9391
9392                     },
9393                     {
9394                         tag : 'i',
9395                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9396                         tooltip : 'This field is required'
9397                     },
9398
9399                    inputblock
9400
9401                ];
9402
9403             }
9404
9405         } else {
9406             
9407             cfg.cn = [
9408
9409                     inputblock
9410
9411             ];
9412                 
9413                 
9414         };
9415         
9416         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9417            cfg.cls += ' navbar-form';
9418         }
9419         
9420         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9421             // on BS4 we do this only if not form 
9422             cfg.cls += ' navbar-form';
9423             cfg.tag = 'li';
9424         }
9425         
9426         return cfg;
9427         
9428     },
9429     /**
9430      * return the real input element.
9431      */
9432     inputEl: function ()
9433     {
9434         return this.el.select('input.form-control',true).first();
9435     },
9436     
9437     tooltipEl : function()
9438     {
9439         return this.inputEl();
9440     },
9441     
9442     indicatorEl : function()
9443     {
9444         if (Roo.bootstrap.version == 4) {
9445             return false; // not enabled in v4 yet.
9446         }
9447         
9448         var indicator = this.el.select('i.roo-required-indicator',true).first();
9449         
9450         if(!indicator){
9451             return false;
9452         }
9453         
9454         return indicator;
9455         
9456     },
9457     
9458     setDisabled : function(v)
9459     {
9460         var i  = this.inputEl().dom;
9461         if (!v) {
9462             i.removeAttribute('disabled');
9463             return;
9464             
9465         }
9466         i.setAttribute('disabled','true');
9467     },
9468     initEvents : function()
9469     {
9470           
9471         this.inputEl().on("keydown" , this.fireKey,  this);
9472         this.inputEl().on("focus", this.onFocus,  this);
9473         this.inputEl().on("blur", this.onBlur,  this);
9474         
9475         this.inputEl().relayEvent('keyup', this);
9476         
9477         this.indicator = this.indicatorEl();
9478         
9479         if(this.indicator){
9480             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9481         }
9482  
9483         // reference to original value for reset
9484         this.originalValue = this.getValue();
9485         //Roo.form.TextField.superclass.initEvents.call(this);
9486         if(this.validationEvent == 'keyup'){
9487             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9488             this.inputEl().on('keyup', this.filterValidation, this);
9489         }
9490         else if(this.validationEvent !== false){
9491             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9492         }
9493         
9494         if(this.selectOnFocus){
9495             this.on("focus", this.preFocus, this);
9496             
9497         }
9498         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9499             this.inputEl().on("keypress", this.filterKeys, this);
9500         } else {
9501             this.inputEl().relayEvent('keypress', this);
9502         }
9503        /* if(this.grow){
9504             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9505             this.el.on("click", this.autoSize,  this);
9506         }
9507         */
9508         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9509             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9510         }
9511         
9512         if (typeof(this.before) == 'object') {
9513             this.before.render(this.el.select('.roo-input-before',true).first());
9514         }
9515         if (typeof(this.after) == 'object') {
9516             this.after.render(this.el.select('.roo-input-after',true).first());
9517         }
9518         
9519         this.inputEl().on('change', this.onChange, this);
9520         
9521     },
9522     filterValidation : function(e){
9523         if(!e.isNavKeyPress()){
9524             this.validationTask.delay(this.validationDelay);
9525         }
9526     },
9527      /**
9528      * Validates the field value
9529      * @return {Boolean} True if the value is valid, else false
9530      */
9531     validate : function(){
9532         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9533         if(this.disabled || this.validateValue(this.getRawValue())){
9534             this.markValid();
9535             return true;
9536         }
9537         
9538         this.markInvalid();
9539         return false;
9540     },
9541     
9542     
9543     /**
9544      * Validates a value according to the field's validation rules and marks the field as invalid
9545      * if the validation fails
9546      * @param {Mixed} value The value to validate
9547      * @return {Boolean} True if the value is valid, else false
9548      */
9549     validateValue : function(value)
9550     {
9551         if(this.getVisibilityEl().hasClass('hidden')){
9552             return true;
9553         }
9554         
9555         if(value.length < 1)  { // if it's blank
9556             if(this.allowBlank){
9557                 return true;
9558             }
9559             return false;
9560         }
9561         
9562         if(value.length < this.minLength){
9563             return false;
9564         }
9565         if(value.length > this.maxLength){
9566             return false;
9567         }
9568         if(this.vtype){
9569             var vt = Roo.form.VTypes;
9570             if(!vt[this.vtype](value, this)){
9571                 return false;
9572             }
9573         }
9574         if(typeof this.validator == "function"){
9575             var msg = this.validator(value);
9576             if(msg !== true){
9577                 return false;
9578             }
9579             if (typeof(msg) == 'string') {
9580                 this.invalidText = msg;
9581             }
9582         }
9583         
9584         if(this.regex && !this.regex.test(value)){
9585             return false;
9586         }
9587         
9588         return true;
9589     },
9590     
9591      // private
9592     fireKey : function(e){
9593         //Roo.log('field ' + e.getKey());
9594         if(e.isNavKeyPress()){
9595             this.fireEvent("specialkey", this, e);
9596         }
9597     },
9598     focus : function (selectText){
9599         if(this.rendered){
9600             this.inputEl().focus();
9601             if(selectText === true){
9602                 this.inputEl().dom.select();
9603             }
9604         }
9605         return this;
9606     } ,
9607     
9608     onFocus : function(){
9609         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9610            // this.el.addClass(this.focusClass);
9611         }
9612         if(!this.hasFocus){
9613             this.hasFocus = true;
9614             this.startValue = this.getValue();
9615             this.fireEvent("focus", this);
9616         }
9617     },
9618     
9619     beforeBlur : Roo.emptyFn,
9620
9621     
9622     // private
9623     onBlur : function(){
9624         this.beforeBlur();
9625         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9626             //this.el.removeClass(this.focusClass);
9627         }
9628         this.hasFocus = false;
9629         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9630             this.validate();
9631         }
9632         var v = this.getValue();
9633         if(String(v) !== String(this.startValue)){
9634             this.fireEvent('change', this, v, this.startValue);
9635         }
9636         this.fireEvent("blur", this);
9637     },
9638     
9639     onChange : function(e)
9640     {
9641         var v = this.getValue();
9642         if(String(v) !== String(this.startValue)){
9643             this.fireEvent('change', this, v, this.startValue);
9644         }
9645         
9646     },
9647     
9648     /**
9649      * Resets the current field value to the originally loaded value and clears any validation messages
9650      */
9651     reset : function(){
9652         this.setValue(this.originalValue);
9653         this.validate();
9654     },
9655      /**
9656      * Returns the name of the field
9657      * @return {Mixed} name The name field
9658      */
9659     getName: function(){
9660         return this.name;
9661     },
9662      /**
9663      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9664      * @return {Mixed} value The field value
9665      */
9666     getValue : function(){
9667         
9668         var v = this.inputEl().getValue();
9669         
9670         return v;
9671     },
9672     /**
9673      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9674      * @return {Mixed} value The field value
9675      */
9676     getRawValue : function(){
9677         var v = this.inputEl().getValue();
9678         
9679         return v;
9680     },
9681     
9682     /**
9683      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9684      * @param {Mixed} value The value to set
9685      */
9686     setRawValue : function(v){
9687         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9688     },
9689     
9690     selectText : function(start, end){
9691         var v = this.getRawValue();
9692         if(v.length > 0){
9693             start = start === undefined ? 0 : start;
9694             end = end === undefined ? v.length : end;
9695             var d = this.inputEl().dom;
9696             if(d.setSelectionRange){
9697                 d.setSelectionRange(start, end);
9698             }else if(d.createTextRange){
9699                 var range = d.createTextRange();
9700                 range.moveStart("character", start);
9701                 range.moveEnd("character", v.length-end);
9702                 range.select();
9703             }
9704         }
9705     },
9706     
9707     /**
9708      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9709      * @param {Mixed} value The value to set
9710      */
9711     setValue : function(v){
9712         this.value = v;
9713         if(this.rendered){
9714             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9715             this.validate();
9716         }
9717     },
9718     
9719     /*
9720     processValue : function(value){
9721         if(this.stripCharsRe){
9722             var newValue = value.replace(this.stripCharsRe, '');
9723             if(newValue !== value){
9724                 this.setRawValue(newValue);
9725                 return newValue;
9726             }
9727         }
9728         return value;
9729     },
9730   */
9731     preFocus : function(){
9732         
9733         if(this.selectOnFocus){
9734             this.inputEl().dom.select();
9735         }
9736     },
9737     filterKeys : function(e){
9738         var k = e.getKey();
9739         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9740             return;
9741         }
9742         var c = e.getCharCode(), cc = String.fromCharCode(c);
9743         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9744             return;
9745         }
9746         if(!this.maskRe.test(cc)){
9747             e.stopEvent();
9748         }
9749     },
9750      /**
9751      * Clear any invalid styles/messages for this field
9752      */
9753     clearInvalid : function(){
9754         
9755         if(!this.el || this.preventMark){ // not rendered
9756             return;
9757         }
9758         
9759         
9760         this.el.removeClass([this.invalidClass, 'is-invalid']);
9761         
9762         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9763             
9764             var feedback = this.el.select('.form-control-feedback', true).first();
9765             
9766             if(feedback){
9767                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9768             }
9769             
9770         }
9771         
9772         if(this.indicator){
9773             this.indicator.removeClass('visible');
9774             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9775         }
9776         
9777         this.fireEvent('valid', this);
9778     },
9779     
9780      /**
9781      * Mark this field as valid
9782      */
9783     markValid : function()
9784     {
9785         if(!this.el  || this.preventMark){ // not rendered...
9786             return;
9787         }
9788         
9789         this.el.removeClass([this.invalidClass, this.validClass]);
9790         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9791
9792         var feedback = this.el.select('.form-control-feedback', true).first();
9793             
9794         if(feedback){
9795             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9796         }
9797         
9798         if(this.indicator){
9799             this.indicator.removeClass('visible');
9800             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9801         }
9802         
9803         if(this.disabled){
9804             return;
9805         }
9806         
9807         if(this.allowBlank && !this.getRawValue().length){
9808             return;
9809         }
9810         if (Roo.bootstrap.version == 3) {
9811             this.el.addClass(this.validClass);
9812         } else {
9813             this.inputEl().addClass('is-valid');
9814         }
9815
9816         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9817             
9818             var feedback = this.el.select('.form-control-feedback', true).first();
9819             
9820             if(feedback){
9821                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9822                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9823             }
9824             
9825         }
9826         
9827         this.fireEvent('valid', this);
9828     },
9829     
9830      /**
9831      * Mark this field as invalid
9832      * @param {String} msg The validation message
9833      */
9834     markInvalid : function(msg)
9835     {
9836         if(!this.el  || this.preventMark){ // not rendered
9837             return;
9838         }
9839         
9840         this.el.removeClass([this.invalidClass, this.validClass]);
9841         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9842         
9843         var feedback = this.el.select('.form-control-feedback', true).first();
9844             
9845         if(feedback){
9846             this.el.select('.form-control-feedback', true).first().removeClass(
9847                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9848         }
9849
9850         if(this.disabled){
9851             return;
9852         }
9853         
9854         if(this.allowBlank && !this.getRawValue().length){
9855             return;
9856         }
9857         
9858         if(this.indicator){
9859             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9860             this.indicator.addClass('visible');
9861         }
9862         if (Roo.bootstrap.version == 3) {
9863             this.el.addClass(this.invalidClass);
9864         } else {
9865             this.inputEl().addClass('is-invalid');
9866         }
9867         
9868         
9869         
9870         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9871             
9872             var feedback = this.el.select('.form-control-feedback', true).first();
9873             
9874             if(feedback){
9875                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9876                 
9877                 if(this.getValue().length || this.forceFeedback){
9878                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9879                 }
9880                 
9881             }
9882             
9883         }
9884         
9885         this.fireEvent('invalid', this, msg);
9886     },
9887     // private
9888     SafariOnKeyDown : function(event)
9889     {
9890         // this is a workaround for a password hang bug on chrome/ webkit.
9891         if (this.inputEl().dom.type != 'password') {
9892             return;
9893         }
9894         
9895         var isSelectAll = false;
9896         
9897         if(this.inputEl().dom.selectionEnd > 0){
9898             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9899         }
9900         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9901             event.preventDefault();
9902             this.setValue('');
9903             return;
9904         }
9905         
9906         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9907             
9908             event.preventDefault();
9909             // this is very hacky as keydown always get's upper case.
9910             //
9911             var cc = String.fromCharCode(event.getCharCode());
9912             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9913             
9914         }
9915     },
9916     adjustWidth : function(tag, w){
9917         tag = tag.toLowerCase();
9918         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9919             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9920                 if(tag == 'input'){
9921                     return w + 2;
9922                 }
9923                 if(tag == 'textarea'){
9924                     return w-2;
9925                 }
9926             }else if(Roo.isOpera){
9927                 if(tag == 'input'){
9928                     return w + 2;
9929                 }
9930                 if(tag == 'textarea'){
9931                     return w-2;
9932                 }
9933             }
9934         }
9935         return w;
9936     },
9937     
9938     setFieldLabel : function(v)
9939     {
9940         if(!this.rendered){
9941             return;
9942         }
9943         
9944         if(this.indicatorEl()){
9945             var ar = this.el.select('label > span',true);
9946             
9947             if (ar.elements.length) {
9948                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9949                 this.fieldLabel = v;
9950                 return;
9951             }
9952             
9953             var br = this.el.select('label',true);
9954             
9955             if(br.elements.length) {
9956                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9957                 this.fieldLabel = v;
9958                 return;
9959             }
9960             
9961             Roo.log('Cannot Found any of label > span || label in input');
9962             return;
9963         }
9964         
9965         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9966         this.fieldLabel = v;
9967         
9968         
9969     }
9970 });
9971
9972  
9973 /*
9974  * - LGPL
9975  *
9976  * Input
9977  * 
9978  */
9979
9980 /**
9981  * @class Roo.bootstrap.TextArea
9982  * @extends Roo.bootstrap.Input
9983  * Bootstrap TextArea class
9984  * @cfg {Number} cols Specifies the visible width of a text area
9985  * @cfg {Number} rows Specifies the visible number of lines in a text area
9986  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9987  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9988  * @cfg {string} html text
9989  * 
9990  * @constructor
9991  * Create a new TextArea
9992  * @param {Object} config The config object
9993  */
9994
9995 Roo.bootstrap.TextArea = function(config){
9996     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9997    
9998 };
9999
10000 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10001      
10002     cols : false,
10003     rows : 5,
10004     readOnly : false,
10005     warp : 'soft',
10006     resize : false,
10007     value: false,
10008     html: false,
10009     
10010     getAutoCreate : function(){
10011         
10012         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10013         
10014         var id = Roo.id();
10015         
10016         var cfg = {};
10017         
10018         if(this.inputType != 'hidden'){
10019             cfg.cls = 'form-group' //input-group
10020         }
10021         
10022         var input =  {
10023             tag: 'textarea',
10024             id : id,
10025             warp : this.warp,
10026             rows : this.rows,
10027             value : this.value || '',
10028             html: this.html || '',
10029             cls : 'form-control',
10030             placeholder : this.placeholder || '' 
10031             
10032         };
10033         
10034         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10035             input.maxLength = this.maxLength;
10036         }
10037         
10038         if(this.resize){
10039             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10040         }
10041         
10042         if(this.cols){
10043             input.cols = this.cols;
10044         }
10045         
10046         if (this.readOnly) {
10047             input.readonly = true;
10048         }
10049         
10050         if (this.name) {
10051             input.name = this.name;
10052         }
10053         
10054         if (this.size) {
10055             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10056         }
10057         
10058         var settings=this;
10059         ['xs','sm','md','lg'].map(function(size){
10060             if (settings[size]) {
10061                 cfg.cls += ' col-' + size + '-' + settings[size];
10062             }
10063         });
10064         
10065         var inputblock = input;
10066         
10067         if(this.hasFeedback && !this.allowBlank){
10068             
10069             var feedback = {
10070                 tag: 'span',
10071                 cls: 'glyphicon form-control-feedback'
10072             };
10073
10074             inputblock = {
10075                 cls : 'has-feedback',
10076                 cn :  [
10077                     input,
10078                     feedback
10079                 ] 
10080             };  
10081         }
10082         
10083         
10084         if (this.before || this.after) {
10085             
10086             inputblock = {
10087                 cls : 'input-group',
10088                 cn :  [] 
10089             };
10090             if (this.before) {
10091                 inputblock.cn.push({
10092                     tag :'span',
10093                     cls : 'input-group-addon',
10094                     html : this.before
10095                 });
10096             }
10097             
10098             inputblock.cn.push(input);
10099             
10100             if(this.hasFeedback && !this.allowBlank){
10101                 inputblock.cls += ' has-feedback';
10102                 inputblock.cn.push(feedback);
10103             }
10104             
10105             if (this.after) {
10106                 inputblock.cn.push({
10107                     tag :'span',
10108                     cls : 'input-group-addon',
10109                     html : this.after
10110                 });
10111             }
10112             
10113         }
10114         
10115         if (align ==='left' && this.fieldLabel.length) {
10116             cfg.cn = [
10117                 {
10118                     tag: 'label',
10119                     'for' :  id,
10120                     cls : 'control-label',
10121                     html : this.fieldLabel
10122                 },
10123                 {
10124                     cls : "",
10125                     cn: [
10126                         inputblock
10127                     ]
10128                 }
10129
10130             ];
10131             
10132             if(this.labelWidth > 12){
10133                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10134             }
10135
10136             if(this.labelWidth < 13 && this.labelmd == 0){
10137                 this.labelmd = this.labelWidth;
10138             }
10139
10140             if(this.labellg > 0){
10141                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10142                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10143             }
10144
10145             if(this.labelmd > 0){
10146                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10147                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10148             }
10149
10150             if(this.labelsm > 0){
10151                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10152                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10153             }
10154
10155             if(this.labelxs > 0){
10156                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10157                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10158             }
10159             
10160         } else if ( this.fieldLabel.length) {
10161             cfg.cn = [
10162
10163                {
10164                    tag: 'label',
10165                    //cls : 'input-group-addon',
10166                    html : this.fieldLabel
10167
10168                },
10169
10170                inputblock
10171
10172            ];
10173
10174         } else {
10175
10176             cfg.cn = [
10177
10178                 inputblock
10179
10180             ];
10181                 
10182         }
10183         
10184         if (this.disabled) {
10185             input.disabled=true;
10186         }
10187         
10188         return cfg;
10189         
10190     },
10191     /**
10192      * return the real textarea element.
10193      */
10194     inputEl: function ()
10195     {
10196         return this.el.select('textarea.form-control',true).first();
10197     },
10198     
10199     /**
10200      * Clear any invalid styles/messages for this field
10201      */
10202     clearInvalid : function()
10203     {
10204         
10205         if(!this.el || this.preventMark){ // not rendered
10206             return;
10207         }
10208         
10209         var label = this.el.select('label', true).first();
10210         var icon = this.el.select('i.fa-star', true).first();
10211         
10212         if(label && icon){
10213             icon.remove();
10214         }
10215         this.el.removeClass( this.validClass);
10216         this.inputEl().removeClass('is-invalid');
10217          
10218         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10219             
10220             var feedback = this.el.select('.form-control-feedback', true).first();
10221             
10222             if(feedback){
10223                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10224             }
10225             
10226         }
10227         
10228         this.fireEvent('valid', this);
10229     },
10230     
10231      /**
10232      * Mark this field as valid
10233      */
10234     markValid : function()
10235     {
10236         if(!this.el  || this.preventMark){ // not rendered
10237             return;
10238         }
10239         
10240         this.el.removeClass([this.invalidClass, this.validClass]);
10241         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10242         
10243         var feedback = this.el.select('.form-control-feedback', true).first();
10244             
10245         if(feedback){
10246             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10247         }
10248
10249         if(this.disabled || this.allowBlank){
10250             return;
10251         }
10252         
10253         var label = this.el.select('label', true).first();
10254         var icon = this.el.select('i.fa-star', true).first();
10255         
10256         if(label && icon){
10257             icon.remove();
10258         }
10259         if (Roo.bootstrap.version == 3) {
10260             this.el.addClass(this.validClass);
10261         } else {
10262             this.inputEl().addClass('is-valid');
10263         }
10264         
10265         
10266         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10267             
10268             var feedback = this.el.select('.form-control-feedback', true).first();
10269             
10270             if(feedback){
10271                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10272                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10273             }
10274             
10275         }
10276         
10277         this.fireEvent('valid', this);
10278     },
10279     
10280      /**
10281      * Mark this field as invalid
10282      * @param {String} msg The validation message
10283      */
10284     markInvalid : function(msg)
10285     {
10286         if(!this.el  || this.preventMark){ // not rendered
10287             return;
10288         }
10289         
10290         this.el.removeClass([this.invalidClass, this.validClass]);
10291         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10292         
10293         var feedback = this.el.select('.form-control-feedback', true).first();
10294             
10295         if(feedback){
10296             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10297         }
10298
10299         if(this.disabled || this.allowBlank){
10300             return;
10301         }
10302         
10303         var label = this.el.select('label', true).first();
10304         var icon = this.el.select('i.fa-star', true).first();
10305         
10306         if(!this.getValue().length && label && !icon){
10307             this.el.createChild({
10308                 tag : 'i',
10309                 cls : 'text-danger fa fa-lg fa-star',
10310                 tooltip : 'This field is required',
10311                 style : 'margin-right:5px;'
10312             }, label, true);
10313         }
10314         
10315         if (Roo.bootstrap.version == 3) {
10316             this.el.addClass(this.invalidClass);
10317         } else {
10318             this.inputEl().addClass('is-invalid');
10319         }
10320         
10321         // fixme ... this may be depricated need to test..
10322         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10323             
10324             var feedback = this.el.select('.form-control-feedback', true).first();
10325             
10326             if(feedback){
10327                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10328                 
10329                 if(this.getValue().length || this.forceFeedback){
10330                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10331                 }
10332                 
10333             }
10334             
10335         }
10336         
10337         this.fireEvent('invalid', this, msg);
10338     }
10339 });
10340
10341  
10342 /*
10343  * - LGPL
10344  *
10345  * trigger field - base class for combo..
10346  * 
10347  */
10348  
10349 /**
10350  * @class Roo.bootstrap.TriggerField
10351  * @extends Roo.bootstrap.Input
10352  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10353  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10354  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10355  * for which you can provide a custom implementation.  For example:
10356  * <pre><code>
10357 var trigger = new Roo.bootstrap.TriggerField();
10358 trigger.onTriggerClick = myTriggerFn;
10359 trigger.applyTo('my-field');
10360 </code></pre>
10361  *
10362  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10363  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10364  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10365  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10366  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10367
10368  * @constructor
10369  * Create a new TriggerField.
10370  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10371  * to the base TextField)
10372  */
10373 Roo.bootstrap.TriggerField = function(config){
10374     this.mimicing = false;
10375     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10376 };
10377
10378 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10379     /**
10380      * @cfg {String} triggerClass A CSS class to apply to the trigger
10381      */
10382      /**
10383      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10384      */
10385     hideTrigger:false,
10386
10387     /**
10388      * @cfg {Boolean} removable (true|false) special filter default false
10389      */
10390     removable : false,
10391     
10392     /** @cfg {Boolean} grow @hide */
10393     /** @cfg {Number} growMin @hide */
10394     /** @cfg {Number} growMax @hide */
10395
10396     /**
10397      * @hide 
10398      * @method
10399      */
10400     autoSize: Roo.emptyFn,
10401     // private
10402     monitorTab : true,
10403     // private
10404     deferHeight : true,
10405
10406     
10407     actionMode : 'wrap',
10408     
10409     caret : false,
10410     
10411     
10412     getAutoCreate : function(){
10413        
10414         var align = this.labelAlign || this.parentLabelAlign();
10415         
10416         var id = Roo.id();
10417         
10418         var cfg = {
10419             cls: 'form-group' //input-group
10420         };
10421         
10422         
10423         var input =  {
10424             tag: 'input',
10425             id : id,
10426             type : this.inputType,
10427             cls : 'form-control',
10428             autocomplete: 'new-password',
10429             placeholder : this.placeholder || '' 
10430             
10431         };
10432         if (this.name) {
10433             input.name = this.name;
10434         }
10435         if (this.size) {
10436             input.cls += ' input-' + this.size;
10437         }
10438         
10439         if (this.disabled) {
10440             input.disabled=true;
10441         }
10442         
10443         var inputblock = input;
10444         
10445         if(this.hasFeedback && !this.allowBlank){
10446             
10447             var feedback = {
10448                 tag: 'span',
10449                 cls: 'glyphicon form-control-feedback'
10450             };
10451             
10452             if(this.removable && !this.editable && !this.tickable){
10453                 inputblock = {
10454                     cls : 'has-feedback',
10455                     cn :  [
10456                         inputblock,
10457                         {
10458                             tag: 'button',
10459                             html : 'x',
10460                             cls : 'roo-combo-removable-btn close'
10461                         },
10462                         feedback
10463                     ] 
10464                 };
10465             } else {
10466                 inputblock = {
10467                     cls : 'has-feedback',
10468                     cn :  [
10469                         inputblock,
10470                         feedback
10471                     ] 
10472                 };
10473             }
10474
10475         } else {
10476             if(this.removable && !this.editable && !this.tickable){
10477                 inputblock = {
10478                     cls : 'roo-removable',
10479                     cn :  [
10480                         inputblock,
10481                         {
10482                             tag: 'button',
10483                             html : 'x',
10484                             cls : 'roo-combo-removable-btn close'
10485                         }
10486                     ] 
10487                 };
10488             }
10489         }
10490         
10491         if (this.before || this.after) {
10492             
10493             inputblock = {
10494                 cls : 'input-group',
10495                 cn :  [] 
10496             };
10497             if (this.before) {
10498                 inputblock.cn.push({
10499                     tag :'span',
10500                     cls : 'input-group-addon input-group-prepend input-group-text',
10501                     html : this.before
10502                 });
10503             }
10504             
10505             inputblock.cn.push(input);
10506             
10507             if(this.hasFeedback && !this.allowBlank){
10508                 inputblock.cls += ' has-feedback';
10509                 inputblock.cn.push(feedback);
10510             }
10511             
10512             if (this.after) {
10513                 inputblock.cn.push({
10514                     tag :'span',
10515                     cls : 'input-group-addon input-group-append input-group-text',
10516                     html : this.after
10517                 });
10518             }
10519             
10520         };
10521         
10522       
10523         
10524         var ibwrap = inputblock;
10525         
10526         if(this.multiple){
10527             ibwrap = {
10528                 tag: 'ul',
10529                 cls: 'roo-select2-choices',
10530                 cn:[
10531                     {
10532                         tag: 'li',
10533                         cls: 'roo-select2-search-field',
10534                         cn: [
10535
10536                             inputblock
10537                         ]
10538                     }
10539                 ]
10540             };
10541                 
10542         }
10543         
10544         var combobox = {
10545             cls: 'roo-select2-container input-group',
10546             cn: [
10547                  {
10548                     tag: 'input',
10549                     type : 'hidden',
10550                     cls: 'form-hidden-field'
10551                 },
10552                 ibwrap
10553             ]
10554         };
10555         
10556         if(!this.multiple && this.showToggleBtn){
10557             
10558             var caret = {
10559                         tag: 'span',
10560                         cls: 'caret'
10561              };
10562             if (this.caret != false) {
10563                 caret = {
10564                      tag: 'i',
10565                      cls: 'fa fa-' + this.caret
10566                 };
10567                 
10568             }
10569             
10570             combobox.cn.push({
10571                 tag :'span',
10572                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10573                 cn : [
10574                     caret,
10575                     {
10576                         tag: 'span',
10577                         cls: 'combobox-clear',
10578                         cn  : [
10579                             {
10580                                 tag : 'i',
10581                                 cls: 'icon-remove'
10582                             }
10583                         ]
10584                     }
10585                 ]
10586
10587             })
10588         }
10589         
10590         if(this.multiple){
10591             combobox.cls += ' roo-select2-container-multi';
10592         }
10593          var indicator = {
10594             tag : 'i',
10595             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10596             tooltip : 'This field is required'
10597         };
10598         if (Roo.bootstrap.version == 4) {
10599             indicator = {
10600                 tag : 'i',
10601                 style : 'display:none'
10602             };
10603         }
10604         
10605         
10606         if (align ==='left' && this.fieldLabel.length) {
10607             
10608             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10609
10610             cfg.cn = [
10611                 indicator,
10612                 {
10613                     tag: 'label',
10614                     'for' :  id,
10615                     cls : 'control-label',
10616                     html : this.fieldLabel
10617
10618                 },
10619                 {
10620                     cls : "", 
10621                     cn: [
10622                         combobox
10623                     ]
10624                 }
10625
10626             ];
10627             
10628             var labelCfg = cfg.cn[1];
10629             var contentCfg = cfg.cn[2];
10630             
10631             if(this.indicatorpos == 'right'){
10632                 cfg.cn = [
10633                     {
10634                         tag: 'label',
10635                         'for' :  id,
10636                         cls : 'control-label',
10637                         cn : [
10638                             {
10639                                 tag : 'span',
10640                                 html : this.fieldLabel
10641                             },
10642                             indicator
10643                         ]
10644                     },
10645                     {
10646                         cls : "", 
10647                         cn: [
10648                             combobox
10649                         ]
10650                     }
10651
10652                 ];
10653                 
10654                 labelCfg = cfg.cn[0];
10655                 contentCfg = cfg.cn[1];
10656             }
10657             
10658             if(this.labelWidth > 12){
10659                 labelCfg.style = "width: " + this.labelWidth + 'px';
10660             }
10661             
10662             if(this.labelWidth < 13 && this.labelmd == 0){
10663                 this.labelmd = this.labelWidth;
10664             }
10665             
10666             if(this.labellg > 0){
10667                 labelCfg.cls += ' col-lg-' + this.labellg;
10668                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10669             }
10670             
10671             if(this.labelmd > 0){
10672                 labelCfg.cls += ' col-md-' + this.labelmd;
10673                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10674             }
10675             
10676             if(this.labelsm > 0){
10677                 labelCfg.cls += ' col-sm-' + this.labelsm;
10678                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10679             }
10680             
10681             if(this.labelxs > 0){
10682                 labelCfg.cls += ' col-xs-' + this.labelxs;
10683                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10684             }
10685             
10686         } else if ( this.fieldLabel.length) {
10687 //                Roo.log(" label");
10688             cfg.cn = [
10689                 indicator,
10690                {
10691                    tag: 'label',
10692                    //cls : 'input-group-addon',
10693                    html : this.fieldLabel
10694
10695                },
10696
10697                combobox
10698
10699             ];
10700             
10701             if(this.indicatorpos == 'right'){
10702                 
10703                 cfg.cn = [
10704                     {
10705                        tag: 'label',
10706                        cn : [
10707                            {
10708                                tag : 'span',
10709                                html : this.fieldLabel
10710                            },
10711                            indicator
10712                        ]
10713
10714                     },
10715                     combobox
10716
10717                 ];
10718
10719             }
10720
10721         } else {
10722             
10723 //                Roo.log(" no label && no align");
10724                 cfg = combobox
10725                      
10726                 
10727         }
10728         
10729         var settings=this;
10730         ['xs','sm','md','lg'].map(function(size){
10731             if (settings[size]) {
10732                 cfg.cls += ' col-' + size + '-' + settings[size];
10733             }
10734         });
10735         
10736         return cfg;
10737         
10738     },
10739     
10740     
10741     
10742     // private
10743     onResize : function(w, h){
10744 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10745 //        if(typeof w == 'number'){
10746 //            var x = w - this.trigger.getWidth();
10747 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10748 //            this.trigger.setStyle('left', x+'px');
10749 //        }
10750     },
10751
10752     // private
10753     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10754
10755     // private
10756     getResizeEl : function(){
10757         return this.inputEl();
10758     },
10759
10760     // private
10761     getPositionEl : function(){
10762         return this.inputEl();
10763     },
10764
10765     // private
10766     alignErrorIcon : function(){
10767         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10768     },
10769
10770     // private
10771     initEvents : function(){
10772         
10773         this.createList();
10774         
10775         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10776         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10777         if(!this.multiple && this.showToggleBtn){
10778             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10779             if(this.hideTrigger){
10780                 this.trigger.setDisplayed(false);
10781             }
10782             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10783         }
10784         
10785         if(this.multiple){
10786             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10787         }
10788         
10789         if(this.removable && !this.editable && !this.tickable){
10790             var close = this.closeTriggerEl();
10791             
10792             if(close){
10793                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10794                 close.on('click', this.removeBtnClick, this, close);
10795             }
10796         }
10797         
10798         //this.trigger.addClassOnOver('x-form-trigger-over');
10799         //this.trigger.addClassOnClick('x-form-trigger-click');
10800         
10801         //if(!this.width){
10802         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10803         //}
10804     },
10805     
10806     closeTriggerEl : function()
10807     {
10808         var close = this.el.select('.roo-combo-removable-btn', true).first();
10809         return close ? close : false;
10810     },
10811     
10812     removeBtnClick : function(e, h, el)
10813     {
10814         e.preventDefault();
10815         
10816         if(this.fireEvent("remove", this) !== false){
10817             this.reset();
10818             this.fireEvent("afterremove", this)
10819         }
10820     },
10821     
10822     createList : function()
10823     {
10824         this.list = Roo.get(document.body).createChild({
10825             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10826             cls: 'typeahead typeahead-long dropdown-menu',
10827             style: 'display:none'
10828         });
10829         
10830         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10831         
10832     },
10833
10834     // private
10835     initTrigger : function(){
10836        
10837     },
10838
10839     // private
10840     onDestroy : function(){
10841         if(this.trigger){
10842             this.trigger.removeAllListeners();
10843           //  this.trigger.remove();
10844         }
10845         //if(this.wrap){
10846         //    this.wrap.remove();
10847         //}
10848         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10849     },
10850
10851     // private
10852     onFocus : function(){
10853         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10854         /*
10855         if(!this.mimicing){
10856             this.wrap.addClass('x-trigger-wrap-focus');
10857             this.mimicing = true;
10858             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10859             if(this.monitorTab){
10860                 this.el.on("keydown", this.checkTab, this);
10861             }
10862         }
10863         */
10864     },
10865
10866     // private
10867     checkTab : function(e){
10868         if(e.getKey() == e.TAB){
10869             this.triggerBlur();
10870         }
10871     },
10872
10873     // private
10874     onBlur : function(){
10875         // do nothing
10876     },
10877
10878     // private
10879     mimicBlur : function(e, t){
10880         /*
10881         if(!this.wrap.contains(t) && this.validateBlur()){
10882             this.triggerBlur();
10883         }
10884         */
10885     },
10886
10887     // private
10888     triggerBlur : function(){
10889         this.mimicing = false;
10890         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10891         if(this.monitorTab){
10892             this.el.un("keydown", this.checkTab, this);
10893         }
10894         //this.wrap.removeClass('x-trigger-wrap-focus');
10895         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10896     },
10897
10898     // private
10899     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10900     validateBlur : function(e, t){
10901         return true;
10902     },
10903
10904     // private
10905     onDisable : function(){
10906         this.inputEl().dom.disabled = true;
10907         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10908         //if(this.wrap){
10909         //    this.wrap.addClass('x-item-disabled');
10910         //}
10911     },
10912
10913     // private
10914     onEnable : function(){
10915         this.inputEl().dom.disabled = false;
10916         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10917         //if(this.wrap){
10918         //    this.el.removeClass('x-item-disabled');
10919         //}
10920     },
10921
10922     // private
10923     onShow : function(){
10924         var ae = this.getActionEl();
10925         
10926         if(ae){
10927             ae.dom.style.display = '';
10928             ae.dom.style.visibility = 'visible';
10929         }
10930     },
10931
10932     // private
10933     
10934     onHide : function(){
10935         var ae = this.getActionEl();
10936         ae.dom.style.display = 'none';
10937     },
10938
10939     /**
10940      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10941      * by an implementing function.
10942      * @method
10943      * @param {EventObject} e
10944      */
10945     onTriggerClick : Roo.emptyFn
10946 });
10947  /*
10948  * Based on:
10949  * Ext JS Library 1.1.1
10950  * Copyright(c) 2006-2007, Ext JS, LLC.
10951  *
10952  * Originally Released Under LGPL - original licence link has changed is not relivant.
10953  *
10954  * Fork - LGPL
10955  * <script type="text/javascript">
10956  */
10957
10958
10959 /**
10960  * @class Roo.data.SortTypes
10961  * @singleton
10962  * Defines the default sorting (casting?) comparison functions used when sorting data.
10963  */
10964 Roo.data.SortTypes = {
10965     /**
10966      * Default sort that does nothing
10967      * @param {Mixed} s The value being converted
10968      * @return {Mixed} The comparison value
10969      */
10970     none : function(s){
10971         return s;
10972     },
10973     
10974     /**
10975      * The regular expression used to strip tags
10976      * @type {RegExp}
10977      * @property
10978      */
10979     stripTagsRE : /<\/?[^>]+>/gi,
10980     
10981     /**
10982      * Strips all HTML tags to sort on text only
10983      * @param {Mixed} s The value being converted
10984      * @return {String} The comparison value
10985      */
10986     asText : function(s){
10987         return String(s).replace(this.stripTagsRE, "");
10988     },
10989     
10990     /**
10991      * Strips all HTML tags to sort on text only - Case insensitive
10992      * @param {Mixed} s The value being converted
10993      * @return {String} The comparison value
10994      */
10995     asUCText : function(s){
10996         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10997     },
10998     
10999     /**
11000      * Case insensitive string
11001      * @param {Mixed} s The value being converted
11002      * @return {String} The comparison value
11003      */
11004     asUCString : function(s) {
11005         return String(s).toUpperCase();
11006     },
11007     
11008     /**
11009      * Date sorting
11010      * @param {Mixed} s The value being converted
11011      * @return {Number} The comparison value
11012      */
11013     asDate : function(s) {
11014         if(!s){
11015             return 0;
11016         }
11017         if(s instanceof Date){
11018             return s.getTime();
11019         }
11020         return Date.parse(String(s));
11021     },
11022     
11023     /**
11024      * Float sorting
11025      * @param {Mixed} s The value being converted
11026      * @return {Float} The comparison value
11027      */
11028     asFloat : function(s) {
11029         var val = parseFloat(String(s).replace(/,/g, ""));
11030         if(isNaN(val)) {
11031             val = 0;
11032         }
11033         return val;
11034     },
11035     
11036     /**
11037      * Integer sorting
11038      * @param {Mixed} s The value being converted
11039      * @return {Number} The comparison value
11040      */
11041     asInt : function(s) {
11042         var val = parseInt(String(s).replace(/,/g, ""));
11043         if(isNaN(val)) {
11044             val = 0;
11045         }
11046         return val;
11047     }
11048 };/*
11049  * Based on:
11050  * Ext JS Library 1.1.1
11051  * Copyright(c) 2006-2007, Ext JS, LLC.
11052  *
11053  * Originally Released Under LGPL - original licence link has changed is not relivant.
11054  *
11055  * Fork - LGPL
11056  * <script type="text/javascript">
11057  */
11058
11059 /**
11060 * @class Roo.data.Record
11061  * Instances of this class encapsulate both record <em>definition</em> information, and record
11062  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11063  * to access Records cached in an {@link Roo.data.Store} object.<br>
11064  * <p>
11065  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11066  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11067  * objects.<br>
11068  * <p>
11069  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11070  * @constructor
11071  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11072  * {@link #create}. The parameters are the same.
11073  * @param {Array} data An associative Array of data values keyed by the field name.
11074  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11075  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11076  * not specified an integer id is generated.
11077  */
11078 Roo.data.Record = function(data, id){
11079     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11080     this.data = data;
11081 };
11082
11083 /**
11084  * Generate a constructor for a specific record layout.
11085  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11086  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11087  * Each field definition object may contain the following properties: <ul>
11088  * <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,
11089  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11090  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11091  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11092  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11093  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11094  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11095  * this may be omitted.</p></li>
11096  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11097  * <ul><li>auto (Default, implies no conversion)</li>
11098  * <li>string</li>
11099  * <li>int</li>
11100  * <li>float</li>
11101  * <li>boolean</li>
11102  * <li>date</li></ul></p></li>
11103  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11104  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11105  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11106  * by the Reader into an object that will be stored in the Record. It is passed the
11107  * following parameters:<ul>
11108  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11109  * </ul></p></li>
11110  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11111  * </ul>
11112  * <br>usage:<br><pre><code>
11113 var TopicRecord = Roo.data.Record.create(
11114     {name: 'title', mapping: 'topic_title'},
11115     {name: 'author', mapping: 'username'},
11116     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11117     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11118     {name: 'lastPoster', mapping: 'user2'},
11119     {name: 'excerpt', mapping: 'post_text'}
11120 );
11121
11122 var myNewRecord = new TopicRecord({
11123     title: 'Do my job please',
11124     author: 'noobie',
11125     totalPosts: 1,
11126     lastPost: new Date(),
11127     lastPoster: 'Animal',
11128     excerpt: 'No way dude!'
11129 });
11130 myStore.add(myNewRecord);
11131 </code></pre>
11132  * @method create
11133  * @static
11134  */
11135 Roo.data.Record.create = function(o){
11136     var f = function(){
11137         f.superclass.constructor.apply(this, arguments);
11138     };
11139     Roo.extend(f, Roo.data.Record);
11140     var p = f.prototype;
11141     p.fields = new Roo.util.MixedCollection(false, function(field){
11142         return field.name;
11143     });
11144     for(var i = 0, len = o.length; i < len; i++){
11145         p.fields.add(new Roo.data.Field(o[i]));
11146     }
11147     f.getField = function(name){
11148         return p.fields.get(name);  
11149     };
11150     return f;
11151 };
11152
11153 Roo.data.Record.AUTO_ID = 1000;
11154 Roo.data.Record.EDIT = 'edit';
11155 Roo.data.Record.REJECT = 'reject';
11156 Roo.data.Record.COMMIT = 'commit';
11157
11158 Roo.data.Record.prototype = {
11159     /**
11160      * Readonly flag - true if this record has been modified.
11161      * @type Boolean
11162      */
11163     dirty : false,
11164     editing : false,
11165     error: null,
11166     modified: null,
11167
11168     // private
11169     join : function(store){
11170         this.store = store;
11171     },
11172
11173     /**
11174      * Set the named field to the specified value.
11175      * @param {String} name The name of the field to set.
11176      * @param {Object} value The value to set the field to.
11177      */
11178     set : function(name, value){
11179         if(this.data[name] == value){
11180             return;
11181         }
11182         this.dirty = true;
11183         if(!this.modified){
11184             this.modified = {};
11185         }
11186         if(typeof this.modified[name] == 'undefined'){
11187             this.modified[name] = this.data[name];
11188         }
11189         this.data[name] = value;
11190         if(!this.editing && this.store){
11191             this.store.afterEdit(this);
11192         }       
11193     },
11194
11195     /**
11196      * Get the value of the named field.
11197      * @param {String} name The name of the field to get the value of.
11198      * @return {Object} The value of the field.
11199      */
11200     get : function(name){
11201         return this.data[name]; 
11202     },
11203
11204     // private
11205     beginEdit : function(){
11206         this.editing = true;
11207         this.modified = {}; 
11208     },
11209
11210     // private
11211     cancelEdit : function(){
11212         this.editing = false;
11213         delete this.modified;
11214     },
11215
11216     // private
11217     endEdit : function(){
11218         this.editing = false;
11219         if(this.dirty && this.store){
11220             this.store.afterEdit(this);
11221         }
11222     },
11223
11224     /**
11225      * Usually called by the {@link Roo.data.Store} which owns the Record.
11226      * Rejects all changes made to the Record since either creation, or the last commit operation.
11227      * Modified fields are reverted to their original values.
11228      * <p>
11229      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11230      * of reject operations.
11231      */
11232     reject : function(){
11233         var m = this.modified;
11234         for(var n in m){
11235             if(typeof m[n] != "function"){
11236                 this.data[n] = m[n];
11237             }
11238         }
11239         this.dirty = false;
11240         delete this.modified;
11241         this.editing = false;
11242         if(this.store){
11243             this.store.afterReject(this);
11244         }
11245     },
11246
11247     /**
11248      * Usually called by the {@link Roo.data.Store} which owns the Record.
11249      * Commits all changes made to the Record since either creation, or the last commit operation.
11250      * <p>
11251      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11252      * of commit operations.
11253      */
11254     commit : function(){
11255         this.dirty = false;
11256         delete this.modified;
11257         this.editing = false;
11258         if(this.store){
11259             this.store.afterCommit(this);
11260         }
11261     },
11262
11263     // private
11264     hasError : function(){
11265         return this.error != null;
11266     },
11267
11268     // private
11269     clearError : function(){
11270         this.error = null;
11271     },
11272
11273     /**
11274      * Creates a copy of this record.
11275      * @param {String} id (optional) A new record id if you don't want to use this record's id
11276      * @return {Record}
11277      */
11278     copy : function(newId) {
11279         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11280     }
11281 };/*
11282  * Based on:
11283  * Ext JS Library 1.1.1
11284  * Copyright(c) 2006-2007, Ext JS, LLC.
11285  *
11286  * Originally Released Under LGPL - original licence link has changed is not relivant.
11287  *
11288  * Fork - LGPL
11289  * <script type="text/javascript">
11290  */
11291
11292
11293
11294 /**
11295  * @class Roo.data.Store
11296  * @extends Roo.util.Observable
11297  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11298  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11299  * <p>
11300  * 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
11301  * has no knowledge of the format of the data returned by the Proxy.<br>
11302  * <p>
11303  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11304  * instances from the data object. These records are cached and made available through accessor functions.
11305  * @constructor
11306  * Creates a new Store.
11307  * @param {Object} config A config object containing the objects needed for the Store to access data,
11308  * and read the data into Records.
11309  */
11310 Roo.data.Store = function(config){
11311     this.data = new Roo.util.MixedCollection(false);
11312     this.data.getKey = function(o){
11313         return o.id;
11314     };
11315     this.baseParams = {};
11316     // private
11317     this.paramNames = {
11318         "start" : "start",
11319         "limit" : "limit",
11320         "sort" : "sort",
11321         "dir" : "dir",
11322         "multisort" : "_multisort"
11323     };
11324
11325     if(config && config.data){
11326         this.inlineData = config.data;
11327         delete config.data;
11328     }
11329
11330     Roo.apply(this, config);
11331     
11332     if(this.reader){ // reader passed
11333         this.reader = Roo.factory(this.reader, Roo.data);
11334         this.reader.xmodule = this.xmodule || false;
11335         if(!this.recordType){
11336             this.recordType = this.reader.recordType;
11337         }
11338         if(this.reader.onMetaChange){
11339             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11340         }
11341     }
11342
11343     if(this.recordType){
11344         this.fields = this.recordType.prototype.fields;
11345     }
11346     this.modified = [];
11347
11348     this.addEvents({
11349         /**
11350          * @event datachanged
11351          * Fires when the data cache has changed, and a widget which is using this Store
11352          * as a Record cache should refresh its view.
11353          * @param {Store} this
11354          */
11355         datachanged : true,
11356         /**
11357          * @event metachange
11358          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11359          * @param {Store} this
11360          * @param {Object} meta The JSON metadata
11361          */
11362         metachange : true,
11363         /**
11364          * @event add
11365          * Fires when Records have been added to the Store
11366          * @param {Store} this
11367          * @param {Roo.data.Record[]} records The array of Records added
11368          * @param {Number} index The index at which the record(s) were added
11369          */
11370         add : true,
11371         /**
11372          * @event remove
11373          * Fires when a Record has been removed from the Store
11374          * @param {Store} this
11375          * @param {Roo.data.Record} record The Record that was removed
11376          * @param {Number} index The index at which the record was removed
11377          */
11378         remove : true,
11379         /**
11380          * @event update
11381          * Fires when a Record has been updated
11382          * @param {Store} this
11383          * @param {Roo.data.Record} record The Record that was updated
11384          * @param {String} operation The update operation being performed.  Value may be one of:
11385          * <pre><code>
11386  Roo.data.Record.EDIT
11387  Roo.data.Record.REJECT
11388  Roo.data.Record.COMMIT
11389          * </code></pre>
11390          */
11391         update : true,
11392         /**
11393          * @event clear
11394          * Fires when the data cache has been cleared.
11395          * @param {Store} this
11396          */
11397         clear : true,
11398         /**
11399          * @event beforeload
11400          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11401          * the load action will be canceled.
11402          * @param {Store} this
11403          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11404          */
11405         beforeload : true,
11406         /**
11407          * @event beforeloadadd
11408          * Fires after a new set of Records has been loaded.
11409          * @param {Store} this
11410          * @param {Roo.data.Record[]} records The Records that were loaded
11411          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11412          */
11413         beforeloadadd : true,
11414         /**
11415          * @event load
11416          * Fires after a new set of Records has been loaded, before they are added to the store.
11417          * @param {Store} this
11418          * @param {Roo.data.Record[]} records The Records that were loaded
11419          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11420          * @params {Object} return from reader
11421          */
11422         load : true,
11423         /**
11424          * @event loadexception
11425          * Fires if an exception occurs in the Proxy during loading.
11426          * Called with the signature of the Proxy's "loadexception" event.
11427          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11428          * 
11429          * @param {Proxy} 
11430          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11431          * @param {Object} load options 
11432          * @param {Object} jsonData from your request (normally this contains the Exception)
11433          */
11434         loadexception : true
11435     });
11436     
11437     if(this.proxy){
11438         this.proxy = Roo.factory(this.proxy, Roo.data);
11439         this.proxy.xmodule = this.xmodule || false;
11440         this.relayEvents(this.proxy,  ["loadexception"]);
11441     }
11442     this.sortToggle = {};
11443     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11444
11445     Roo.data.Store.superclass.constructor.call(this);
11446
11447     if(this.inlineData){
11448         this.loadData(this.inlineData);
11449         delete this.inlineData;
11450     }
11451 };
11452
11453 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11454      /**
11455     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11456     * without a remote query - used by combo/forms at present.
11457     */
11458     
11459     /**
11460     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11461     */
11462     /**
11463     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11464     */
11465     /**
11466     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11467     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11468     */
11469     /**
11470     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11471     * on any HTTP request
11472     */
11473     /**
11474     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11475     */
11476     /**
11477     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11478     */
11479     multiSort: false,
11480     /**
11481     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11482     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11483     */
11484     remoteSort : false,
11485
11486     /**
11487     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11488      * loaded or when a record is removed. (defaults to false).
11489     */
11490     pruneModifiedRecords : false,
11491
11492     // private
11493     lastOptions : null,
11494
11495     /**
11496      * Add Records to the Store and fires the add event.
11497      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11498      */
11499     add : function(records){
11500         records = [].concat(records);
11501         for(var i = 0, len = records.length; i < len; i++){
11502             records[i].join(this);
11503         }
11504         var index = this.data.length;
11505         this.data.addAll(records);
11506         this.fireEvent("add", this, records, index);
11507     },
11508
11509     /**
11510      * Remove a Record from the Store and fires the remove event.
11511      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11512      */
11513     remove : function(record){
11514         var index = this.data.indexOf(record);
11515         this.data.removeAt(index);
11516  
11517         if(this.pruneModifiedRecords){
11518             this.modified.remove(record);
11519         }
11520         this.fireEvent("remove", this, record, index);
11521     },
11522
11523     /**
11524      * Remove all Records from the Store and fires the clear event.
11525      */
11526     removeAll : function(){
11527         this.data.clear();
11528         if(this.pruneModifiedRecords){
11529             this.modified = [];
11530         }
11531         this.fireEvent("clear", this);
11532     },
11533
11534     /**
11535      * Inserts Records to the Store at the given index and fires the add event.
11536      * @param {Number} index The start index at which to insert the passed Records.
11537      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11538      */
11539     insert : function(index, records){
11540         records = [].concat(records);
11541         for(var i = 0, len = records.length; i < len; i++){
11542             this.data.insert(index, records[i]);
11543             records[i].join(this);
11544         }
11545         this.fireEvent("add", this, records, index);
11546     },
11547
11548     /**
11549      * Get the index within the cache of the passed Record.
11550      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11551      * @return {Number} The index of the passed Record. Returns -1 if not found.
11552      */
11553     indexOf : function(record){
11554         return this.data.indexOf(record);
11555     },
11556
11557     /**
11558      * Get the index within the cache of the Record with the passed id.
11559      * @param {String} id The id of the Record to find.
11560      * @return {Number} The index of the Record. Returns -1 if not found.
11561      */
11562     indexOfId : function(id){
11563         return this.data.indexOfKey(id);
11564     },
11565
11566     /**
11567      * Get the Record with the specified id.
11568      * @param {String} id The id of the Record to find.
11569      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11570      */
11571     getById : function(id){
11572         return this.data.key(id);
11573     },
11574
11575     /**
11576      * Get the Record at the specified index.
11577      * @param {Number} index The index of the Record to find.
11578      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11579      */
11580     getAt : function(index){
11581         return this.data.itemAt(index);
11582     },
11583
11584     /**
11585      * Returns a range of Records between specified indices.
11586      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11587      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11588      * @return {Roo.data.Record[]} An array of Records
11589      */
11590     getRange : function(start, end){
11591         return this.data.getRange(start, end);
11592     },
11593
11594     // private
11595     storeOptions : function(o){
11596         o = Roo.apply({}, o);
11597         delete o.callback;
11598         delete o.scope;
11599         this.lastOptions = o;
11600     },
11601
11602     /**
11603      * Loads the Record cache from the configured Proxy using the configured Reader.
11604      * <p>
11605      * If using remote paging, then the first load call must specify the <em>start</em>
11606      * and <em>limit</em> properties in the options.params property to establish the initial
11607      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11608      * <p>
11609      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11610      * and this call will return before the new data has been loaded. Perform any post-processing
11611      * in a callback function, or in a "load" event handler.</strong>
11612      * <p>
11613      * @param {Object} options An object containing properties which control loading options:<ul>
11614      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11615      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11616      * passed the following arguments:<ul>
11617      * <li>r : Roo.data.Record[]</li>
11618      * <li>options: Options object from the load call</li>
11619      * <li>success: Boolean success indicator</li></ul></li>
11620      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11621      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11622      * </ul>
11623      */
11624     load : function(options){
11625         options = options || {};
11626         if(this.fireEvent("beforeload", this, options) !== false){
11627             this.storeOptions(options);
11628             var p = Roo.apply(options.params || {}, this.baseParams);
11629             // if meta was not loaded from remote source.. try requesting it.
11630             if (!this.reader.metaFromRemote) {
11631                 p._requestMeta = 1;
11632             }
11633             if(this.sortInfo && this.remoteSort){
11634                 var pn = this.paramNames;
11635                 p[pn["sort"]] = this.sortInfo.field;
11636                 p[pn["dir"]] = this.sortInfo.direction;
11637             }
11638             if (this.multiSort) {
11639                 var pn = this.paramNames;
11640                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11641             }
11642             
11643             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11644         }
11645     },
11646
11647     /**
11648      * Reloads the Record cache from the configured Proxy using the configured Reader and
11649      * the options from the last load operation performed.
11650      * @param {Object} options (optional) An object containing properties which may override the options
11651      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11652      * the most recently used options are reused).
11653      */
11654     reload : function(options){
11655         this.load(Roo.applyIf(options||{}, this.lastOptions));
11656     },
11657
11658     // private
11659     // Called as a callback by the Reader during a load operation.
11660     loadRecords : function(o, options, success){
11661         if(!o || success === false){
11662             if(success !== false){
11663                 this.fireEvent("load", this, [], options, o);
11664             }
11665             if(options.callback){
11666                 options.callback.call(options.scope || this, [], options, false);
11667             }
11668             return;
11669         }
11670         // if data returned failure - throw an exception.
11671         if (o.success === false) {
11672             // show a message if no listener is registered.
11673             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11674                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11675             }
11676             // loadmask wil be hooked into this..
11677             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11678             return;
11679         }
11680         var r = o.records, t = o.totalRecords || r.length;
11681         
11682         this.fireEvent("beforeloadadd", this, r, options, o);
11683         
11684         if(!options || options.add !== true){
11685             if(this.pruneModifiedRecords){
11686                 this.modified = [];
11687             }
11688             for(var i = 0, len = r.length; i < len; i++){
11689                 r[i].join(this);
11690             }
11691             if(this.snapshot){
11692                 this.data = this.snapshot;
11693                 delete this.snapshot;
11694             }
11695             this.data.clear();
11696             this.data.addAll(r);
11697             this.totalLength = t;
11698             this.applySort();
11699             this.fireEvent("datachanged", this);
11700         }else{
11701             this.totalLength = Math.max(t, this.data.length+r.length);
11702             this.add(r);
11703         }
11704         
11705         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11706                 
11707             var e = new Roo.data.Record({});
11708
11709             e.set(this.parent.displayField, this.parent.emptyTitle);
11710             e.set(this.parent.valueField, '');
11711
11712             this.insert(0, e);
11713         }
11714             
11715         this.fireEvent("load", this, r, options, o);
11716         if(options.callback){
11717             options.callback.call(options.scope || this, r, options, true);
11718         }
11719     },
11720
11721
11722     /**
11723      * Loads data from a passed data block. A Reader which understands the format of the data
11724      * must have been configured in the constructor.
11725      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11726      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11727      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11728      */
11729     loadData : function(o, append){
11730         var r = this.reader.readRecords(o);
11731         this.loadRecords(r, {add: append}, true);
11732     },
11733
11734     /**
11735      * Gets the number of cached records.
11736      * <p>
11737      * <em>If using paging, this may not be the total size of the dataset. If the data object
11738      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11739      * the data set size</em>
11740      */
11741     getCount : function(){
11742         return this.data.length || 0;
11743     },
11744
11745     /**
11746      * Gets the total number of records in the dataset as returned by the server.
11747      * <p>
11748      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11749      * the dataset size</em>
11750      */
11751     getTotalCount : function(){
11752         return this.totalLength || 0;
11753     },
11754
11755     /**
11756      * Returns the sort state of the Store as an object with two properties:
11757      * <pre><code>
11758  field {String} The name of the field by which the Records are sorted
11759  direction {String} The sort order, "ASC" or "DESC"
11760      * </code></pre>
11761      */
11762     getSortState : function(){
11763         return this.sortInfo;
11764     },
11765
11766     // private
11767     applySort : function(){
11768         if(this.sortInfo && !this.remoteSort){
11769             var s = this.sortInfo, f = s.field;
11770             var st = this.fields.get(f).sortType;
11771             var fn = function(r1, r2){
11772                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11773                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11774             };
11775             this.data.sort(s.direction, fn);
11776             if(this.snapshot && this.snapshot != this.data){
11777                 this.snapshot.sort(s.direction, fn);
11778             }
11779         }
11780     },
11781
11782     /**
11783      * Sets the default sort column and order to be used by the next load operation.
11784      * @param {String} fieldName The name of the field to sort by.
11785      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11786      */
11787     setDefaultSort : function(field, dir){
11788         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11789     },
11790
11791     /**
11792      * Sort the Records.
11793      * If remote sorting is used, the sort is performed on the server, and the cache is
11794      * reloaded. If local sorting is used, the cache is sorted internally.
11795      * @param {String} fieldName The name of the field to sort by.
11796      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11797      */
11798     sort : function(fieldName, dir){
11799         var f = this.fields.get(fieldName);
11800         if(!dir){
11801             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11802             
11803             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11804                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11805             }else{
11806                 dir = f.sortDir;
11807             }
11808         }
11809         this.sortToggle[f.name] = dir;
11810         this.sortInfo = {field: f.name, direction: dir};
11811         if(!this.remoteSort){
11812             this.applySort();
11813             this.fireEvent("datachanged", this);
11814         }else{
11815             this.load(this.lastOptions);
11816         }
11817     },
11818
11819     /**
11820      * Calls the specified function for each of the Records in the cache.
11821      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11822      * Returning <em>false</em> aborts and exits the iteration.
11823      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11824      */
11825     each : function(fn, scope){
11826         this.data.each(fn, scope);
11827     },
11828
11829     /**
11830      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11831      * (e.g., during paging).
11832      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11833      */
11834     getModifiedRecords : function(){
11835         return this.modified;
11836     },
11837
11838     // private
11839     createFilterFn : function(property, value, anyMatch){
11840         if(!value.exec){ // not a regex
11841             value = String(value);
11842             if(value.length == 0){
11843                 return false;
11844             }
11845             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11846         }
11847         return function(r){
11848             return value.test(r.data[property]);
11849         };
11850     },
11851
11852     /**
11853      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11854      * @param {String} property A field on your records
11855      * @param {Number} start The record index to start at (defaults to 0)
11856      * @param {Number} end The last record index to include (defaults to length - 1)
11857      * @return {Number} The sum
11858      */
11859     sum : function(property, start, end){
11860         var rs = this.data.items, v = 0;
11861         start = start || 0;
11862         end = (end || end === 0) ? end : rs.length-1;
11863
11864         for(var i = start; i <= end; i++){
11865             v += (rs[i].data[property] || 0);
11866         }
11867         return v;
11868     },
11869
11870     /**
11871      * Filter the records by a specified property.
11872      * @param {String} field A field on your records
11873      * @param {String/RegExp} value Either a string that the field
11874      * should start with or a RegExp to test against the field
11875      * @param {Boolean} anyMatch True to match any part not just the beginning
11876      */
11877     filter : function(property, value, anyMatch){
11878         var fn = this.createFilterFn(property, value, anyMatch);
11879         return fn ? this.filterBy(fn) : this.clearFilter();
11880     },
11881
11882     /**
11883      * Filter by a function. The specified function will be called with each
11884      * record in this data source. If the function returns true the record is included,
11885      * otherwise it is filtered.
11886      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11887      * @param {Object} scope (optional) The scope of the function (defaults to this)
11888      */
11889     filterBy : function(fn, scope){
11890         this.snapshot = this.snapshot || this.data;
11891         this.data = this.queryBy(fn, scope||this);
11892         this.fireEvent("datachanged", this);
11893     },
11894
11895     /**
11896      * Query the records by a specified property.
11897      * @param {String} field A field on your records
11898      * @param {String/RegExp} value Either a string that the field
11899      * should start with or a RegExp to test against the field
11900      * @param {Boolean} anyMatch True to match any part not just the beginning
11901      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11902      */
11903     query : function(property, value, anyMatch){
11904         var fn = this.createFilterFn(property, value, anyMatch);
11905         return fn ? this.queryBy(fn) : this.data.clone();
11906     },
11907
11908     /**
11909      * Query by a function. The specified function will be called with each
11910      * record in this data source. If the function returns true the record is included
11911      * in the results.
11912      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11913      * @param {Object} scope (optional) The scope of the function (defaults to this)
11914       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11915      **/
11916     queryBy : function(fn, scope){
11917         var data = this.snapshot || this.data;
11918         return data.filterBy(fn, scope||this);
11919     },
11920
11921     /**
11922      * Collects unique values for a particular dataIndex from this store.
11923      * @param {String} dataIndex The property to collect
11924      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11925      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11926      * @return {Array} An array of the unique values
11927      **/
11928     collect : function(dataIndex, allowNull, bypassFilter){
11929         var d = (bypassFilter === true && this.snapshot) ?
11930                 this.snapshot.items : this.data.items;
11931         var v, sv, r = [], l = {};
11932         for(var i = 0, len = d.length; i < len; i++){
11933             v = d[i].data[dataIndex];
11934             sv = String(v);
11935             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11936                 l[sv] = true;
11937                 r[r.length] = v;
11938             }
11939         }
11940         return r;
11941     },
11942
11943     /**
11944      * Revert to a view of the Record cache with no filtering applied.
11945      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11946      */
11947     clearFilter : function(suppressEvent){
11948         if(this.snapshot && this.snapshot != this.data){
11949             this.data = this.snapshot;
11950             delete this.snapshot;
11951             if(suppressEvent !== true){
11952                 this.fireEvent("datachanged", this);
11953             }
11954         }
11955     },
11956
11957     // private
11958     afterEdit : function(record){
11959         if(this.modified.indexOf(record) == -1){
11960             this.modified.push(record);
11961         }
11962         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11963     },
11964     
11965     // private
11966     afterReject : function(record){
11967         this.modified.remove(record);
11968         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11969     },
11970
11971     // private
11972     afterCommit : function(record){
11973         this.modified.remove(record);
11974         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11975     },
11976
11977     /**
11978      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11979      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11980      */
11981     commitChanges : function(){
11982         var m = this.modified.slice(0);
11983         this.modified = [];
11984         for(var i = 0, len = m.length; i < len; i++){
11985             m[i].commit();
11986         }
11987     },
11988
11989     /**
11990      * Cancel outstanding changes on all changed records.
11991      */
11992     rejectChanges : function(){
11993         var m = this.modified.slice(0);
11994         this.modified = [];
11995         for(var i = 0, len = m.length; i < len; i++){
11996             m[i].reject();
11997         }
11998     },
11999
12000     onMetaChange : function(meta, rtype, o){
12001         this.recordType = rtype;
12002         this.fields = rtype.prototype.fields;
12003         delete this.snapshot;
12004         this.sortInfo = meta.sortInfo || this.sortInfo;
12005         this.modified = [];
12006         this.fireEvent('metachange', this, this.reader.meta);
12007     },
12008     
12009     moveIndex : function(data, type)
12010     {
12011         var index = this.indexOf(data);
12012         
12013         var newIndex = index + type;
12014         
12015         this.remove(data);
12016         
12017         this.insert(newIndex, data);
12018         
12019     }
12020 });/*
12021  * Based on:
12022  * Ext JS Library 1.1.1
12023  * Copyright(c) 2006-2007, Ext JS, LLC.
12024  *
12025  * Originally Released Under LGPL - original licence link has changed is not relivant.
12026  *
12027  * Fork - LGPL
12028  * <script type="text/javascript">
12029  */
12030
12031 /**
12032  * @class Roo.data.SimpleStore
12033  * @extends Roo.data.Store
12034  * Small helper class to make creating Stores from Array data easier.
12035  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12036  * @cfg {Array} fields An array of field definition objects, or field name strings.
12037  * @cfg {Array} data The multi-dimensional array of data
12038  * @constructor
12039  * @param {Object} config
12040  */
12041 Roo.data.SimpleStore = function(config){
12042     Roo.data.SimpleStore.superclass.constructor.call(this, {
12043         isLocal : true,
12044         reader: new Roo.data.ArrayReader({
12045                 id: config.id
12046             },
12047             Roo.data.Record.create(config.fields)
12048         ),
12049         proxy : new Roo.data.MemoryProxy(config.data)
12050     });
12051     this.load();
12052 };
12053 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12054  * Based on:
12055  * Ext JS Library 1.1.1
12056  * Copyright(c) 2006-2007, Ext JS, LLC.
12057  *
12058  * Originally Released Under LGPL - original licence link has changed is not relivant.
12059  *
12060  * Fork - LGPL
12061  * <script type="text/javascript">
12062  */
12063
12064 /**
12065 /**
12066  * @extends Roo.data.Store
12067  * @class Roo.data.JsonStore
12068  * Small helper class to make creating Stores for JSON data easier. <br/>
12069 <pre><code>
12070 var store = new Roo.data.JsonStore({
12071     url: 'get-images.php',
12072     root: 'images',
12073     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12074 });
12075 </code></pre>
12076  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12077  * JsonReader and HttpProxy (unless inline data is provided).</b>
12078  * @cfg {Array} fields An array of field definition objects, or field name strings.
12079  * @constructor
12080  * @param {Object} config
12081  */
12082 Roo.data.JsonStore = function(c){
12083     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12084         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12085         reader: new Roo.data.JsonReader(c, c.fields)
12086     }));
12087 };
12088 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12089  * Based on:
12090  * Ext JS Library 1.1.1
12091  * Copyright(c) 2006-2007, Ext JS, LLC.
12092  *
12093  * Originally Released Under LGPL - original licence link has changed is not relivant.
12094  *
12095  * Fork - LGPL
12096  * <script type="text/javascript">
12097  */
12098
12099  
12100 Roo.data.Field = function(config){
12101     if(typeof config == "string"){
12102         config = {name: config};
12103     }
12104     Roo.apply(this, config);
12105     
12106     if(!this.type){
12107         this.type = "auto";
12108     }
12109     
12110     var st = Roo.data.SortTypes;
12111     // named sortTypes are supported, here we look them up
12112     if(typeof this.sortType == "string"){
12113         this.sortType = st[this.sortType];
12114     }
12115     
12116     // set default sortType for strings and dates
12117     if(!this.sortType){
12118         switch(this.type){
12119             case "string":
12120                 this.sortType = st.asUCString;
12121                 break;
12122             case "date":
12123                 this.sortType = st.asDate;
12124                 break;
12125             default:
12126                 this.sortType = st.none;
12127         }
12128     }
12129
12130     // define once
12131     var stripRe = /[\$,%]/g;
12132
12133     // prebuilt conversion function for this field, instead of
12134     // switching every time we're reading a value
12135     if(!this.convert){
12136         var cv, dateFormat = this.dateFormat;
12137         switch(this.type){
12138             case "":
12139             case "auto":
12140             case undefined:
12141                 cv = function(v){ return v; };
12142                 break;
12143             case "string":
12144                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12145                 break;
12146             case "int":
12147                 cv = function(v){
12148                     return v !== undefined && v !== null && v !== '' ?
12149                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12150                     };
12151                 break;
12152             case "float":
12153                 cv = function(v){
12154                     return v !== undefined && v !== null && v !== '' ?
12155                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12156                     };
12157                 break;
12158             case "bool":
12159             case "boolean":
12160                 cv = function(v){ return v === true || v === "true" || v == 1; };
12161                 break;
12162             case "date":
12163                 cv = function(v){
12164                     if(!v){
12165                         return '';
12166                     }
12167                     if(v instanceof Date){
12168                         return v;
12169                     }
12170                     if(dateFormat){
12171                         if(dateFormat == "timestamp"){
12172                             return new Date(v*1000);
12173                         }
12174                         return Date.parseDate(v, dateFormat);
12175                     }
12176                     var parsed = Date.parse(v);
12177                     return parsed ? new Date(parsed) : null;
12178                 };
12179              break;
12180             
12181         }
12182         this.convert = cv;
12183     }
12184 };
12185
12186 Roo.data.Field.prototype = {
12187     dateFormat: null,
12188     defaultValue: "",
12189     mapping: null,
12190     sortType : null,
12191     sortDir : "ASC"
12192 };/*
12193  * Based on:
12194  * Ext JS Library 1.1.1
12195  * Copyright(c) 2006-2007, Ext JS, LLC.
12196  *
12197  * Originally Released Under LGPL - original licence link has changed is not relivant.
12198  *
12199  * Fork - LGPL
12200  * <script type="text/javascript">
12201  */
12202  
12203 // Base class for reading structured data from a data source.  This class is intended to be
12204 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12205
12206 /**
12207  * @class Roo.data.DataReader
12208  * Base class for reading structured data from a data source.  This class is intended to be
12209  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12210  */
12211
12212 Roo.data.DataReader = function(meta, recordType){
12213     
12214     this.meta = meta;
12215     
12216     this.recordType = recordType instanceof Array ? 
12217         Roo.data.Record.create(recordType) : recordType;
12218 };
12219
12220 Roo.data.DataReader.prototype = {
12221      /**
12222      * Create an empty record
12223      * @param {Object} data (optional) - overlay some values
12224      * @return {Roo.data.Record} record created.
12225      */
12226     newRow :  function(d) {
12227         var da =  {};
12228         this.recordType.prototype.fields.each(function(c) {
12229             switch( c.type) {
12230                 case 'int' : da[c.name] = 0; break;
12231                 case 'date' : da[c.name] = new Date(); break;
12232                 case 'float' : da[c.name] = 0.0; break;
12233                 case 'boolean' : da[c.name] = false; break;
12234                 default : da[c.name] = ""; break;
12235             }
12236             
12237         });
12238         return new this.recordType(Roo.apply(da, d));
12239     }
12240     
12241 };/*
12242  * Based on:
12243  * Ext JS Library 1.1.1
12244  * Copyright(c) 2006-2007, Ext JS, LLC.
12245  *
12246  * Originally Released Under LGPL - original licence link has changed is not relivant.
12247  *
12248  * Fork - LGPL
12249  * <script type="text/javascript">
12250  */
12251
12252 /**
12253  * @class Roo.data.DataProxy
12254  * @extends Roo.data.Observable
12255  * This class is an abstract base class for implementations which provide retrieval of
12256  * unformatted data objects.<br>
12257  * <p>
12258  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12259  * (of the appropriate type which knows how to parse the data object) to provide a block of
12260  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12261  * <p>
12262  * Custom implementations must implement the load method as described in
12263  * {@link Roo.data.HttpProxy#load}.
12264  */
12265 Roo.data.DataProxy = function(){
12266     this.addEvents({
12267         /**
12268          * @event beforeload
12269          * Fires before a network request is made to retrieve a data object.
12270          * @param {Object} This DataProxy object.
12271          * @param {Object} params The params parameter to the load function.
12272          */
12273         beforeload : true,
12274         /**
12275          * @event load
12276          * Fires before the load method's callback is called.
12277          * @param {Object} This DataProxy object.
12278          * @param {Object} o The data object.
12279          * @param {Object} arg The callback argument object passed to the load function.
12280          */
12281         load : true,
12282         /**
12283          * @event loadexception
12284          * Fires if an Exception occurs during data retrieval.
12285          * @param {Object} This DataProxy object.
12286          * @param {Object} o The data object.
12287          * @param {Object} arg The callback argument object passed to the load function.
12288          * @param {Object} e The Exception.
12289          */
12290         loadexception : true
12291     });
12292     Roo.data.DataProxy.superclass.constructor.call(this);
12293 };
12294
12295 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12296
12297     /**
12298      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12299      */
12300 /*
12301  * Based on:
12302  * Ext JS Library 1.1.1
12303  * Copyright(c) 2006-2007, Ext JS, LLC.
12304  *
12305  * Originally Released Under LGPL - original licence link has changed is not relivant.
12306  *
12307  * Fork - LGPL
12308  * <script type="text/javascript">
12309  */
12310 /**
12311  * @class Roo.data.MemoryProxy
12312  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12313  * to the Reader when its load method is called.
12314  * @constructor
12315  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12316  */
12317 Roo.data.MemoryProxy = function(data){
12318     if (data.data) {
12319         data = data.data;
12320     }
12321     Roo.data.MemoryProxy.superclass.constructor.call(this);
12322     this.data = data;
12323 };
12324
12325 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12326     
12327     /**
12328      * Load data from the requested source (in this case an in-memory
12329      * data object passed to the constructor), read the data object into
12330      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12331      * process that block using the passed callback.
12332      * @param {Object} params This parameter is not used by the MemoryProxy class.
12333      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12334      * object into a block of Roo.data.Records.
12335      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12336      * The function must be passed <ul>
12337      * <li>The Record block object</li>
12338      * <li>The "arg" argument from the load function</li>
12339      * <li>A boolean success indicator</li>
12340      * </ul>
12341      * @param {Object} scope The scope in which to call the callback
12342      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12343      */
12344     load : function(params, reader, callback, scope, arg){
12345         params = params || {};
12346         var result;
12347         try {
12348             result = reader.readRecords(this.data);
12349         }catch(e){
12350             this.fireEvent("loadexception", this, arg, null, e);
12351             callback.call(scope, null, arg, false);
12352             return;
12353         }
12354         callback.call(scope, result, arg, true);
12355     },
12356     
12357     // private
12358     update : function(params, records){
12359         
12360     }
12361 });/*
12362  * Based on:
12363  * Ext JS Library 1.1.1
12364  * Copyright(c) 2006-2007, Ext JS, LLC.
12365  *
12366  * Originally Released Under LGPL - original licence link has changed is not relivant.
12367  *
12368  * Fork - LGPL
12369  * <script type="text/javascript">
12370  */
12371 /**
12372  * @class Roo.data.HttpProxy
12373  * @extends Roo.data.DataProxy
12374  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12375  * configured to reference a certain URL.<br><br>
12376  * <p>
12377  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12378  * from which the running page was served.<br><br>
12379  * <p>
12380  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12381  * <p>
12382  * Be aware that to enable the browser to parse an XML document, the server must set
12383  * the Content-Type header in the HTTP response to "text/xml".
12384  * @constructor
12385  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12386  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12387  * will be used to make the request.
12388  */
12389 Roo.data.HttpProxy = function(conn){
12390     Roo.data.HttpProxy.superclass.constructor.call(this);
12391     // is conn a conn config or a real conn?
12392     this.conn = conn;
12393     this.useAjax = !conn || !conn.events;
12394   
12395 };
12396
12397 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12398     // thse are take from connection...
12399     
12400     /**
12401      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12402      */
12403     /**
12404      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12405      * extra parameters to each request made by this object. (defaults to undefined)
12406      */
12407     /**
12408      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12409      *  to each request made by this object. (defaults to undefined)
12410      */
12411     /**
12412      * @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)
12413      */
12414     /**
12415      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12416      */
12417      /**
12418      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12419      * @type Boolean
12420      */
12421   
12422
12423     /**
12424      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12425      * @type Boolean
12426      */
12427     /**
12428      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12429      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12430      * a finer-grained basis than the DataProxy events.
12431      */
12432     getConnection : function(){
12433         return this.useAjax ? Roo.Ajax : this.conn;
12434     },
12435
12436     /**
12437      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12438      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12439      * process that block using the passed callback.
12440      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12441      * for the request to the remote server.
12442      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12443      * object into a block of Roo.data.Records.
12444      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12445      * The function must be passed <ul>
12446      * <li>The Record block object</li>
12447      * <li>The "arg" argument from the load function</li>
12448      * <li>A boolean success indicator</li>
12449      * </ul>
12450      * @param {Object} scope The scope in which to call the callback
12451      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12452      */
12453     load : function(params, reader, callback, scope, arg){
12454         if(this.fireEvent("beforeload", this, params) !== false){
12455             var  o = {
12456                 params : params || {},
12457                 request: {
12458                     callback : callback,
12459                     scope : scope,
12460                     arg : arg
12461                 },
12462                 reader: reader,
12463                 callback : this.loadResponse,
12464                 scope: this
12465             };
12466             if(this.useAjax){
12467                 Roo.applyIf(o, this.conn);
12468                 if(this.activeRequest){
12469                     Roo.Ajax.abort(this.activeRequest);
12470                 }
12471                 this.activeRequest = Roo.Ajax.request(o);
12472             }else{
12473                 this.conn.request(o);
12474             }
12475         }else{
12476             callback.call(scope||this, null, arg, false);
12477         }
12478     },
12479
12480     // private
12481     loadResponse : function(o, success, response){
12482         delete this.activeRequest;
12483         if(!success){
12484             this.fireEvent("loadexception", this, o, response);
12485             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12486             return;
12487         }
12488         var result;
12489         try {
12490             result = o.reader.read(response);
12491         }catch(e){
12492             this.fireEvent("loadexception", this, o, response, e);
12493             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12494             return;
12495         }
12496         
12497         this.fireEvent("load", this, o, o.request.arg);
12498         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12499     },
12500
12501     // private
12502     update : function(dataSet){
12503
12504     },
12505
12506     // private
12507     updateResponse : function(dataSet){
12508
12509     }
12510 });/*
12511  * Based on:
12512  * Ext JS Library 1.1.1
12513  * Copyright(c) 2006-2007, Ext JS, LLC.
12514  *
12515  * Originally Released Under LGPL - original licence link has changed is not relivant.
12516  *
12517  * Fork - LGPL
12518  * <script type="text/javascript">
12519  */
12520
12521 /**
12522  * @class Roo.data.ScriptTagProxy
12523  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12524  * other than the originating domain of the running page.<br><br>
12525  * <p>
12526  * <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
12527  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12528  * <p>
12529  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12530  * source code that is used as the source inside a &lt;script> tag.<br><br>
12531  * <p>
12532  * In order for the browser to process the returned data, the server must wrap the data object
12533  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12534  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12535  * depending on whether the callback name was passed:
12536  * <p>
12537  * <pre><code>
12538 boolean scriptTag = false;
12539 String cb = request.getParameter("callback");
12540 if (cb != null) {
12541     scriptTag = true;
12542     response.setContentType("text/javascript");
12543 } else {
12544     response.setContentType("application/x-json");
12545 }
12546 Writer out = response.getWriter();
12547 if (scriptTag) {
12548     out.write(cb + "(");
12549 }
12550 out.print(dataBlock.toJsonString());
12551 if (scriptTag) {
12552     out.write(");");
12553 }
12554 </pre></code>
12555  *
12556  * @constructor
12557  * @param {Object} config A configuration object.
12558  */
12559 Roo.data.ScriptTagProxy = function(config){
12560     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12561     Roo.apply(this, config);
12562     this.head = document.getElementsByTagName("head")[0];
12563 };
12564
12565 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12566
12567 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12568     /**
12569      * @cfg {String} url The URL from which to request the data object.
12570      */
12571     /**
12572      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12573      */
12574     timeout : 30000,
12575     /**
12576      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12577      * the server the name of the callback function set up by the load call to process the returned data object.
12578      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12579      * javascript output which calls this named function passing the data object as its only parameter.
12580      */
12581     callbackParam : "callback",
12582     /**
12583      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12584      * name to the request.
12585      */
12586     nocache : true,
12587
12588     /**
12589      * Load data from the configured URL, read the data object into
12590      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12591      * process that block using the passed callback.
12592      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12593      * for the request to the remote server.
12594      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12595      * object into a block of Roo.data.Records.
12596      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12597      * The function must be passed <ul>
12598      * <li>The Record block object</li>
12599      * <li>The "arg" argument from the load function</li>
12600      * <li>A boolean success indicator</li>
12601      * </ul>
12602      * @param {Object} scope The scope in which to call the callback
12603      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12604      */
12605     load : function(params, reader, callback, scope, arg){
12606         if(this.fireEvent("beforeload", this, params) !== false){
12607
12608             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12609
12610             var url = this.url;
12611             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12612             if(this.nocache){
12613                 url += "&_dc=" + (new Date().getTime());
12614             }
12615             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12616             var trans = {
12617                 id : transId,
12618                 cb : "stcCallback"+transId,
12619                 scriptId : "stcScript"+transId,
12620                 params : params,
12621                 arg : arg,
12622                 url : url,
12623                 callback : callback,
12624                 scope : scope,
12625                 reader : reader
12626             };
12627             var conn = this;
12628
12629             window[trans.cb] = function(o){
12630                 conn.handleResponse(o, trans);
12631             };
12632
12633             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12634
12635             if(this.autoAbort !== false){
12636                 this.abort();
12637             }
12638
12639             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12640
12641             var script = document.createElement("script");
12642             script.setAttribute("src", url);
12643             script.setAttribute("type", "text/javascript");
12644             script.setAttribute("id", trans.scriptId);
12645             this.head.appendChild(script);
12646
12647             this.trans = trans;
12648         }else{
12649             callback.call(scope||this, null, arg, false);
12650         }
12651     },
12652
12653     // private
12654     isLoading : function(){
12655         return this.trans ? true : false;
12656     },
12657
12658     /**
12659      * Abort the current server request.
12660      */
12661     abort : function(){
12662         if(this.isLoading()){
12663             this.destroyTrans(this.trans);
12664         }
12665     },
12666
12667     // private
12668     destroyTrans : function(trans, isLoaded){
12669         this.head.removeChild(document.getElementById(trans.scriptId));
12670         clearTimeout(trans.timeoutId);
12671         if(isLoaded){
12672             window[trans.cb] = undefined;
12673             try{
12674                 delete window[trans.cb];
12675             }catch(e){}
12676         }else{
12677             // if hasn't been loaded, wait for load to remove it to prevent script error
12678             window[trans.cb] = function(){
12679                 window[trans.cb] = undefined;
12680                 try{
12681                     delete window[trans.cb];
12682                 }catch(e){}
12683             };
12684         }
12685     },
12686
12687     // private
12688     handleResponse : function(o, trans){
12689         this.trans = false;
12690         this.destroyTrans(trans, true);
12691         var result;
12692         try {
12693             result = trans.reader.readRecords(o);
12694         }catch(e){
12695             this.fireEvent("loadexception", this, o, trans.arg, e);
12696             trans.callback.call(trans.scope||window, null, trans.arg, false);
12697             return;
12698         }
12699         this.fireEvent("load", this, o, trans.arg);
12700         trans.callback.call(trans.scope||window, result, trans.arg, true);
12701     },
12702
12703     // private
12704     handleFailure : function(trans){
12705         this.trans = false;
12706         this.destroyTrans(trans, false);
12707         this.fireEvent("loadexception", this, null, trans.arg);
12708         trans.callback.call(trans.scope||window, null, trans.arg, false);
12709     }
12710 });/*
12711  * Based on:
12712  * Ext JS Library 1.1.1
12713  * Copyright(c) 2006-2007, Ext JS, LLC.
12714  *
12715  * Originally Released Under LGPL - original licence link has changed is not relivant.
12716  *
12717  * Fork - LGPL
12718  * <script type="text/javascript">
12719  */
12720
12721 /**
12722  * @class Roo.data.JsonReader
12723  * @extends Roo.data.DataReader
12724  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12725  * based on mappings in a provided Roo.data.Record constructor.
12726  * 
12727  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12728  * in the reply previously. 
12729  * 
12730  * <p>
12731  * Example code:
12732  * <pre><code>
12733 var RecordDef = Roo.data.Record.create([
12734     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12735     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12736 ]);
12737 var myReader = new Roo.data.JsonReader({
12738     totalProperty: "results",    // The property which contains the total dataset size (optional)
12739     root: "rows",                // The property which contains an Array of row objects
12740     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12741 }, RecordDef);
12742 </code></pre>
12743  * <p>
12744  * This would consume a JSON file like this:
12745  * <pre><code>
12746 { 'results': 2, 'rows': [
12747     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12748     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12749 }
12750 </code></pre>
12751  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12752  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12753  * paged from the remote server.
12754  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12755  * @cfg {String} root name of the property which contains the Array of row objects.
12756  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12757  * @cfg {Array} fields Array of field definition objects
12758  * @constructor
12759  * Create a new JsonReader
12760  * @param {Object} meta Metadata configuration options
12761  * @param {Object} recordType Either an Array of field definition objects,
12762  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12763  */
12764 Roo.data.JsonReader = function(meta, recordType){
12765     
12766     meta = meta || {};
12767     // set some defaults:
12768     Roo.applyIf(meta, {
12769         totalProperty: 'total',
12770         successProperty : 'success',
12771         root : 'data',
12772         id : 'id'
12773     });
12774     
12775     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12776 };
12777 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12778     
12779     /**
12780      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12781      * Used by Store query builder to append _requestMeta to params.
12782      * 
12783      */
12784     metaFromRemote : false,
12785     /**
12786      * This method is only used by a DataProxy which has retrieved data from a remote server.
12787      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12788      * @return {Object} data A data block which is used by an Roo.data.Store object as
12789      * a cache of Roo.data.Records.
12790      */
12791     read : function(response){
12792         var json = response.responseText;
12793        
12794         var o = /* eval:var:o */ eval("("+json+")");
12795         if(!o) {
12796             throw {message: "JsonReader.read: Json object not found"};
12797         }
12798         
12799         if(o.metaData){
12800             
12801             delete this.ef;
12802             this.metaFromRemote = true;
12803             this.meta = o.metaData;
12804             this.recordType = Roo.data.Record.create(o.metaData.fields);
12805             this.onMetaChange(this.meta, this.recordType, o);
12806         }
12807         return this.readRecords(o);
12808     },
12809
12810     // private function a store will implement
12811     onMetaChange : function(meta, recordType, o){
12812
12813     },
12814
12815     /**
12816          * @ignore
12817          */
12818     simpleAccess: function(obj, subsc) {
12819         return obj[subsc];
12820     },
12821
12822         /**
12823          * @ignore
12824          */
12825     getJsonAccessor: function(){
12826         var re = /[\[\.]/;
12827         return function(expr) {
12828             try {
12829                 return(re.test(expr))
12830                     ? new Function("obj", "return obj." + expr)
12831                     : function(obj){
12832                         return obj[expr];
12833                     };
12834             } catch(e){}
12835             return Roo.emptyFn;
12836         };
12837     }(),
12838
12839     /**
12840      * Create a data block containing Roo.data.Records from an XML document.
12841      * @param {Object} o An object which contains an Array of row objects in the property specified
12842      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12843      * which contains the total size of the dataset.
12844      * @return {Object} data A data block which is used by an Roo.data.Store object as
12845      * a cache of Roo.data.Records.
12846      */
12847     readRecords : function(o){
12848         /**
12849          * After any data loads, the raw JSON data is available for further custom processing.
12850          * @type Object
12851          */
12852         this.o = o;
12853         var s = this.meta, Record = this.recordType,
12854             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12855
12856 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12857         if (!this.ef) {
12858             if(s.totalProperty) {
12859                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12860                 }
12861                 if(s.successProperty) {
12862                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12863                 }
12864                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12865                 if (s.id) {
12866                         var g = this.getJsonAccessor(s.id);
12867                         this.getId = function(rec) {
12868                                 var r = g(rec);  
12869                                 return (r === undefined || r === "") ? null : r;
12870                         };
12871                 } else {
12872                         this.getId = function(){return null;};
12873                 }
12874             this.ef = [];
12875             for(var jj = 0; jj < fl; jj++){
12876                 f = fi[jj];
12877                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12878                 this.ef[jj] = this.getJsonAccessor(map);
12879             }
12880         }
12881
12882         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12883         if(s.totalProperty){
12884             var vt = parseInt(this.getTotal(o), 10);
12885             if(!isNaN(vt)){
12886                 totalRecords = vt;
12887             }
12888         }
12889         if(s.successProperty){
12890             var vs = this.getSuccess(o);
12891             if(vs === false || vs === 'false'){
12892                 success = false;
12893             }
12894         }
12895         var records = [];
12896         for(var i = 0; i < c; i++){
12897                 var n = root[i];
12898             var values = {};
12899             var id = this.getId(n);
12900             for(var j = 0; j < fl; j++){
12901                 f = fi[j];
12902             var v = this.ef[j](n);
12903             if (!f.convert) {
12904                 Roo.log('missing convert for ' + f.name);
12905                 Roo.log(f);
12906                 continue;
12907             }
12908             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12909             }
12910             var record = new Record(values, id);
12911             record.json = n;
12912             records[i] = record;
12913         }
12914         return {
12915             raw : o,
12916             success : success,
12917             records : records,
12918             totalRecords : totalRecords
12919         };
12920     }
12921 });/*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931
12932 /**
12933  * @class Roo.data.ArrayReader
12934  * @extends Roo.data.DataReader
12935  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12936  * Each element of that Array represents a row of data fields. The
12937  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12938  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12939  * <p>
12940  * Example code:.
12941  * <pre><code>
12942 var RecordDef = Roo.data.Record.create([
12943     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12944     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12945 ]);
12946 var myReader = new Roo.data.ArrayReader({
12947     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12948 }, RecordDef);
12949 </code></pre>
12950  * <p>
12951  * This would consume an Array like this:
12952  * <pre><code>
12953 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12954   </code></pre>
12955  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12956  * @constructor
12957  * Create a new JsonReader
12958  * @param {Object} meta Metadata configuration options.
12959  * @param {Object} recordType Either an Array of field definition objects
12960  * as specified to {@link Roo.data.Record#create},
12961  * or an {@link Roo.data.Record} object
12962  * created using {@link Roo.data.Record#create}.
12963  */
12964 Roo.data.ArrayReader = function(meta, recordType){
12965     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12966 };
12967
12968 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12969     /**
12970      * Create a data block containing Roo.data.Records from an XML document.
12971      * @param {Object} o An Array of row objects which represents the dataset.
12972      * @return {Object} data A data block which is used by an Roo.data.Store object as
12973      * a cache of Roo.data.Records.
12974      */
12975     readRecords : function(o){
12976         var sid = this.meta ? this.meta.id : null;
12977         var recordType = this.recordType, fields = recordType.prototype.fields;
12978         var records = [];
12979         var root = o;
12980             for(var i = 0; i < root.length; i++){
12981                     var n = root[i];
12982                 var values = {};
12983                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12984                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12985                 var f = fields.items[j];
12986                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12987                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12988                 v = f.convert(v);
12989                 values[f.name] = v;
12990             }
12991                 var record = new recordType(values, id);
12992                 record.json = n;
12993                 records[records.length] = record;
12994             }
12995             return {
12996                 records : records,
12997                 totalRecords : records.length
12998             };
12999     }
13000 });/*
13001  * - LGPL
13002  * * 
13003  */
13004
13005 /**
13006  * @class Roo.bootstrap.ComboBox
13007  * @extends Roo.bootstrap.TriggerField
13008  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13009  * @cfg {Boolean} append (true|false) default false
13010  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13011  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13012  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13013  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13014  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13015  * @cfg {Boolean} animate default true
13016  * @cfg {Boolean} emptyResultText only for touch device
13017  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13018  * @cfg {String} emptyTitle default ''
13019  * @constructor
13020  * Create a new ComboBox.
13021  * @param {Object} config Configuration options
13022  */
13023 Roo.bootstrap.ComboBox = function(config){
13024     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13025     this.addEvents({
13026         /**
13027          * @event expand
13028          * Fires when the dropdown list is expanded
13029         * @param {Roo.bootstrap.ComboBox} combo This combo box
13030         */
13031         'expand' : true,
13032         /**
13033          * @event collapse
13034          * Fires when the dropdown list is collapsed
13035         * @param {Roo.bootstrap.ComboBox} combo This combo box
13036         */
13037         'collapse' : true,
13038         /**
13039          * @event beforeselect
13040          * Fires before a list item is selected. Return false to cancel the selection.
13041         * @param {Roo.bootstrap.ComboBox} combo This combo box
13042         * @param {Roo.data.Record} record The data record returned from the underlying store
13043         * @param {Number} index The index of the selected item in the dropdown list
13044         */
13045         'beforeselect' : true,
13046         /**
13047          * @event select
13048          * Fires when a list item is selected
13049         * @param {Roo.bootstrap.ComboBox} combo This combo box
13050         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13051         * @param {Number} index The index of the selected item in the dropdown list
13052         */
13053         'select' : true,
13054         /**
13055          * @event beforequery
13056          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13057          * The event object passed has these properties:
13058         * @param {Roo.bootstrap.ComboBox} combo This combo box
13059         * @param {String} query The query
13060         * @param {Boolean} forceAll true to force "all" query
13061         * @param {Boolean} cancel true to cancel the query
13062         * @param {Object} e The query event object
13063         */
13064         'beforequery': true,
13065          /**
13066          * @event add
13067          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13068         * @param {Roo.bootstrap.ComboBox} combo This combo box
13069         */
13070         'add' : true,
13071         /**
13072          * @event edit
13073          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13074         * @param {Roo.bootstrap.ComboBox} combo This combo box
13075         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13076         */
13077         'edit' : true,
13078         /**
13079          * @event remove
13080          * Fires when the remove value from the combobox array
13081         * @param {Roo.bootstrap.ComboBox} combo This combo box
13082         */
13083         'remove' : true,
13084         /**
13085          * @event afterremove
13086          * Fires when the remove value from the combobox array
13087         * @param {Roo.bootstrap.ComboBox} combo This combo box
13088         */
13089         'afterremove' : true,
13090         /**
13091          * @event specialfilter
13092          * Fires when specialfilter
13093             * @param {Roo.bootstrap.ComboBox} combo This combo box
13094             */
13095         'specialfilter' : true,
13096         /**
13097          * @event tick
13098          * Fires when tick the element
13099             * @param {Roo.bootstrap.ComboBox} combo This combo box
13100             */
13101         'tick' : true,
13102         /**
13103          * @event touchviewdisplay
13104          * Fires when touch view require special display (default is using displayField)
13105             * @param {Roo.bootstrap.ComboBox} combo This combo box
13106             * @param {Object} cfg set html .
13107             */
13108         'touchviewdisplay' : true
13109         
13110     });
13111     
13112     this.item = [];
13113     this.tickItems = [];
13114     
13115     this.selectedIndex = -1;
13116     if(this.mode == 'local'){
13117         if(config.queryDelay === undefined){
13118             this.queryDelay = 10;
13119         }
13120         if(config.minChars === undefined){
13121             this.minChars = 0;
13122         }
13123     }
13124 };
13125
13126 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13127      
13128     /**
13129      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13130      * rendering into an Roo.Editor, defaults to false)
13131      */
13132     /**
13133      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13134      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13135      */
13136     /**
13137      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13138      */
13139     /**
13140      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13141      * the dropdown list (defaults to undefined, with no header element)
13142      */
13143
13144      /**
13145      * @cfg {String/Roo.Template} tpl The template to use to render the output
13146      */
13147      
13148      /**
13149      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13150      */
13151     listWidth: undefined,
13152     /**
13153      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13154      * mode = 'remote' or 'text' if mode = 'local')
13155      */
13156     displayField: undefined,
13157     
13158     /**
13159      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13160      * mode = 'remote' or 'value' if mode = 'local'). 
13161      * Note: use of a valueField requires the user make a selection
13162      * in order for a value to be mapped.
13163      */
13164     valueField: undefined,
13165     /**
13166      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13167      */
13168     modalTitle : '',
13169     
13170     /**
13171      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13172      * field's data value (defaults to the underlying DOM element's name)
13173      */
13174     hiddenName: undefined,
13175     /**
13176      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13177      */
13178     listClass: '',
13179     /**
13180      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13181      */
13182     selectedClass: 'active',
13183     
13184     /**
13185      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13186      */
13187     shadow:'sides',
13188     /**
13189      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13190      * anchor positions (defaults to 'tl-bl')
13191      */
13192     listAlign: 'tl-bl?',
13193     /**
13194      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13195      */
13196     maxHeight: 300,
13197     /**
13198      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13199      * query specified by the allQuery config option (defaults to 'query')
13200      */
13201     triggerAction: 'query',
13202     /**
13203      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13204      * (defaults to 4, does not apply if editable = false)
13205      */
13206     minChars : 4,
13207     /**
13208      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13209      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13210      */
13211     typeAhead: false,
13212     /**
13213      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13214      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13215      */
13216     queryDelay: 500,
13217     /**
13218      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13219      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13220      */
13221     pageSize: 0,
13222     /**
13223      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13224      * when editable = true (defaults to false)
13225      */
13226     selectOnFocus:false,
13227     /**
13228      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13229      */
13230     queryParam: 'query',
13231     /**
13232      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13233      * when mode = 'remote' (defaults to 'Loading...')
13234      */
13235     loadingText: 'Loading...',
13236     /**
13237      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13238      */
13239     resizable: false,
13240     /**
13241      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13242      */
13243     handleHeight : 8,
13244     /**
13245      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13246      * traditional select (defaults to true)
13247      */
13248     editable: true,
13249     /**
13250      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13251      */
13252     allQuery: '',
13253     /**
13254      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13255      */
13256     mode: 'remote',
13257     /**
13258      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13259      * listWidth has a higher value)
13260      */
13261     minListWidth : 70,
13262     /**
13263      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13264      * allow the user to set arbitrary text into the field (defaults to false)
13265      */
13266     forceSelection:false,
13267     /**
13268      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13269      * if typeAhead = true (defaults to 250)
13270      */
13271     typeAheadDelay : 250,
13272     /**
13273      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13274      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13275      */
13276     valueNotFoundText : undefined,
13277     /**
13278      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13279      */
13280     blockFocus : false,
13281     
13282     /**
13283      * @cfg {Boolean} disableClear Disable showing of clear button.
13284      */
13285     disableClear : false,
13286     /**
13287      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13288      */
13289     alwaysQuery : false,
13290     
13291     /**
13292      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13293      */
13294     multiple : false,
13295     
13296     /**
13297      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13298      */
13299     invalidClass : "has-warning",
13300     
13301     /**
13302      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13303      */
13304     validClass : "has-success",
13305     
13306     /**
13307      * @cfg {Boolean} specialFilter (true|false) special filter default false
13308      */
13309     specialFilter : false,
13310     
13311     /**
13312      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13313      */
13314     mobileTouchView : true,
13315     
13316     /**
13317      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13318      */
13319     useNativeIOS : false,
13320     
13321     /**
13322      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13323      */
13324     mobile_restrict_height : false,
13325     
13326     ios_options : false,
13327     
13328     //private
13329     addicon : false,
13330     editicon: false,
13331     
13332     page: 0,
13333     hasQuery: false,
13334     append: false,
13335     loadNext: false,
13336     autoFocus : true,
13337     tickable : false,
13338     btnPosition : 'right',
13339     triggerList : true,
13340     showToggleBtn : true,
13341     animate : true,
13342     emptyResultText: 'Empty',
13343     triggerText : 'Select',
13344     emptyTitle : '',
13345     
13346     // element that contains real text value.. (when hidden is used..)
13347     
13348     getAutoCreate : function()
13349     {   
13350         var cfg = false;
13351         //render
13352         /*
13353          * Render classic select for iso
13354          */
13355         
13356         if(Roo.isIOS && this.useNativeIOS){
13357             cfg = this.getAutoCreateNativeIOS();
13358             return cfg;
13359         }
13360         
13361         /*
13362          * Touch Devices
13363          */
13364         
13365         if(Roo.isTouch && this.mobileTouchView){
13366             cfg = this.getAutoCreateTouchView();
13367             return cfg;;
13368         }
13369         
13370         /*
13371          *  Normal ComboBox
13372          */
13373         if(!this.tickable){
13374             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13375             return cfg;
13376         }
13377         
13378         /*
13379          *  ComboBox with tickable selections
13380          */
13381              
13382         var align = this.labelAlign || this.parentLabelAlign();
13383         
13384         cfg = {
13385             cls : 'form-group roo-combobox-tickable' //input-group
13386         };
13387         
13388         var btn_text_select = '';
13389         var btn_text_done = '';
13390         var btn_text_cancel = '';
13391         
13392         if (this.btn_text_show) {
13393             btn_text_select = 'Select';
13394             btn_text_done = 'Done';
13395             btn_text_cancel = 'Cancel'; 
13396         }
13397         
13398         var buttons = {
13399             tag : 'div',
13400             cls : 'tickable-buttons',
13401             cn : [
13402                 {
13403                     tag : 'button',
13404                     type : 'button',
13405                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13406                     //html : this.triggerText
13407                     html: btn_text_select
13408                 },
13409                 {
13410                     tag : 'button',
13411                     type : 'button',
13412                     name : 'ok',
13413                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13414                     //html : 'Done'
13415                     html: btn_text_done
13416                 },
13417                 {
13418                     tag : 'button',
13419                     type : 'button',
13420                     name : 'cancel',
13421                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13422                     //html : 'Cancel'
13423                     html: btn_text_cancel
13424                 }
13425             ]
13426         };
13427         
13428         if(this.editable){
13429             buttons.cn.unshift({
13430                 tag: 'input',
13431                 cls: 'roo-select2-search-field-input'
13432             });
13433         }
13434         
13435         var _this = this;
13436         
13437         Roo.each(buttons.cn, function(c){
13438             if (_this.size) {
13439                 c.cls += ' btn-' + _this.size;
13440             }
13441
13442             if (_this.disabled) {
13443                 c.disabled = true;
13444             }
13445         });
13446         
13447         var box = {
13448             tag: 'div',
13449             style : 'display: contents',
13450             cn: [
13451                 {
13452                     tag: 'input',
13453                     type : 'hidden',
13454                     cls: 'form-hidden-field'
13455                 },
13456                 {
13457                     tag: 'ul',
13458                     cls: 'roo-select2-choices',
13459                     cn:[
13460                         {
13461                             tag: 'li',
13462                             cls: 'roo-select2-search-field',
13463                             cn: [
13464                                 buttons
13465                             ]
13466                         }
13467                     ]
13468                 }
13469             ]
13470         };
13471         
13472         var combobox = {
13473             cls: 'roo-select2-container input-group roo-select2-container-multi',
13474             cn: [
13475                 
13476                 box
13477 //                {
13478 //                    tag: 'ul',
13479 //                    cls: 'typeahead typeahead-long dropdown-menu',
13480 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13481 //                }
13482             ]
13483         };
13484         
13485         if(this.hasFeedback && !this.allowBlank){
13486             
13487             var feedback = {
13488                 tag: 'span',
13489                 cls: 'glyphicon form-control-feedback'
13490             };
13491
13492             combobox.cn.push(feedback);
13493         }
13494         
13495         var indicator = {
13496             tag : 'i',
13497             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13498             tooltip : 'This field is required'
13499         };
13500         if (Roo.bootstrap.version == 4) {
13501             indicator = {
13502                 tag : 'i',
13503                 style : 'display:none'
13504             };
13505         }
13506         if (align ==='left' && this.fieldLabel.length) {
13507             
13508             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13509             
13510             cfg.cn = [
13511                 indicator,
13512                 {
13513                     tag: 'label',
13514                     'for' :  id,
13515                     cls : 'control-label col-form-label',
13516                     html : this.fieldLabel
13517
13518                 },
13519                 {
13520                     cls : "", 
13521                     cn: [
13522                         combobox
13523                     ]
13524                 }
13525
13526             ];
13527             
13528             var labelCfg = cfg.cn[1];
13529             var contentCfg = cfg.cn[2];
13530             
13531
13532             if(this.indicatorpos == 'right'){
13533                 
13534                 cfg.cn = [
13535                     {
13536                         tag: 'label',
13537                         'for' :  id,
13538                         cls : 'control-label col-form-label',
13539                         cn : [
13540                             {
13541                                 tag : 'span',
13542                                 html : this.fieldLabel
13543                             },
13544                             indicator
13545                         ]
13546                     },
13547                     {
13548                         cls : "",
13549                         cn: [
13550                             combobox
13551                         ]
13552                     }
13553
13554                 ];
13555                 
13556                 
13557                 
13558                 labelCfg = cfg.cn[0];
13559                 contentCfg = cfg.cn[1];
13560             
13561             }
13562             
13563             if(this.labelWidth > 12){
13564                 labelCfg.style = "width: " + this.labelWidth + 'px';
13565             }
13566             
13567             if(this.labelWidth < 13 && this.labelmd == 0){
13568                 this.labelmd = this.labelWidth;
13569             }
13570             
13571             if(this.labellg > 0){
13572                 labelCfg.cls += ' col-lg-' + this.labellg;
13573                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13574             }
13575             
13576             if(this.labelmd > 0){
13577                 labelCfg.cls += ' col-md-' + this.labelmd;
13578                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13579             }
13580             
13581             if(this.labelsm > 0){
13582                 labelCfg.cls += ' col-sm-' + this.labelsm;
13583                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13584             }
13585             
13586             if(this.labelxs > 0){
13587                 labelCfg.cls += ' col-xs-' + this.labelxs;
13588                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13589             }
13590                 
13591                 
13592         } else if ( this.fieldLabel.length) {
13593 //                Roo.log(" label");
13594                  cfg.cn = [
13595                    indicator,
13596                     {
13597                         tag: 'label',
13598                         //cls : 'input-group-addon',
13599                         html : this.fieldLabel
13600                     },
13601                     combobox
13602                 ];
13603                 
13604                 if(this.indicatorpos == 'right'){
13605                     cfg.cn = [
13606                         {
13607                             tag: 'label',
13608                             //cls : 'input-group-addon',
13609                             html : this.fieldLabel
13610                         },
13611                         indicator,
13612                         combobox
13613                     ];
13614                     
13615                 }
13616
13617         } else {
13618             
13619 //                Roo.log(" no label && no align");
13620                 cfg = combobox
13621                      
13622                 
13623         }
13624          
13625         var settings=this;
13626         ['xs','sm','md','lg'].map(function(size){
13627             if (settings[size]) {
13628                 cfg.cls += ' col-' + size + '-' + settings[size];
13629             }
13630         });
13631         
13632         return cfg;
13633         
13634     },
13635     
13636     _initEventsCalled : false,
13637     
13638     // private
13639     initEvents: function()
13640     {   
13641         if (this._initEventsCalled) { // as we call render... prevent looping...
13642             return;
13643         }
13644         this._initEventsCalled = true;
13645         
13646         if (!this.store) {
13647             throw "can not find store for combo";
13648         }
13649         
13650         this.indicator = this.indicatorEl();
13651         
13652         this.store = Roo.factory(this.store, Roo.data);
13653         this.store.parent = this;
13654         
13655         // if we are building from html. then this element is so complex, that we can not really
13656         // use the rendered HTML.
13657         // so we have to trash and replace the previous code.
13658         if (Roo.XComponent.build_from_html) {
13659             // remove this element....
13660             var e = this.el.dom, k=0;
13661             while (e ) { e = e.previousSibling;  ++k;}
13662
13663             this.el.remove();
13664             
13665             this.el=false;
13666             this.rendered = false;
13667             
13668             this.render(this.parent().getChildContainer(true), k);
13669         }
13670         
13671         if(Roo.isIOS && this.useNativeIOS){
13672             this.initIOSView();
13673             return;
13674         }
13675         
13676         /*
13677          * Touch Devices
13678          */
13679         
13680         if(Roo.isTouch && this.mobileTouchView){
13681             this.initTouchView();
13682             return;
13683         }
13684         
13685         if(this.tickable){
13686             this.initTickableEvents();
13687             return;
13688         }
13689         
13690         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13691         
13692         if(this.hiddenName){
13693             
13694             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13695             
13696             this.hiddenField.dom.value =
13697                 this.hiddenValue !== undefined ? this.hiddenValue :
13698                 this.value !== undefined ? this.value : '';
13699
13700             // prevent input submission
13701             this.el.dom.removeAttribute('name');
13702             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13703              
13704              
13705         }
13706         //if(Roo.isGecko){
13707         //    this.el.dom.setAttribute('autocomplete', 'off');
13708         //}
13709         
13710         var cls = 'x-combo-list';
13711         
13712         //this.list = new Roo.Layer({
13713         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13714         //});
13715         
13716         var _this = this;
13717         
13718         (function(){
13719             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13720             _this.list.setWidth(lw);
13721         }).defer(100);
13722         
13723         this.list.on('mouseover', this.onViewOver, this);
13724         this.list.on('mousemove', this.onViewMove, this);
13725         this.list.on('scroll', this.onViewScroll, this);
13726         
13727         /*
13728         this.list.swallowEvent('mousewheel');
13729         this.assetHeight = 0;
13730
13731         if(this.title){
13732             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13733             this.assetHeight += this.header.getHeight();
13734         }
13735
13736         this.innerList = this.list.createChild({cls:cls+'-inner'});
13737         this.innerList.on('mouseover', this.onViewOver, this);
13738         this.innerList.on('mousemove', this.onViewMove, this);
13739         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13740         
13741         if(this.allowBlank && !this.pageSize && !this.disableClear){
13742             this.footer = this.list.createChild({cls:cls+'-ft'});
13743             this.pageTb = new Roo.Toolbar(this.footer);
13744            
13745         }
13746         if(this.pageSize){
13747             this.footer = this.list.createChild({cls:cls+'-ft'});
13748             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13749                     {pageSize: this.pageSize});
13750             
13751         }
13752         
13753         if (this.pageTb && this.allowBlank && !this.disableClear) {
13754             var _this = this;
13755             this.pageTb.add(new Roo.Toolbar.Fill(), {
13756                 cls: 'x-btn-icon x-btn-clear',
13757                 text: '&#160;',
13758                 handler: function()
13759                 {
13760                     _this.collapse();
13761                     _this.clearValue();
13762                     _this.onSelect(false, -1);
13763                 }
13764             });
13765         }
13766         if (this.footer) {
13767             this.assetHeight += this.footer.getHeight();
13768         }
13769         */
13770             
13771         if(!this.tpl){
13772             this.tpl = Roo.bootstrap.version == 4 ?
13773                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13774                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13775         }
13776
13777         this.view = new Roo.View(this.list, this.tpl, {
13778             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13779         });
13780         //this.view.wrapEl.setDisplayed(false);
13781         this.view.on('click', this.onViewClick, this);
13782         
13783         
13784         this.store.on('beforeload', this.onBeforeLoad, this);
13785         this.store.on('load', this.onLoad, this);
13786         this.store.on('loadexception', this.onLoadException, this);
13787         /*
13788         if(this.resizable){
13789             this.resizer = new Roo.Resizable(this.list,  {
13790                pinned:true, handles:'se'
13791             });
13792             this.resizer.on('resize', function(r, w, h){
13793                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13794                 this.listWidth = w;
13795                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13796                 this.restrictHeight();
13797             }, this);
13798             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13799         }
13800         */
13801         if(!this.editable){
13802             this.editable = true;
13803             this.setEditable(false);
13804         }
13805         
13806         /*
13807         
13808         if (typeof(this.events.add.listeners) != 'undefined') {
13809             
13810             this.addicon = this.wrap.createChild(
13811                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13812        
13813             this.addicon.on('click', function(e) {
13814                 this.fireEvent('add', this);
13815             }, this);
13816         }
13817         if (typeof(this.events.edit.listeners) != 'undefined') {
13818             
13819             this.editicon = this.wrap.createChild(
13820                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13821             if (this.addicon) {
13822                 this.editicon.setStyle('margin-left', '40px');
13823             }
13824             this.editicon.on('click', function(e) {
13825                 
13826                 // we fire even  if inothing is selected..
13827                 this.fireEvent('edit', this, this.lastData );
13828                 
13829             }, this);
13830         }
13831         */
13832         
13833         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13834             "up" : function(e){
13835                 this.inKeyMode = true;
13836                 this.selectPrev();
13837             },
13838
13839             "down" : function(e){
13840                 if(!this.isExpanded()){
13841                     this.onTriggerClick();
13842                 }else{
13843                     this.inKeyMode = true;
13844                     this.selectNext();
13845                 }
13846             },
13847
13848             "enter" : function(e){
13849 //                this.onViewClick();
13850                 //return true;
13851                 this.collapse();
13852                 
13853                 if(this.fireEvent("specialkey", this, e)){
13854                     this.onViewClick(false);
13855                 }
13856                 
13857                 return true;
13858             },
13859
13860             "esc" : function(e){
13861                 this.collapse();
13862             },
13863
13864             "tab" : function(e){
13865                 this.collapse();
13866                 
13867                 if(this.fireEvent("specialkey", this, e)){
13868                     this.onViewClick(false);
13869                 }
13870                 
13871                 return true;
13872             },
13873
13874             scope : this,
13875
13876             doRelay : function(foo, bar, hname){
13877                 if(hname == 'down' || this.scope.isExpanded()){
13878                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13879                 }
13880                 return true;
13881             },
13882
13883             forceKeyDown: true
13884         });
13885         
13886         
13887         this.queryDelay = Math.max(this.queryDelay || 10,
13888                 this.mode == 'local' ? 10 : 250);
13889         
13890         
13891         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13892         
13893         if(this.typeAhead){
13894             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13895         }
13896         if(this.editable !== false){
13897             this.inputEl().on("keyup", this.onKeyUp, this);
13898         }
13899         if(this.forceSelection){
13900             this.inputEl().on('blur', this.doForce, this);
13901         }
13902         
13903         if(this.multiple){
13904             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13905             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13906         }
13907     },
13908     
13909     initTickableEvents: function()
13910     {   
13911         this.createList();
13912         
13913         if(this.hiddenName){
13914             
13915             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13916             
13917             this.hiddenField.dom.value =
13918                 this.hiddenValue !== undefined ? this.hiddenValue :
13919                 this.value !== undefined ? this.value : '';
13920
13921             // prevent input submission
13922             this.el.dom.removeAttribute('name');
13923             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13924              
13925              
13926         }
13927         
13928 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13929         
13930         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13931         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13932         if(this.triggerList){
13933             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13934         }
13935          
13936         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13937         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13938         
13939         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13940         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13941         
13942         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13943         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13944         
13945         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13946         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13947         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13948         
13949         this.okBtn.hide();
13950         this.cancelBtn.hide();
13951         
13952         var _this = this;
13953         
13954         (function(){
13955             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13956             _this.list.setWidth(lw);
13957         }).defer(100);
13958         
13959         this.list.on('mouseover', this.onViewOver, this);
13960         this.list.on('mousemove', this.onViewMove, this);
13961         
13962         this.list.on('scroll', this.onViewScroll, this);
13963         
13964         if(!this.tpl){
13965             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13966                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13967         }
13968
13969         this.view = new Roo.View(this.list, this.tpl, {
13970             singleSelect:true,
13971             tickable:true,
13972             parent:this,
13973             store: this.store,
13974             selectedClass: this.selectedClass
13975         });
13976         
13977         //this.view.wrapEl.setDisplayed(false);
13978         this.view.on('click', this.onViewClick, this);
13979         
13980         
13981         
13982         this.store.on('beforeload', this.onBeforeLoad, this);
13983         this.store.on('load', this.onLoad, this);
13984         this.store.on('loadexception', this.onLoadException, this);
13985         
13986         if(this.editable){
13987             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13988                 "up" : function(e){
13989                     this.inKeyMode = true;
13990                     this.selectPrev();
13991                 },
13992
13993                 "down" : function(e){
13994                     this.inKeyMode = true;
13995                     this.selectNext();
13996                 },
13997
13998                 "enter" : function(e){
13999                     if(this.fireEvent("specialkey", this, e)){
14000                         this.onViewClick(false);
14001                     }
14002                     
14003                     return true;
14004                 },
14005
14006                 "esc" : function(e){
14007                     this.onTickableFooterButtonClick(e, false, false);
14008                 },
14009
14010                 "tab" : function(e){
14011                     this.fireEvent("specialkey", this, e);
14012                     
14013                     this.onTickableFooterButtonClick(e, false, false);
14014                     
14015                     return true;
14016                 },
14017
14018                 scope : this,
14019
14020                 doRelay : function(e, fn, key){
14021                     if(this.scope.isExpanded()){
14022                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14023                     }
14024                     return true;
14025                 },
14026
14027                 forceKeyDown: true
14028             });
14029         }
14030         
14031         this.queryDelay = Math.max(this.queryDelay || 10,
14032                 this.mode == 'local' ? 10 : 250);
14033         
14034         
14035         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14036         
14037         if(this.typeAhead){
14038             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14039         }
14040         
14041         if(this.editable !== false){
14042             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14043         }
14044         
14045         this.indicator = this.indicatorEl();
14046         
14047         if(this.indicator){
14048             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14049             this.indicator.hide();
14050         }
14051         
14052     },
14053
14054     onDestroy : function(){
14055         if(this.view){
14056             this.view.setStore(null);
14057             this.view.el.removeAllListeners();
14058             this.view.el.remove();
14059             this.view.purgeListeners();
14060         }
14061         if(this.list){
14062             this.list.dom.innerHTML  = '';
14063         }
14064         
14065         if(this.store){
14066             this.store.un('beforeload', this.onBeforeLoad, this);
14067             this.store.un('load', this.onLoad, this);
14068             this.store.un('loadexception', this.onLoadException, this);
14069         }
14070         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14071     },
14072
14073     // private
14074     fireKey : function(e){
14075         if(e.isNavKeyPress() && !this.list.isVisible()){
14076             this.fireEvent("specialkey", this, e);
14077         }
14078     },
14079
14080     // private
14081     onResize: function(w, h){
14082 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14083 //        
14084 //        if(typeof w != 'number'){
14085 //            // we do not handle it!?!?
14086 //            return;
14087 //        }
14088 //        var tw = this.trigger.getWidth();
14089 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14090 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14091 //        var x = w - tw;
14092 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14093 //            
14094 //        //this.trigger.setStyle('left', x+'px');
14095 //        
14096 //        if(this.list && this.listWidth === undefined){
14097 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14098 //            this.list.setWidth(lw);
14099 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14100 //        }
14101         
14102     
14103         
14104     },
14105
14106     /**
14107      * Allow or prevent the user from directly editing the field text.  If false is passed,
14108      * the user will only be able to select from the items defined in the dropdown list.  This method
14109      * is the runtime equivalent of setting the 'editable' config option at config time.
14110      * @param {Boolean} value True to allow the user to directly edit the field text
14111      */
14112     setEditable : function(value){
14113         if(value == this.editable){
14114             return;
14115         }
14116         this.editable = value;
14117         if(!value){
14118             this.inputEl().dom.setAttribute('readOnly', true);
14119             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14120             this.inputEl().addClass('x-combo-noedit');
14121         }else{
14122             this.inputEl().dom.setAttribute('readOnly', false);
14123             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14124             this.inputEl().removeClass('x-combo-noedit');
14125         }
14126     },
14127
14128     // private
14129     
14130     onBeforeLoad : function(combo,opts){
14131         if(!this.hasFocus){
14132             return;
14133         }
14134          if (!opts.add) {
14135             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14136          }
14137         this.restrictHeight();
14138         this.selectedIndex = -1;
14139     },
14140
14141     // private
14142     onLoad : function(){
14143         
14144         this.hasQuery = false;
14145         
14146         if(!this.hasFocus){
14147             return;
14148         }
14149         
14150         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14151             this.loading.hide();
14152         }
14153         
14154         if(this.store.getCount() > 0){
14155             
14156             this.expand();
14157             this.restrictHeight();
14158             if(this.lastQuery == this.allQuery){
14159                 if(this.editable && !this.tickable){
14160                     this.inputEl().dom.select();
14161                 }
14162                 
14163                 if(
14164                     !this.selectByValue(this.value, true) &&
14165                     this.autoFocus && 
14166                     (
14167                         !this.store.lastOptions ||
14168                         typeof(this.store.lastOptions.add) == 'undefined' || 
14169                         this.store.lastOptions.add != true
14170                     )
14171                 ){
14172                     this.select(0, true);
14173                 }
14174             }else{
14175                 if(this.autoFocus){
14176                     this.selectNext();
14177                 }
14178                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14179                     this.taTask.delay(this.typeAheadDelay);
14180                 }
14181             }
14182         }else{
14183             this.onEmptyResults();
14184         }
14185         
14186         //this.el.focus();
14187     },
14188     // private
14189     onLoadException : function()
14190     {
14191         this.hasQuery = false;
14192         
14193         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14194             this.loading.hide();
14195         }
14196         
14197         if(this.tickable && this.editable){
14198             return;
14199         }
14200         
14201         this.collapse();
14202         // only causes errors at present
14203         //Roo.log(this.store.reader.jsonData);
14204         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14205             // fixme
14206             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14207         //}
14208         
14209         
14210     },
14211     // private
14212     onTypeAhead : function(){
14213         if(this.store.getCount() > 0){
14214             var r = this.store.getAt(0);
14215             var newValue = r.data[this.displayField];
14216             var len = newValue.length;
14217             var selStart = this.getRawValue().length;
14218             
14219             if(selStart != len){
14220                 this.setRawValue(newValue);
14221                 this.selectText(selStart, newValue.length);
14222             }
14223         }
14224     },
14225
14226     // private
14227     onSelect : function(record, index){
14228         
14229         if(this.fireEvent('beforeselect', this, record, index) !== false){
14230         
14231             this.setFromData(index > -1 ? record.data : false);
14232             
14233             this.collapse();
14234             this.fireEvent('select', this, record, index);
14235         }
14236     },
14237
14238     /**
14239      * Returns the currently selected field value or empty string if no value is set.
14240      * @return {String} value The selected value
14241      */
14242     getValue : function()
14243     {
14244         if(Roo.isIOS && this.useNativeIOS){
14245             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14246         }
14247         
14248         if(this.multiple){
14249             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14250         }
14251         
14252         if(this.valueField){
14253             return typeof this.value != 'undefined' ? this.value : '';
14254         }else{
14255             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14256         }
14257     },
14258     
14259     getRawValue : function()
14260     {
14261         if(Roo.isIOS && this.useNativeIOS){
14262             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14263         }
14264         
14265         var v = this.inputEl().getValue();
14266         
14267         return v;
14268     },
14269
14270     /**
14271      * Clears any text/value currently set in the field
14272      */
14273     clearValue : function(){
14274         
14275         if(this.hiddenField){
14276             this.hiddenField.dom.value = '';
14277         }
14278         this.value = '';
14279         this.setRawValue('');
14280         this.lastSelectionText = '';
14281         this.lastData = false;
14282         
14283         var close = this.closeTriggerEl();
14284         
14285         if(close){
14286             close.hide();
14287         }
14288         
14289         this.validate();
14290         
14291     },
14292
14293     /**
14294      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14295      * will be displayed in the field.  If the value does not match the data value of an existing item,
14296      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14297      * Otherwise the field will be blank (although the value will still be set).
14298      * @param {String} value The value to match
14299      */
14300     setValue : function(v)
14301     {
14302         if(Roo.isIOS && this.useNativeIOS){
14303             this.setIOSValue(v);
14304             return;
14305         }
14306         
14307         if(this.multiple){
14308             this.syncValue();
14309             return;
14310         }
14311         
14312         var text = v;
14313         if(this.valueField){
14314             var r = this.findRecord(this.valueField, v);
14315             if(r){
14316                 text = r.data[this.displayField];
14317             }else if(this.valueNotFoundText !== undefined){
14318                 text = this.valueNotFoundText;
14319             }
14320         }
14321         this.lastSelectionText = text;
14322         if(this.hiddenField){
14323             this.hiddenField.dom.value = v;
14324         }
14325         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14326         this.value = v;
14327         
14328         var close = this.closeTriggerEl();
14329         
14330         if(close){
14331             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14332         }
14333         
14334         this.validate();
14335     },
14336     /**
14337      * @property {Object} the last set data for the element
14338      */
14339     
14340     lastData : false,
14341     /**
14342      * Sets the value of the field based on a object which is related to the record format for the store.
14343      * @param {Object} value the value to set as. or false on reset?
14344      */
14345     setFromData : function(o){
14346         
14347         if(this.multiple){
14348             this.addItem(o);
14349             return;
14350         }
14351             
14352         var dv = ''; // display value
14353         var vv = ''; // value value..
14354         this.lastData = o;
14355         if (this.displayField) {
14356             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14357         } else {
14358             // this is an error condition!!!
14359             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14360         }
14361         
14362         if(this.valueField){
14363             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14364         }
14365         
14366         var close = this.closeTriggerEl();
14367         
14368         if(close){
14369             if(dv.length || vv * 1 > 0){
14370                 close.show() ;
14371                 this.blockFocus=true;
14372             } else {
14373                 close.hide();
14374             }             
14375         }
14376         
14377         if(this.hiddenField){
14378             this.hiddenField.dom.value = vv;
14379             
14380             this.lastSelectionText = dv;
14381             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14382             this.value = vv;
14383             return;
14384         }
14385         // no hidden field.. - we store the value in 'value', but still display
14386         // display field!!!!
14387         this.lastSelectionText = dv;
14388         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14389         this.value = vv;
14390         
14391         
14392         
14393     },
14394     // private
14395     reset : function(){
14396         // overridden so that last data is reset..
14397         
14398         if(this.multiple){
14399             this.clearItem();
14400             return;
14401         }
14402         
14403         this.setValue(this.originalValue);
14404         //this.clearInvalid();
14405         this.lastData = false;
14406         if (this.view) {
14407             this.view.clearSelections();
14408         }
14409         
14410         this.validate();
14411     },
14412     // private
14413     findRecord : function(prop, value){
14414         var record;
14415         if(this.store.getCount() > 0){
14416             this.store.each(function(r){
14417                 if(r.data[prop] == value){
14418                     record = r;
14419                     return false;
14420                 }
14421                 return true;
14422             });
14423         }
14424         return record;
14425     },
14426     
14427     getName: function()
14428     {
14429         // returns hidden if it's set..
14430         if (!this.rendered) {return ''};
14431         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14432         
14433     },
14434     // private
14435     onViewMove : function(e, t){
14436         this.inKeyMode = false;
14437     },
14438
14439     // private
14440     onViewOver : function(e, t){
14441         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14442             return;
14443         }
14444         var item = this.view.findItemFromChild(t);
14445         
14446         if(item){
14447             var index = this.view.indexOf(item);
14448             this.select(index, false);
14449         }
14450     },
14451
14452     // private
14453     onViewClick : function(view, doFocus, el, e)
14454     {
14455         var index = this.view.getSelectedIndexes()[0];
14456         
14457         var r = this.store.getAt(index);
14458         
14459         if(this.tickable){
14460             
14461             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14462                 return;
14463             }
14464             
14465             var rm = false;
14466             var _this = this;
14467             
14468             Roo.each(this.tickItems, function(v,k){
14469                 
14470                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14471                     Roo.log(v);
14472                     _this.tickItems.splice(k, 1);
14473                     
14474                     if(typeof(e) == 'undefined' && view == false){
14475                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14476                     }
14477                     
14478                     rm = true;
14479                     return;
14480                 }
14481             });
14482             
14483             if(rm){
14484                 return;
14485             }
14486             
14487             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14488                 this.tickItems.push(r.data);
14489             }
14490             
14491             if(typeof(e) == 'undefined' && view == false){
14492                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14493             }
14494                     
14495             return;
14496         }
14497         
14498         if(r){
14499             this.onSelect(r, index);
14500         }
14501         if(doFocus !== false && !this.blockFocus){
14502             this.inputEl().focus();
14503         }
14504     },
14505
14506     // private
14507     restrictHeight : function(){
14508         //this.innerList.dom.style.height = '';
14509         //var inner = this.innerList.dom;
14510         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14511         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14512         //this.list.beginUpdate();
14513         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14514         this.list.alignTo(this.inputEl(), this.listAlign);
14515         this.list.alignTo(this.inputEl(), this.listAlign);
14516         //this.list.endUpdate();
14517     },
14518
14519     // private
14520     onEmptyResults : function(){
14521         
14522         if(this.tickable && this.editable){
14523             this.hasFocus = false;
14524             this.restrictHeight();
14525             return;
14526         }
14527         
14528         this.collapse();
14529     },
14530
14531     /**
14532      * Returns true if the dropdown list is expanded, else false.
14533      */
14534     isExpanded : function(){
14535         return this.list.isVisible();
14536     },
14537
14538     /**
14539      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14540      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14541      * @param {String} value The data value of the item to select
14542      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14543      * selected item if it is not currently in view (defaults to true)
14544      * @return {Boolean} True if the value matched an item in the list, else false
14545      */
14546     selectByValue : function(v, scrollIntoView){
14547         if(v !== undefined && v !== null){
14548             var r = this.findRecord(this.valueField || this.displayField, v);
14549             if(r){
14550                 this.select(this.store.indexOf(r), scrollIntoView);
14551                 return true;
14552             }
14553         }
14554         return false;
14555     },
14556
14557     /**
14558      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14559      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14560      * @param {Number} index The zero-based index of the list item to select
14561      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14562      * selected item if it is not currently in view (defaults to true)
14563      */
14564     select : function(index, scrollIntoView){
14565         this.selectedIndex = index;
14566         this.view.select(index);
14567         if(scrollIntoView !== false){
14568             var el = this.view.getNode(index);
14569             /*
14570              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14571              */
14572             if(el){
14573                 this.list.scrollChildIntoView(el, false);
14574             }
14575         }
14576     },
14577
14578     // private
14579     selectNext : function(){
14580         var ct = this.store.getCount();
14581         if(ct > 0){
14582             if(this.selectedIndex == -1){
14583                 this.select(0);
14584             }else if(this.selectedIndex < ct-1){
14585                 this.select(this.selectedIndex+1);
14586             }
14587         }
14588     },
14589
14590     // private
14591     selectPrev : function(){
14592         var ct = this.store.getCount();
14593         if(ct > 0){
14594             if(this.selectedIndex == -1){
14595                 this.select(0);
14596             }else if(this.selectedIndex != 0){
14597                 this.select(this.selectedIndex-1);
14598             }
14599         }
14600     },
14601
14602     // private
14603     onKeyUp : function(e){
14604         if(this.editable !== false && !e.isSpecialKey()){
14605             this.lastKey = e.getKey();
14606             this.dqTask.delay(this.queryDelay);
14607         }
14608     },
14609
14610     // private
14611     validateBlur : function(){
14612         return !this.list || !this.list.isVisible();   
14613     },
14614
14615     // private
14616     initQuery : function(){
14617         
14618         var v = this.getRawValue();
14619         
14620         if(this.tickable && this.editable){
14621             v = this.tickableInputEl().getValue();
14622         }
14623         
14624         this.doQuery(v);
14625     },
14626
14627     // private
14628     doForce : function(){
14629         if(this.inputEl().dom.value.length > 0){
14630             this.inputEl().dom.value =
14631                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14632              
14633         }
14634     },
14635
14636     /**
14637      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14638      * query allowing the query action to be canceled if needed.
14639      * @param {String} query The SQL query to execute
14640      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14641      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14642      * saved in the current store (defaults to false)
14643      */
14644     doQuery : function(q, forceAll){
14645         
14646         if(q === undefined || q === null){
14647             q = '';
14648         }
14649         var qe = {
14650             query: q,
14651             forceAll: forceAll,
14652             combo: this,
14653             cancel:false
14654         };
14655         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14656             return false;
14657         }
14658         q = qe.query;
14659         
14660         forceAll = qe.forceAll;
14661         if(forceAll === true || (q.length >= this.minChars)){
14662             
14663             this.hasQuery = true;
14664             
14665             if(this.lastQuery != q || this.alwaysQuery){
14666                 this.lastQuery = q;
14667                 if(this.mode == 'local'){
14668                     this.selectedIndex = -1;
14669                     if(forceAll){
14670                         this.store.clearFilter();
14671                     }else{
14672                         
14673                         if(this.specialFilter){
14674                             this.fireEvent('specialfilter', this);
14675                             this.onLoad();
14676                             return;
14677                         }
14678                         
14679                         this.store.filter(this.displayField, q);
14680                     }
14681                     
14682                     this.store.fireEvent("datachanged", this.store);
14683                     
14684                     this.onLoad();
14685                     
14686                     
14687                 }else{
14688                     
14689                     this.store.baseParams[this.queryParam] = q;
14690                     
14691                     var options = {params : this.getParams(q)};
14692                     
14693                     if(this.loadNext){
14694                         options.add = true;
14695                         options.params.start = this.page * this.pageSize;
14696                     }
14697                     
14698                     this.store.load(options);
14699                     
14700                     /*
14701                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14702                      *  we should expand the list on onLoad
14703                      *  so command out it
14704                      */
14705 //                    this.expand();
14706                 }
14707             }else{
14708                 this.selectedIndex = -1;
14709                 this.onLoad();   
14710             }
14711         }
14712         
14713         this.loadNext = false;
14714     },
14715     
14716     // private
14717     getParams : function(q){
14718         var p = {};
14719         //p[this.queryParam] = q;
14720         
14721         if(this.pageSize){
14722             p.start = 0;
14723             p.limit = this.pageSize;
14724         }
14725         return p;
14726     },
14727
14728     /**
14729      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14730      */
14731     collapse : function(){
14732         if(!this.isExpanded()){
14733             return;
14734         }
14735         
14736         this.list.hide();
14737         
14738         this.hasFocus = false;
14739         
14740         if(this.tickable){
14741             this.okBtn.hide();
14742             this.cancelBtn.hide();
14743             this.trigger.show();
14744             
14745             if(this.editable){
14746                 this.tickableInputEl().dom.value = '';
14747                 this.tickableInputEl().blur();
14748             }
14749             
14750         }
14751         
14752         Roo.get(document).un('mousedown', this.collapseIf, this);
14753         Roo.get(document).un('mousewheel', this.collapseIf, this);
14754         if (!this.editable) {
14755             Roo.get(document).un('keydown', this.listKeyPress, this);
14756         }
14757         this.fireEvent('collapse', this);
14758         
14759         this.validate();
14760     },
14761
14762     // private
14763     collapseIf : function(e){
14764         var in_combo  = e.within(this.el);
14765         var in_list =  e.within(this.list);
14766         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14767         
14768         if (in_combo || in_list || is_list) {
14769             //e.stopPropagation();
14770             return;
14771         }
14772         
14773         if(this.tickable){
14774             this.onTickableFooterButtonClick(e, false, false);
14775         }
14776
14777         this.collapse();
14778         
14779     },
14780
14781     /**
14782      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14783      */
14784     expand : function(){
14785        
14786         if(this.isExpanded() || !this.hasFocus){
14787             return;
14788         }
14789         
14790         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14791         this.list.setWidth(lw);
14792         
14793         Roo.log('expand');
14794         
14795         this.list.show();
14796         
14797         this.restrictHeight();
14798         
14799         if(this.tickable){
14800             
14801             this.tickItems = Roo.apply([], this.item);
14802             
14803             this.okBtn.show();
14804             this.cancelBtn.show();
14805             this.trigger.hide();
14806             
14807             if(this.editable){
14808                 this.tickableInputEl().focus();
14809             }
14810             
14811         }
14812         
14813         Roo.get(document).on('mousedown', this.collapseIf, this);
14814         Roo.get(document).on('mousewheel', this.collapseIf, this);
14815         if (!this.editable) {
14816             Roo.get(document).on('keydown', this.listKeyPress, this);
14817         }
14818         
14819         this.fireEvent('expand', this);
14820     },
14821
14822     // private
14823     // Implements the default empty TriggerField.onTriggerClick function
14824     onTriggerClick : function(e)
14825     {
14826         Roo.log('trigger click');
14827         
14828         if(this.disabled || !this.triggerList){
14829             return;
14830         }
14831         
14832         this.page = 0;
14833         this.loadNext = false;
14834         
14835         if(this.isExpanded()){
14836             this.collapse();
14837             if (!this.blockFocus) {
14838                 this.inputEl().focus();
14839             }
14840             
14841         }else {
14842             this.hasFocus = true;
14843             if(this.triggerAction == 'all') {
14844                 this.doQuery(this.allQuery, true);
14845             } else {
14846                 this.doQuery(this.getRawValue());
14847             }
14848             if (!this.blockFocus) {
14849                 this.inputEl().focus();
14850             }
14851         }
14852     },
14853     
14854     onTickableTriggerClick : function(e)
14855     {
14856         if(this.disabled){
14857             return;
14858         }
14859         
14860         this.page = 0;
14861         this.loadNext = false;
14862         this.hasFocus = true;
14863         
14864         if(this.triggerAction == 'all') {
14865             this.doQuery(this.allQuery, true);
14866         } else {
14867             this.doQuery(this.getRawValue());
14868         }
14869     },
14870     
14871     onSearchFieldClick : function(e)
14872     {
14873         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14874             this.onTickableFooterButtonClick(e, false, false);
14875             return;
14876         }
14877         
14878         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14879             return;
14880         }
14881         
14882         this.page = 0;
14883         this.loadNext = false;
14884         this.hasFocus = true;
14885         
14886         if(this.triggerAction == 'all') {
14887             this.doQuery(this.allQuery, true);
14888         } else {
14889             this.doQuery(this.getRawValue());
14890         }
14891     },
14892     
14893     listKeyPress : function(e)
14894     {
14895         //Roo.log('listkeypress');
14896         // scroll to first matching element based on key pres..
14897         if (e.isSpecialKey()) {
14898             return false;
14899         }
14900         var k = String.fromCharCode(e.getKey()).toUpperCase();
14901         //Roo.log(k);
14902         var match  = false;
14903         var csel = this.view.getSelectedNodes();
14904         var cselitem = false;
14905         if (csel.length) {
14906             var ix = this.view.indexOf(csel[0]);
14907             cselitem  = this.store.getAt(ix);
14908             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14909                 cselitem = false;
14910             }
14911             
14912         }
14913         
14914         this.store.each(function(v) { 
14915             if (cselitem) {
14916                 // start at existing selection.
14917                 if (cselitem.id == v.id) {
14918                     cselitem = false;
14919                 }
14920                 return true;
14921             }
14922                 
14923             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14924                 match = this.store.indexOf(v);
14925                 return false;
14926             }
14927             return true;
14928         }, this);
14929         
14930         if (match === false) {
14931             return true; // no more action?
14932         }
14933         // scroll to?
14934         this.view.select(match);
14935         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14936         sn.scrollIntoView(sn.dom.parentNode, false);
14937     },
14938     
14939     onViewScroll : function(e, t){
14940         
14941         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){
14942             return;
14943         }
14944         
14945         this.hasQuery = true;
14946         
14947         this.loading = this.list.select('.loading', true).first();
14948         
14949         if(this.loading === null){
14950             this.list.createChild({
14951                 tag: 'div',
14952                 cls: 'loading roo-select2-more-results roo-select2-active',
14953                 html: 'Loading more results...'
14954             });
14955             
14956             this.loading = this.list.select('.loading', true).first();
14957             
14958             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14959             
14960             this.loading.hide();
14961         }
14962         
14963         this.loading.show();
14964         
14965         var _combo = this;
14966         
14967         this.page++;
14968         this.loadNext = true;
14969         
14970         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14971         
14972         return;
14973     },
14974     
14975     addItem : function(o)
14976     {   
14977         var dv = ''; // display value
14978         
14979         if (this.displayField) {
14980             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14981         } else {
14982             // this is an error condition!!!
14983             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14984         }
14985         
14986         if(!dv.length){
14987             return;
14988         }
14989         
14990         var choice = this.choices.createChild({
14991             tag: 'li',
14992             cls: 'roo-select2-search-choice',
14993             cn: [
14994                 {
14995                     tag: 'div',
14996                     html: dv
14997                 },
14998                 {
14999                     tag: 'a',
15000                     href: '#',
15001                     cls: 'roo-select2-search-choice-close fa fa-times',
15002                     tabindex: '-1'
15003                 }
15004             ]
15005             
15006         }, this.searchField);
15007         
15008         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15009         
15010         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15011         
15012         this.item.push(o);
15013         
15014         this.lastData = o;
15015         
15016         this.syncValue();
15017         
15018         this.inputEl().dom.value = '';
15019         
15020         this.validate();
15021     },
15022     
15023     onRemoveItem : function(e, _self, o)
15024     {
15025         e.preventDefault();
15026         
15027         this.lastItem = Roo.apply([], this.item);
15028         
15029         var index = this.item.indexOf(o.data) * 1;
15030         
15031         if( index < 0){
15032             Roo.log('not this item?!');
15033             return;
15034         }
15035         
15036         this.item.splice(index, 1);
15037         o.item.remove();
15038         
15039         this.syncValue();
15040         
15041         this.fireEvent('remove', this, e);
15042         
15043         this.validate();
15044         
15045     },
15046     
15047     syncValue : function()
15048     {
15049         if(!this.item.length){
15050             this.clearValue();
15051             return;
15052         }
15053             
15054         var value = [];
15055         var _this = this;
15056         Roo.each(this.item, function(i){
15057             if(_this.valueField){
15058                 value.push(i[_this.valueField]);
15059                 return;
15060             }
15061
15062             value.push(i);
15063         });
15064
15065         this.value = value.join(',');
15066
15067         if(this.hiddenField){
15068             this.hiddenField.dom.value = this.value;
15069         }
15070         
15071         this.store.fireEvent("datachanged", this.store);
15072         
15073         this.validate();
15074     },
15075     
15076     clearItem : function()
15077     {
15078         if(!this.multiple){
15079             return;
15080         }
15081         
15082         this.item = [];
15083         
15084         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15085            c.remove();
15086         });
15087         
15088         this.syncValue();
15089         
15090         this.validate();
15091         
15092         if(this.tickable && !Roo.isTouch){
15093             this.view.refresh();
15094         }
15095     },
15096     
15097     inputEl: function ()
15098     {
15099         if(Roo.isIOS && this.useNativeIOS){
15100             return this.el.select('select.roo-ios-select', true).first();
15101         }
15102         
15103         if(Roo.isTouch && this.mobileTouchView){
15104             return this.el.select('input.form-control',true).first();
15105         }
15106         
15107         if(this.tickable){
15108             return this.searchField;
15109         }
15110         
15111         return this.el.select('input.form-control',true).first();
15112     },
15113     
15114     onTickableFooterButtonClick : function(e, btn, el)
15115     {
15116         e.preventDefault();
15117         
15118         this.lastItem = Roo.apply([], this.item);
15119         
15120         if(btn && btn.name == 'cancel'){
15121             this.tickItems = Roo.apply([], this.item);
15122             this.collapse();
15123             return;
15124         }
15125         
15126         this.clearItem();
15127         
15128         var _this = this;
15129         
15130         Roo.each(this.tickItems, function(o){
15131             _this.addItem(o);
15132         });
15133         
15134         this.collapse();
15135         
15136     },
15137     
15138     validate : function()
15139     {
15140         if(this.getVisibilityEl().hasClass('hidden')){
15141             return true;
15142         }
15143         
15144         var v = this.getRawValue();
15145         
15146         if(this.multiple){
15147             v = this.getValue();
15148         }
15149         
15150         if(this.disabled || this.allowBlank || v.length){
15151             this.markValid();
15152             return true;
15153         }
15154         
15155         this.markInvalid();
15156         return false;
15157     },
15158     
15159     tickableInputEl : function()
15160     {
15161         if(!this.tickable || !this.editable){
15162             return this.inputEl();
15163         }
15164         
15165         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15166     },
15167     
15168     
15169     getAutoCreateTouchView : function()
15170     {
15171         var id = Roo.id();
15172         
15173         var cfg = {
15174             cls: 'form-group' //input-group
15175         };
15176         
15177         var input =  {
15178             tag: 'input',
15179             id : id,
15180             type : this.inputType,
15181             cls : 'form-control x-combo-noedit',
15182             autocomplete: 'new-password',
15183             placeholder : this.placeholder || '',
15184             readonly : true
15185         };
15186         
15187         if (this.name) {
15188             input.name = this.name;
15189         }
15190         
15191         if (this.size) {
15192             input.cls += ' input-' + this.size;
15193         }
15194         
15195         if (this.disabled) {
15196             input.disabled = true;
15197         }
15198         
15199         var inputblock = {
15200             cls : '',
15201             cn : [
15202                 input
15203             ]
15204         };
15205         
15206         if(this.before){
15207             inputblock.cls += ' input-group';
15208             
15209             inputblock.cn.unshift({
15210                 tag :'span',
15211                 cls : 'input-group-addon input-group-prepend input-group-text',
15212                 html : this.before
15213             });
15214         }
15215         
15216         if(this.removable && !this.multiple){
15217             inputblock.cls += ' roo-removable';
15218             
15219             inputblock.cn.push({
15220                 tag: 'button',
15221                 html : 'x',
15222                 cls : 'roo-combo-removable-btn close'
15223             });
15224         }
15225
15226         if(this.hasFeedback && !this.allowBlank){
15227             
15228             inputblock.cls += ' has-feedback';
15229             
15230             inputblock.cn.push({
15231                 tag: 'span',
15232                 cls: 'glyphicon form-control-feedback'
15233             });
15234             
15235         }
15236         
15237         if (this.after) {
15238             
15239             inputblock.cls += (this.before) ? '' : ' input-group';
15240             
15241             inputblock.cn.push({
15242                 tag :'span',
15243                 cls : 'input-group-addon input-group-append input-group-text',
15244                 html : this.after
15245             });
15246         }
15247
15248         
15249         var ibwrap = inputblock;
15250         
15251         if(this.multiple){
15252             ibwrap = {
15253                 tag: 'ul',
15254                 cls: 'roo-select2-choices',
15255                 cn:[
15256                     {
15257                         tag: 'li',
15258                         cls: 'roo-select2-search-field',
15259                         cn: [
15260
15261                             inputblock
15262                         ]
15263                     }
15264                 ]
15265             };
15266         
15267             
15268         }
15269         
15270         var combobox = {
15271             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15272             cn: [
15273                 {
15274                     tag: 'input',
15275                     type : 'hidden',
15276                     cls: 'form-hidden-field'
15277                 },
15278                 ibwrap
15279             ]
15280         };
15281         
15282         if(!this.multiple && this.showToggleBtn){
15283             
15284             var caret = {
15285                         tag: 'span',
15286                         cls: 'caret'
15287             };
15288             
15289             if (this.caret != false) {
15290                 caret = {
15291                      tag: 'i',
15292                      cls: 'fa fa-' + this.caret
15293                 };
15294                 
15295             }
15296             
15297             combobox.cn.push({
15298                 tag :'span',
15299                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15300                 cn : [
15301                     caret,
15302                     {
15303                         tag: 'span',
15304                         cls: 'combobox-clear',
15305                         cn  : [
15306                             {
15307                                 tag : 'i',
15308                                 cls: 'icon-remove'
15309                             }
15310                         ]
15311                     }
15312                 ]
15313
15314             })
15315         }
15316         
15317         if(this.multiple){
15318             combobox.cls += ' roo-select2-container-multi';
15319         }
15320         
15321         var align = this.labelAlign || this.parentLabelAlign();
15322         
15323         if (align ==='left' && this.fieldLabel.length) {
15324
15325             cfg.cn = [
15326                 {
15327                    tag : 'i',
15328                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15329                    tooltip : 'This field is required'
15330                 },
15331                 {
15332                     tag: 'label',
15333                     cls : 'control-label col-form-label',
15334                     html : this.fieldLabel
15335
15336                 },
15337                 {
15338                     cls : '', 
15339                     cn: [
15340                         combobox
15341                     ]
15342                 }
15343             ];
15344             
15345             var labelCfg = cfg.cn[1];
15346             var contentCfg = cfg.cn[2];
15347             
15348
15349             if(this.indicatorpos == 'right'){
15350                 cfg.cn = [
15351                     {
15352                         tag: 'label',
15353                         'for' :  id,
15354                         cls : 'control-label col-form-label',
15355                         cn : [
15356                             {
15357                                 tag : 'span',
15358                                 html : this.fieldLabel
15359                             },
15360                             {
15361                                 tag : 'i',
15362                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15363                                 tooltip : 'This field is required'
15364                             }
15365                         ]
15366                     },
15367                     {
15368                         cls : "",
15369                         cn: [
15370                             combobox
15371                         ]
15372                     }
15373
15374                 ];
15375                 
15376                 labelCfg = cfg.cn[0];
15377                 contentCfg = cfg.cn[1];
15378             }
15379             
15380            
15381             
15382             if(this.labelWidth > 12){
15383                 labelCfg.style = "width: " + this.labelWidth + 'px';
15384             }
15385             
15386             if(this.labelWidth < 13 && this.labelmd == 0){
15387                 this.labelmd = this.labelWidth;
15388             }
15389             
15390             if(this.labellg > 0){
15391                 labelCfg.cls += ' col-lg-' + this.labellg;
15392                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15393             }
15394             
15395             if(this.labelmd > 0){
15396                 labelCfg.cls += ' col-md-' + this.labelmd;
15397                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15398             }
15399             
15400             if(this.labelsm > 0){
15401                 labelCfg.cls += ' col-sm-' + this.labelsm;
15402                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15403             }
15404             
15405             if(this.labelxs > 0){
15406                 labelCfg.cls += ' col-xs-' + this.labelxs;
15407                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15408             }
15409                 
15410                 
15411         } else if ( this.fieldLabel.length) {
15412             cfg.cn = [
15413                 {
15414                    tag : 'i',
15415                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15416                    tooltip : 'This field is required'
15417                 },
15418                 {
15419                     tag: 'label',
15420                     cls : 'control-label',
15421                     html : this.fieldLabel
15422
15423                 },
15424                 {
15425                     cls : '', 
15426                     cn: [
15427                         combobox
15428                     ]
15429                 }
15430             ];
15431             
15432             if(this.indicatorpos == 'right'){
15433                 cfg.cn = [
15434                     {
15435                         tag: 'label',
15436                         cls : 'control-label',
15437                         html : this.fieldLabel,
15438                         cn : [
15439                             {
15440                                tag : 'i',
15441                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15442                                tooltip : 'This field is required'
15443                             }
15444                         ]
15445                     },
15446                     {
15447                         cls : '', 
15448                         cn: [
15449                             combobox
15450                         ]
15451                     }
15452                 ];
15453             }
15454         } else {
15455             cfg.cn = combobox;    
15456         }
15457         
15458         
15459         var settings = this;
15460         
15461         ['xs','sm','md','lg'].map(function(size){
15462             if (settings[size]) {
15463                 cfg.cls += ' col-' + size + '-' + settings[size];
15464             }
15465         });
15466         
15467         return cfg;
15468     },
15469     
15470     initTouchView : function()
15471     {
15472         this.renderTouchView();
15473         
15474         this.touchViewEl.on('scroll', function(){
15475             this.el.dom.scrollTop = 0;
15476         }, this);
15477         
15478         this.originalValue = this.getValue();
15479         
15480         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15481         
15482         this.inputEl().on("click", this.showTouchView, this);
15483         if (this.triggerEl) {
15484             this.triggerEl.on("click", this.showTouchView, this);
15485         }
15486         
15487         
15488         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15489         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15490         
15491         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15492         
15493         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15494         this.store.on('load', this.onTouchViewLoad, this);
15495         this.store.on('loadexception', this.onTouchViewLoadException, this);
15496         
15497         if(this.hiddenName){
15498             
15499             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15500             
15501             this.hiddenField.dom.value =
15502                 this.hiddenValue !== undefined ? this.hiddenValue :
15503                 this.value !== undefined ? this.value : '';
15504         
15505             this.el.dom.removeAttribute('name');
15506             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15507         }
15508         
15509         if(this.multiple){
15510             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15511             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15512         }
15513         
15514         if(this.removable && !this.multiple){
15515             var close = this.closeTriggerEl();
15516             if(close){
15517                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15518                 close.on('click', this.removeBtnClick, this, close);
15519             }
15520         }
15521         /*
15522          * fix the bug in Safari iOS8
15523          */
15524         this.inputEl().on("focus", function(e){
15525             document.activeElement.blur();
15526         }, this);
15527         
15528         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15529         
15530         return;
15531         
15532         
15533     },
15534     
15535     renderTouchView : function()
15536     {
15537         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15538         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15539         
15540         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15541         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15542         
15543         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15544         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15545         this.touchViewBodyEl.setStyle('overflow', 'auto');
15546         
15547         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15548         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15549         
15550         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15551         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15552         
15553     },
15554     
15555     showTouchView : function()
15556     {
15557         if(this.disabled){
15558             return;
15559         }
15560         
15561         this.touchViewHeaderEl.hide();
15562
15563         if(this.modalTitle.length){
15564             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15565             this.touchViewHeaderEl.show();
15566         }
15567
15568         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15569         this.touchViewEl.show();
15570
15571         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15572         
15573         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15574         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15575
15576         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15577
15578         if(this.modalTitle.length){
15579             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15580         }
15581         
15582         this.touchViewBodyEl.setHeight(bodyHeight);
15583
15584         if(this.animate){
15585             var _this = this;
15586             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15587         }else{
15588             this.touchViewEl.addClass('in');
15589         }
15590         
15591         if(this._touchViewMask){
15592             Roo.get(document.body).addClass("x-body-masked");
15593             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15594             this._touchViewMask.setStyle('z-index', 10000);
15595             this._touchViewMask.addClass('show');
15596         }
15597         
15598         this.doTouchViewQuery();
15599         
15600     },
15601     
15602     hideTouchView : function()
15603     {
15604         this.touchViewEl.removeClass('in');
15605
15606         if(this.animate){
15607             var _this = this;
15608             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15609         }else{
15610             this.touchViewEl.setStyle('display', 'none');
15611         }
15612         
15613         if(this._touchViewMask){
15614             this._touchViewMask.removeClass('show');
15615             Roo.get(document.body).removeClass("x-body-masked");
15616         }
15617     },
15618     
15619     setTouchViewValue : function()
15620     {
15621         if(this.multiple){
15622             this.clearItem();
15623         
15624             var _this = this;
15625
15626             Roo.each(this.tickItems, function(o){
15627                 this.addItem(o);
15628             }, this);
15629         }
15630         
15631         this.hideTouchView();
15632     },
15633     
15634     doTouchViewQuery : function()
15635     {
15636         var qe = {
15637             query: '',
15638             forceAll: true,
15639             combo: this,
15640             cancel:false
15641         };
15642         
15643         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15644             return false;
15645         }
15646         
15647         if(!this.alwaysQuery || this.mode == 'local'){
15648             this.onTouchViewLoad();
15649             return;
15650         }
15651         
15652         this.store.load();
15653     },
15654     
15655     onTouchViewBeforeLoad : function(combo,opts)
15656     {
15657         return;
15658     },
15659
15660     // private
15661     onTouchViewLoad : function()
15662     {
15663         if(this.store.getCount() < 1){
15664             this.onTouchViewEmptyResults();
15665             return;
15666         }
15667         
15668         this.clearTouchView();
15669         
15670         var rawValue = this.getRawValue();
15671         
15672         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15673         
15674         this.tickItems = [];
15675         
15676         this.store.data.each(function(d, rowIndex){
15677             var row = this.touchViewListGroup.createChild(template);
15678             
15679             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15680                 row.addClass(d.data.cls);
15681             }
15682             
15683             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15684                 var cfg = {
15685                     data : d.data,
15686                     html : d.data[this.displayField]
15687                 };
15688                 
15689                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15690                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15691                 }
15692             }
15693             row.removeClass('selected');
15694             if(!this.multiple && this.valueField &&
15695                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15696             {
15697                 // radio buttons..
15698                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15699                 row.addClass('selected');
15700             }
15701             
15702             if(this.multiple && this.valueField &&
15703                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15704             {
15705                 
15706                 // checkboxes...
15707                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15708                 this.tickItems.push(d.data);
15709             }
15710             
15711             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15712             
15713         }, this);
15714         
15715         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15716         
15717         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15718
15719         if(this.modalTitle.length){
15720             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15721         }
15722
15723         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15724         
15725         if(this.mobile_restrict_height && listHeight < bodyHeight){
15726             this.touchViewBodyEl.setHeight(listHeight);
15727         }
15728         
15729         var _this = this;
15730         
15731         if(firstChecked && listHeight > bodyHeight){
15732             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15733         }
15734         
15735     },
15736     
15737     onTouchViewLoadException : function()
15738     {
15739         this.hideTouchView();
15740     },
15741     
15742     onTouchViewEmptyResults : function()
15743     {
15744         this.clearTouchView();
15745         
15746         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15747         
15748         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15749         
15750     },
15751     
15752     clearTouchView : function()
15753     {
15754         this.touchViewListGroup.dom.innerHTML = '';
15755     },
15756     
15757     onTouchViewClick : function(e, el, o)
15758     {
15759         e.preventDefault();
15760         
15761         var row = o.row;
15762         var rowIndex = o.rowIndex;
15763         
15764         var r = this.store.getAt(rowIndex);
15765         
15766         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15767             
15768             if(!this.multiple){
15769                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15770                     c.dom.removeAttribute('checked');
15771                 }, this);
15772
15773                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15774
15775                 this.setFromData(r.data);
15776
15777                 var close = this.closeTriggerEl();
15778
15779                 if(close){
15780                     close.show();
15781                 }
15782
15783                 this.hideTouchView();
15784
15785                 this.fireEvent('select', this, r, rowIndex);
15786
15787                 return;
15788             }
15789
15790             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15791                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15792                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15793                 return;
15794             }
15795
15796             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15797             this.addItem(r.data);
15798             this.tickItems.push(r.data);
15799         }
15800     },
15801     
15802     getAutoCreateNativeIOS : function()
15803     {
15804         var cfg = {
15805             cls: 'form-group' //input-group,
15806         };
15807         
15808         var combobox =  {
15809             tag: 'select',
15810             cls : 'roo-ios-select'
15811         };
15812         
15813         if (this.name) {
15814             combobox.name = this.name;
15815         }
15816         
15817         if (this.disabled) {
15818             combobox.disabled = true;
15819         }
15820         
15821         var settings = this;
15822         
15823         ['xs','sm','md','lg'].map(function(size){
15824             if (settings[size]) {
15825                 cfg.cls += ' col-' + size + '-' + settings[size];
15826             }
15827         });
15828         
15829         cfg.cn = combobox;
15830         
15831         return cfg;
15832         
15833     },
15834     
15835     initIOSView : function()
15836     {
15837         this.store.on('load', this.onIOSViewLoad, this);
15838         
15839         return;
15840     },
15841     
15842     onIOSViewLoad : function()
15843     {
15844         if(this.store.getCount() < 1){
15845             return;
15846         }
15847         
15848         this.clearIOSView();
15849         
15850         if(this.allowBlank) {
15851             
15852             var default_text = '-- SELECT --';
15853             
15854             if(this.placeholder.length){
15855                 default_text = this.placeholder;
15856             }
15857             
15858             if(this.emptyTitle.length){
15859                 default_text += ' - ' + this.emptyTitle + ' -';
15860             }
15861             
15862             var opt = this.inputEl().createChild({
15863                 tag: 'option',
15864                 value : 0,
15865                 html : default_text
15866             });
15867             
15868             var o = {};
15869             o[this.valueField] = 0;
15870             o[this.displayField] = default_text;
15871             
15872             this.ios_options.push({
15873                 data : o,
15874                 el : opt
15875             });
15876             
15877         }
15878         
15879         this.store.data.each(function(d, rowIndex){
15880             
15881             var html = '';
15882             
15883             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15884                 html = d.data[this.displayField];
15885             }
15886             
15887             var value = '';
15888             
15889             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15890                 value = d.data[this.valueField];
15891             }
15892             
15893             var option = {
15894                 tag: 'option',
15895                 value : value,
15896                 html : html
15897             };
15898             
15899             if(this.value == d.data[this.valueField]){
15900                 option['selected'] = true;
15901             }
15902             
15903             var opt = this.inputEl().createChild(option);
15904             
15905             this.ios_options.push({
15906                 data : d.data,
15907                 el : opt
15908             });
15909             
15910         }, this);
15911         
15912         this.inputEl().on('change', function(){
15913            this.fireEvent('select', this);
15914         }, this);
15915         
15916     },
15917     
15918     clearIOSView: function()
15919     {
15920         this.inputEl().dom.innerHTML = '';
15921         
15922         this.ios_options = [];
15923     },
15924     
15925     setIOSValue: function(v)
15926     {
15927         this.value = v;
15928         
15929         if(!this.ios_options){
15930             return;
15931         }
15932         
15933         Roo.each(this.ios_options, function(opts){
15934            
15935            opts.el.dom.removeAttribute('selected');
15936            
15937            if(opts.data[this.valueField] != v){
15938                return;
15939            }
15940            
15941            opts.el.dom.setAttribute('selected', true);
15942            
15943         }, this);
15944     }
15945
15946     /** 
15947     * @cfg {Boolean} grow 
15948     * @hide 
15949     */
15950     /** 
15951     * @cfg {Number} growMin 
15952     * @hide 
15953     */
15954     /** 
15955     * @cfg {Number} growMax 
15956     * @hide 
15957     */
15958     /**
15959      * @hide
15960      * @method autoSize
15961      */
15962 });
15963
15964 Roo.apply(Roo.bootstrap.ComboBox,  {
15965     
15966     header : {
15967         tag: 'div',
15968         cls: 'modal-header',
15969         cn: [
15970             {
15971                 tag: 'h4',
15972                 cls: 'modal-title'
15973             }
15974         ]
15975     },
15976     
15977     body : {
15978         tag: 'div',
15979         cls: 'modal-body',
15980         cn: [
15981             {
15982                 tag: 'ul',
15983                 cls: 'list-group'
15984             }
15985         ]
15986     },
15987     
15988     listItemRadio : {
15989         tag: 'li',
15990         cls: 'list-group-item',
15991         cn: [
15992             {
15993                 tag: 'span',
15994                 cls: 'roo-combobox-list-group-item-value'
15995             },
15996             {
15997                 tag: 'div',
15998                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15999                 cn: [
16000                     {
16001                         tag: 'input',
16002                         type: 'radio'
16003                     },
16004                     {
16005                         tag: 'label'
16006                     }
16007                 ]
16008             }
16009         ]
16010     },
16011     
16012     listItemCheckbox : {
16013         tag: 'li',
16014         cls: 'list-group-item',
16015         cn: [
16016             {
16017                 tag: 'span',
16018                 cls: 'roo-combobox-list-group-item-value'
16019             },
16020             {
16021                 tag: 'div',
16022                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16023                 cn: [
16024                     {
16025                         tag: 'input',
16026                         type: 'checkbox'
16027                     },
16028                     {
16029                         tag: 'label'
16030                     }
16031                 ]
16032             }
16033         ]
16034     },
16035     
16036     emptyResult : {
16037         tag: 'div',
16038         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16039     },
16040     
16041     footer : {
16042         tag: 'div',
16043         cls: 'modal-footer',
16044         cn: [
16045             {
16046                 tag: 'div',
16047                 cls: 'row',
16048                 cn: [
16049                     {
16050                         tag: 'div',
16051                         cls: 'col-xs-6 text-left',
16052                         cn: {
16053                             tag: 'button',
16054                             cls: 'btn btn-danger roo-touch-view-cancel',
16055                             html: 'Cancel'
16056                         }
16057                     },
16058                     {
16059                         tag: 'div',
16060                         cls: 'col-xs-6 text-right',
16061                         cn: {
16062                             tag: 'button',
16063                             cls: 'btn btn-success roo-touch-view-ok',
16064                             html: 'OK'
16065                         }
16066                     }
16067                 ]
16068             }
16069         ]
16070         
16071     }
16072 });
16073
16074 Roo.apply(Roo.bootstrap.ComboBox,  {
16075     
16076     touchViewTemplate : {
16077         tag: 'div',
16078         cls: 'modal fade roo-combobox-touch-view',
16079         cn: [
16080             {
16081                 tag: 'div',
16082                 cls: 'modal-dialog',
16083                 style : 'position:fixed', // we have to fix position....
16084                 cn: [
16085                     {
16086                         tag: 'div',
16087                         cls: 'modal-content',
16088                         cn: [
16089                             Roo.bootstrap.ComboBox.header,
16090                             Roo.bootstrap.ComboBox.body,
16091                             Roo.bootstrap.ComboBox.footer
16092                         ]
16093                     }
16094                 ]
16095             }
16096         ]
16097     }
16098 });/*
16099  * Based on:
16100  * Ext JS Library 1.1.1
16101  * Copyright(c) 2006-2007, Ext JS, LLC.
16102  *
16103  * Originally Released Under LGPL - original licence link has changed is not relivant.
16104  *
16105  * Fork - LGPL
16106  * <script type="text/javascript">
16107  */
16108
16109 /**
16110  * @class Roo.View
16111  * @extends Roo.util.Observable
16112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16113  * This class also supports single and multi selection modes. <br>
16114  * Create a data model bound view:
16115  <pre><code>
16116  var store = new Roo.data.Store(...);
16117
16118  var view = new Roo.View({
16119     el : "my-element",
16120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16121  
16122     singleSelect: true,
16123     selectedClass: "ydataview-selected",
16124     store: store
16125  });
16126
16127  // listen for node click?
16128  view.on("click", function(vw, index, node, e){
16129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16130  });
16131
16132  // load XML data
16133  dataModel.load("foobar.xml");
16134  </code></pre>
16135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16136  * <br><br>
16137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16139  * 
16140  * Note: old style constructor is still suported (container, template, config)
16141  * 
16142  * @constructor
16143  * Create a new View
16144  * @param {Object} config The config object
16145  * 
16146  */
16147 Roo.View = function(config, depreciated_tpl, depreciated_config){
16148     
16149     this.parent = false;
16150     
16151     if (typeof(depreciated_tpl) == 'undefined') {
16152         // new way.. - universal constructor.
16153         Roo.apply(this, config);
16154         this.el  = Roo.get(this.el);
16155     } else {
16156         // old format..
16157         this.el  = Roo.get(config);
16158         this.tpl = depreciated_tpl;
16159         Roo.apply(this, depreciated_config);
16160     }
16161     this.wrapEl  = this.el.wrap().wrap();
16162     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16163     
16164     
16165     if(typeof(this.tpl) == "string"){
16166         this.tpl = new Roo.Template(this.tpl);
16167     } else {
16168         // support xtype ctors..
16169         this.tpl = new Roo.factory(this.tpl, Roo);
16170     }
16171     
16172     
16173     this.tpl.compile();
16174     
16175     /** @private */
16176     this.addEvents({
16177         /**
16178          * @event beforeclick
16179          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
16186         /**
16187          * @event click
16188          * Fires when a template node is 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             "click" : true,
16195         /**
16196          * @event dblclick
16197          * Fires when a template node is double 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             "dblclick" : true,
16204         /**
16205          * @event contextmenu
16206          * Fires when a template node is right clicked.
16207          * @param {Roo.View} this
16208          * @param {Number} index The index of the target node
16209          * @param {HTMLElement} node The target node
16210          * @param {Roo.EventObject} e The raw event object
16211          */
16212             "contextmenu" : true,
16213         /**
16214          * @event selectionchange
16215          * Fires when the selected nodes change.
16216          * @param {Roo.View} this
16217          * @param {Array} selections Array of the selected nodes
16218          */
16219             "selectionchange" : true,
16220     
16221         /**
16222          * @event beforeselect
16223          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16224          * @param {Roo.View} this
16225          * @param {HTMLElement} node The node to be selected
16226          * @param {Array} selections Array of currently selected nodes
16227          */
16228             "beforeselect" : true,
16229         /**
16230          * @event preparedata
16231          * Fires on every row to render, to allow you to change the data.
16232          * @param {Roo.View} this
16233          * @param {Object} data to be rendered (change this)
16234          */
16235           "preparedata" : true
16236           
16237           
16238         });
16239
16240
16241
16242     this.el.on({
16243         "click": this.onClick,
16244         "dblclick": this.onDblClick,
16245         "contextmenu": this.onContextMenu,
16246         scope:this
16247     });
16248
16249     this.selections = [];
16250     this.nodes = [];
16251     this.cmp = new Roo.CompositeElementLite([]);
16252     if(this.store){
16253         this.store = Roo.factory(this.store, Roo.data);
16254         this.setStore(this.store, true);
16255     }
16256     
16257     if ( this.footer && this.footer.xtype) {
16258            
16259          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16260         
16261         this.footer.dataSource = this.store;
16262         this.footer.container = fctr;
16263         this.footer = Roo.factory(this.footer, Roo);
16264         fctr.insertFirst(this.el);
16265         
16266         // this is a bit insane - as the paging toolbar seems to detach the el..
16267 //        dom.parentNode.parentNode.parentNode
16268          // they get detached?
16269     }
16270     
16271     
16272     Roo.View.superclass.constructor.call(this);
16273     
16274     
16275 };
16276
16277 Roo.extend(Roo.View, Roo.util.Observable, {
16278     
16279      /**
16280      * @cfg {Roo.data.Store} store Data store to load data from.
16281      */
16282     store : false,
16283     
16284     /**
16285      * @cfg {String|Roo.Element} el The container element.
16286      */
16287     el : '',
16288     
16289     /**
16290      * @cfg {String|Roo.Template} tpl The template used by this View 
16291      */
16292     tpl : false,
16293     /**
16294      * @cfg {String} dataName the named area of the template to use as the data area
16295      *                          Works with domtemplates roo-name="name"
16296      */
16297     dataName: false,
16298     /**
16299      * @cfg {String} selectedClass The css class to add to selected nodes
16300      */
16301     selectedClass : "x-view-selected",
16302      /**
16303      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16304      */
16305     emptyText : "",
16306     
16307     /**
16308      * @cfg {String} text to display on mask (default Loading)
16309      */
16310     mask : false,
16311     /**
16312      * @cfg {Boolean} multiSelect Allow multiple selection
16313      */
16314     multiSelect : false,
16315     /**
16316      * @cfg {Boolean} singleSelect Allow single selection
16317      */
16318     singleSelect:  false,
16319     
16320     /**
16321      * @cfg {Boolean} toggleSelect - selecting 
16322      */
16323     toggleSelect : false,
16324     
16325     /**
16326      * @cfg {Boolean} tickable - selecting 
16327      */
16328     tickable : false,
16329     
16330     /**
16331      * Returns the element this view is bound to.
16332      * @return {Roo.Element}
16333      */
16334     getEl : function(){
16335         return this.wrapEl;
16336     },
16337     
16338     
16339
16340     /**
16341      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16342      */
16343     refresh : function(){
16344         //Roo.log('refresh');
16345         var t = this.tpl;
16346         
16347         // if we are using something like 'domtemplate', then
16348         // the what gets used is:
16349         // t.applySubtemplate(NAME, data, wrapping data..)
16350         // the outer template then get' applied with
16351         //     the store 'extra data'
16352         // and the body get's added to the
16353         //      roo-name="data" node?
16354         //      <span class='roo-tpl-{name}'></span> ?????
16355         
16356         
16357         
16358         this.clearSelections();
16359         this.el.update("");
16360         var html = [];
16361         var records = this.store.getRange();
16362         if(records.length < 1) {
16363             
16364             // is this valid??  = should it render a template??
16365             
16366             this.el.update(this.emptyText);
16367             return;
16368         }
16369         var el = this.el;
16370         if (this.dataName) {
16371             this.el.update(t.apply(this.store.meta)); //????
16372             el = this.el.child('.roo-tpl-' + this.dataName);
16373         }
16374         
16375         for(var i = 0, len = records.length; i < len; i++){
16376             var data = this.prepareData(records[i].data, i, records[i]);
16377             this.fireEvent("preparedata", this, data, i, records[i]);
16378             
16379             var d = Roo.apply({}, data);
16380             
16381             if(this.tickable){
16382                 Roo.apply(d, {'roo-id' : Roo.id()});
16383                 
16384                 var _this = this;
16385             
16386                 Roo.each(this.parent.item, function(item){
16387                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16388                         return;
16389                     }
16390                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16391                 });
16392             }
16393             
16394             html[html.length] = Roo.util.Format.trim(
16395                 this.dataName ?
16396                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16397                     t.apply(d)
16398             );
16399         }
16400         
16401         
16402         
16403         el.update(html.join(""));
16404         this.nodes = el.dom.childNodes;
16405         this.updateIndexes(0);
16406     },
16407     
16408
16409     /**
16410      * Function to override to reformat the data that is sent to
16411      * the template for each node.
16412      * DEPRICATED - use the preparedata event handler.
16413      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16414      * a JSON object for an UpdateManager bound view).
16415      */
16416     prepareData : function(data, index, record)
16417     {
16418         this.fireEvent("preparedata", this, data, index, record);
16419         return data;
16420     },
16421
16422     onUpdate : function(ds, record){
16423         // Roo.log('on update');   
16424         this.clearSelections();
16425         var index = this.store.indexOf(record);
16426         var n = this.nodes[index];
16427         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16428         n.parentNode.removeChild(n);
16429         this.updateIndexes(index, index);
16430     },
16431
16432     
16433     
16434 // --------- FIXME     
16435     onAdd : function(ds, records, index)
16436     {
16437         //Roo.log(['on Add', ds, records, index] );        
16438         this.clearSelections();
16439         if(this.nodes.length == 0){
16440             this.refresh();
16441             return;
16442         }
16443         var n = this.nodes[index];
16444         for(var i = 0, len = records.length; i < len; i++){
16445             var d = this.prepareData(records[i].data, i, records[i]);
16446             if(n){
16447                 this.tpl.insertBefore(n, d);
16448             }else{
16449                 
16450                 this.tpl.append(this.el, d);
16451             }
16452         }
16453         this.updateIndexes(index);
16454     },
16455
16456     onRemove : function(ds, record, index){
16457        // Roo.log('onRemove');
16458         this.clearSelections();
16459         var el = this.dataName  ?
16460             this.el.child('.roo-tpl-' + this.dataName) :
16461             this.el; 
16462         
16463         el.dom.removeChild(this.nodes[index]);
16464         this.updateIndexes(index);
16465     },
16466
16467     /**
16468      * Refresh an individual node.
16469      * @param {Number} index
16470      */
16471     refreshNode : function(index){
16472         this.onUpdate(this.store, this.store.getAt(index));
16473     },
16474
16475     updateIndexes : function(startIndex, endIndex){
16476         var ns = this.nodes;
16477         startIndex = startIndex || 0;
16478         endIndex = endIndex || ns.length - 1;
16479         for(var i = startIndex; i <= endIndex; i++){
16480             ns[i].nodeIndex = i;
16481         }
16482     },
16483
16484     /**
16485      * Changes the data store this view uses and refresh the view.
16486      * @param {Store} store
16487      */
16488     setStore : function(store, initial){
16489         if(!initial && this.store){
16490             this.store.un("datachanged", this.refresh);
16491             this.store.un("add", this.onAdd);
16492             this.store.un("remove", this.onRemove);
16493             this.store.un("update", this.onUpdate);
16494             this.store.un("clear", this.refresh);
16495             this.store.un("beforeload", this.onBeforeLoad);
16496             this.store.un("load", this.onLoad);
16497             this.store.un("loadexception", this.onLoad);
16498         }
16499         if(store){
16500           
16501             store.on("datachanged", this.refresh, this);
16502             store.on("add", this.onAdd, this);
16503             store.on("remove", this.onRemove, this);
16504             store.on("update", this.onUpdate, this);
16505             store.on("clear", this.refresh, this);
16506             store.on("beforeload", this.onBeforeLoad, this);
16507             store.on("load", this.onLoad, this);
16508             store.on("loadexception", this.onLoad, this);
16509         }
16510         
16511         if(store){
16512             this.refresh();
16513         }
16514     },
16515     /**
16516      * onbeforeLoad - masks the loading area.
16517      *
16518      */
16519     onBeforeLoad : function(store,opts)
16520     {
16521          //Roo.log('onBeforeLoad');   
16522         if (!opts.add) {
16523             this.el.update("");
16524         }
16525         this.el.mask(this.mask ? this.mask : "Loading" ); 
16526     },
16527     onLoad : function ()
16528     {
16529         this.el.unmask();
16530     },
16531     
16532
16533     /**
16534      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16535      * @param {HTMLElement} node
16536      * @return {HTMLElement} The template node
16537      */
16538     findItemFromChild : function(node){
16539         var el = this.dataName  ?
16540             this.el.child('.roo-tpl-' + this.dataName,true) :
16541             this.el.dom; 
16542         
16543         if(!node || node.parentNode == el){
16544                     return node;
16545             }
16546             var p = node.parentNode;
16547             while(p && p != el){
16548             if(p.parentNode == el){
16549                 return p;
16550             }
16551             p = p.parentNode;
16552         }
16553             return null;
16554     },
16555
16556     /** @ignore */
16557     onClick : function(e){
16558         var item = this.findItemFromChild(e.getTarget());
16559         if(item){
16560             var index = this.indexOf(item);
16561             if(this.onItemClick(item, index, e) !== false){
16562                 this.fireEvent("click", this, index, item, e);
16563             }
16564         }else{
16565             this.clearSelections();
16566         }
16567     },
16568
16569     /** @ignore */
16570     onContextMenu : function(e){
16571         var item = this.findItemFromChild(e.getTarget());
16572         if(item){
16573             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16574         }
16575     },
16576
16577     /** @ignore */
16578     onDblClick : function(e){
16579         var item = this.findItemFromChild(e.getTarget());
16580         if(item){
16581             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16582         }
16583     },
16584
16585     onItemClick : function(item, index, e)
16586     {
16587         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16588             return false;
16589         }
16590         if (this.toggleSelect) {
16591             var m = this.isSelected(item) ? 'unselect' : 'select';
16592             //Roo.log(m);
16593             var _t = this;
16594             _t[m](item, true, false);
16595             return true;
16596         }
16597         if(this.multiSelect || this.singleSelect){
16598             if(this.multiSelect && e.shiftKey && this.lastSelection){
16599                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16600             }else{
16601                 this.select(item, this.multiSelect && e.ctrlKey);
16602                 this.lastSelection = item;
16603             }
16604             
16605             if(!this.tickable){
16606                 e.preventDefault();
16607             }
16608             
16609         }
16610         return true;
16611     },
16612
16613     /**
16614      * Get the number of selected nodes.
16615      * @return {Number}
16616      */
16617     getSelectionCount : function(){
16618         return this.selections.length;
16619     },
16620
16621     /**
16622      * Get the currently selected nodes.
16623      * @return {Array} An array of HTMLElements
16624      */
16625     getSelectedNodes : function(){
16626         return this.selections;
16627     },
16628
16629     /**
16630      * Get the indexes of the selected nodes.
16631      * @return {Array}
16632      */
16633     getSelectedIndexes : function(){
16634         var indexes = [], s = this.selections;
16635         for(var i = 0, len = s.length; i < len; i++){
16636             indexes.push(s[i].nodeIndex);
16637         }
16638         return indexes;
16639     },
16640
16641     /**
16642      * Clear all selections
16643      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16644      */
16645     clearSelections : function(suppressEvent){
16646         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16647             this.cmp.elements = this.selections;
16648             this.cmp.removeClass(this.selectedClass);
16649             this.selections = [];
16650             if(!suppressEvent){
16651                 this.fireEvent("selectionchange", this, this.selections);
16652             }
16653         }
16654     },
16655
16656     /**
16657      * Returns true if the passed node is selected
16658      * @param {HTMLElement/Number} node The node or node index
16659      * @return {Boolean}
16660      */
16661     isSelected : function(node){
16662         var s = this.selections;
16663         if(s.length < 1){
16664             return false;
16665         }
16666         node = this.getNode(node);
16667         return s.indexOf(node) !== -1;
16668     },
16669
16670     /**
16671      * Selects nodes.
16672      * @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
16673      * @param {Boolean} keepExisting (optional) true to keep existing selections
16674      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16675      */
16676     select : function(nodeInfo, keepExisting, suppressEvent){
16677         if(nodeInfo instanceof Array){
16678             if(!keepExisting){
16679                 this.clearSelections(true);
16680             }
16681             for(var i = 0, len = nodeInfo.length; i < len; i++){
16682                 this.select(nodeInfo[i], true, true);
16683             }
16684             return;
16685         } 
16686         var node = this.getNode(nodeInfo);
16687         if(!node || this.isSelected(node)){
16688             return; // already selected.
16689         }
16690         if(!keepExisting){
16691             this.clearSelections(true);
16692         }
16693         
16694         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16695             Roo.fly(node).addClass(this.selectedClass);
16696             this.selections.push(node);
16697             if(!suppressEvent){
16698                 this.fireEvent("selectionchange", this, this.selections);
16699             }
16700         }
16701         
16702         
16703     },
16704       /**
16705      * Unselects nodes.
16706      * @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
16707      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16708      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16709      */
16710     unselect : function(nodeInfo, keepExisting, suppressEvent)
16711     {
16712         if(nodeInfo instanceof Array){
16713             Roo.each(this.selections, function(s) {
16714                 this.unselect(s, nodeInfo);
16715             }, this);
16716             return;
16717         }
16718         var node = this.getNode(nodeInfo);
16719         if(!node || !this.isSelected(node)){
16720             //Roo.log("not selected");
16721             return; // not selected.
16722         }
16723         // fireevent???
16724         var ns = [];
16725         Roo.each(this.selections, function(s) {
16726             if (s == node ) {
16727                 Roo.fly(node).removeClass(this.selectedClass);
16728
16729                 return;
16730             }
16731             ns.push(s);
16732         },this);
16733         
16734         this.selections= ns;
16735         this.fireEvent("selectionchange", this, this.selections);
16736     },
16737
16738     /**
16739      * Gets a template node.
16740      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16741      * @return {HTMLElement} The node or null if it wasn't found
16742      */
16743     getNode : function(nodeInfo){
16744         if(typeof nodeInfo == "string"){
16745             return document.getElementById(nodeInfo);
16746         }else if(typeof nodeInfo == "number"){
16747             return this.nodes[nodeInfo];
16748         }
16749         return nodeInfo;
16750     },
16751
16752     /**
16753      * Gets a range template nodes.
16754      * @param {Number} startIndex
16755      * @param {Number} endIndex
16756      * @return {Array} An array of nodes
16757      */
16758     getNodes : function(start, end){
16759         var ns = this.nodes;
16760         start = start || 0;
16761         end = typeof end == "undefined" ? ns.length - 1 : end;
16762         var nodes = [];
16763         if(start <= end){
16764             for(var i = start; i <= end; i++){
16765                 nodes.push(ns[i]);
16766             }
16767         } else{
16768             for(var i = start; i >= end; i--){
16769                 nodes.push(ns[i]);
16770             }
16771         }
16772         return nodes;
16773     },
16774
16775     /**
16776      * Finds the index of the passed node
16777      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16778      * @return {Number} The index of the node or -1
16779      */
16780     indexOf : function(node){
16781         node = this.getNode(node);
16782         if(typeof node.nodeIndex == "number"){
16783             return node.nodeIndex;
16784         }
16785         var ns = this.nodes;
16786         for(var i = 0, len = ns.length; i < len; i++){
16787             if(ns[i] == node){
16788                 return i;
16789             }
16790         }
16791         return -1;
16792     }
16793 });
16794 /*
16795  * - LGPL
16796  *
16797  * based on jquery fullcalendar
16798  * 
16799  */
16800
16801 Roo.bootstrap = Roo.bootstrap || {};
16802 /**
16803  * @class Roo.bootstrap.Calendar
16804  * @extends Roo.bootstrap.Component
16805  * Bootstrap Calendar class
16806  * @cfg {Boolean} loadMask (true|false) default false
16807  * @cfg {Object} header generate the user specific header of the calendar, default false
16808
16809  * @constructor
16810  * Create a new Container
16811  * @param {Object} config The config object
16812  */
16813
16814
16815
16816 Roo.bootstrap.Calendar = function(config){
16817     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16818      this.addEvents({
16819         /**
16820              * @event select
16821              * Fires when a date is selected
16822              * @param {DatePicker} this
16823              * @param {Date} date The selected date
16824              */
16825         'select': true,
16826         /**
16827              * @event monthchange
16828              * Fires when the displayed month changes 
16829              * @param {DatePicker} this
16830              * @param {Date} date The selected month
16831              */
16832         'monthchange': true,
16833         /**
16834              * @event evententer
16835              * Fires when mouse over an event
16836              * @param {Calendar} this
16837              * @param {event} Event
16838              */
16839         'evententer': true,
16840         /**
16841              * @event eventleave
16842              * Fires when the mouse leaves an
16843              * @param {Calendar} this
16844              * @param {event}
16845              */
16846         'eventleave': true,
16847         /**
16848              * @event eventclick
16849              * Fires when the mouse click an
16850              * @param {Calendar} this
16851              * @param {event}
16852              */
16853         'eventclick': true
16854         
16855     });
16856
16857 };
16858
16859 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16860     
16861      /**
16862      * @cfg {Number} startDay
16863      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16864      */
16865     startDay : 0,
16866     
16867     loadMask : false,
16868     
16869     header : false,
16870       
16871     getAutoCreate : function(){
16872         
16873         
16874         var fc_button = function(name, corner, style, content ) {
16875             return Roo.apply({},{
16876                 tag : 'span',
16877                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16878                          (corner.length ?
16879                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16880                             ''
16881                         ),
16882                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16883                 unselectable: 'on'
16884             });
16885         };
16886         
16887         var header = {};
16888         
16889         if(!this.header){
16890             header = {
16891                 tag : 'table',
16892                 cls : 'fc-header',
16893                 style : 'width:100%',
16894                 cn : [
16895                     {
16896                         tag: 'tr',
16897                         cn : [
16898                             {
16899                                 tag : 'td',
16900                                 cls : 'fc-header-left',
16901                                 cn : [
16902                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16903                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16904                                     { tag: 'span', cls: 'fc-header-space' },
16905                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16906
16907
16908                                 ]
16909                             },
16910
16911                             {
16912                                 tag : 'td',
16913                                 cls : 'fc-header-center',
16914                                 cn : [
16915                                     {
16916                                         tag: 'span',
16917                                         cls: 'fc-header-title',
16918                                         cn : {
16919                                             tag: 'H2',
16920                                             html : 'month / year'
16921                                         }
16922                                     }
16923
16924                                 ]
16925                             },
16926                             {
16927                                 tag : 'td',
16928                                 cls : 'fc-header-right',
16929                                 cn : [
16930                               /*      fc_button('month', 'left', '', 'month' ),
16931                                     fc_button('week', '', '', 'week' ),
16932                                     fc_button('day', 'right', '', 'day' )
16933                                 */    
16934
16935                                 ]
16936                             }
16937
16938                         ]
16939                     }
16940                 ]
16941             };
16942         }
16943         
16944         header = this.header;
16945         
16946        
16947         var cal_heads = function() {
16948             var ret = [];
16949             // fixme - handle this.
16950             
16951             for (var i =0; i < Date.dayNames.length; i++) {
16952                 var d = Date.dayNames[i];
16953                 ret.push({
16954                     tag: 'th',
16955                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16956                     html : d.substring(0,3)
16957                 });
16958                 
16959             }
16960             ret[0].cls += ' fc-first';
16961             ret[6].cls += ' fc-last';
16962             return ret;
16963         };
16964         var cal_cell = function(n) {
16965             return  {
16966                 tag: 'td',
16967                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16968                 cn : [
16969                     {
16970                         cn : [
16971                             {
16972                                 cls: 'fc-day-number',
16973                                 html: 'D'
16974                             },
16975                             {
16976                                 cls: 'fc-day-content',
16977                              
16978                                 cn : [
16979                                      {
16980                                         style: 'position: relative;' // height: 17px;
16981                                     }
16982                                 ]
16983                             }
16984                             
16985                             
16986                         ]
16987                     }
16988                 ]
16989                 
16990             }
16991         };
16992         var cal_rows = function() {
16993             
16994             var ret = [];
16995             for (var r = 0; r < 6; r++) {
16996                 var row= {
16997                     tag : 'tr',
16998                     cls : 'fc-week',
16999                     cn : []
17000                 };
17001                 
17002                 for (var i =0; i < Date.dayNames.length; i++) {
17003                     var d = Date.dayNames[i];
17004                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17005
17006                 }
17007                 row.cn[0].cls+=' fc-first';
17008                 row.cn[0].cn[0].style = 'min-height:90px';
17009                 row.cn[6].cls+=' fc-last';
17010                 ret.push(row);
17011                 
17012             }
17013             ret[0].cls += ' fc-first';
17014             ret[4].cls += ' fc-prev-last';
17015             ret[5].cls += ' fc-last';
17016             return ret;
17017             
17018         };
17019         
17020         var cal_table = {
17021             tag: 'table',
17022             cls: 'fc-border-separate',
17023             style : 'width:100%',
17024             cellspacing  : 0,
17025             cn : [
17026                 { 
17027                     tag: 'thead',
17028                     cn : [
17029                         { 
17030                             tag: 'tr',
17031                             cls : 'fc-first fc-last',
17032                             cn : cal_heads()
17033                         }
17034                     ]
17035                 },
17036                 { 
17037                     tag: 'tbody',
17038                     cn : cal_rows()
17039                 }
17040                   
17041             ]
17042         };
17043          
17044          var cfg = {
17045             cls : 'fc fc-ltr',
17046             cn : [
17047                 header,
17048                 {
17049                     cls : 'fc-content',
17050                     style : "position: relative;",
17051                     cn : [
17052                         {
17053                             cls : 'fc-view fc-view-month fc-grid',
17054                             style : 'position: relative',
17055                             unselectable : 'on',
17056                             cn : [
17057                                 {
17058                                     cls : 'fc-event-container',
17059                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17060                                 },
17061                                 cal_table
17062                             ]
17063                         }
17064                     ]
17065     
17066                 }
17067            ] 
17068             
17069         };
17070         
17071          
17072         
17073         return cfg;
17074     },
17075     
17076     
17077     initEvents : function()
17078     {
17079         if(!this.store){
17080             throw "can not find store for calendar";
17081         }
17082         
17083         var mark = {
17084             tag: "div",
17085             cls:"x-dlg-mask",
17086             style: "text-align:center",
17087             cn: [
17088                 {
17089                     tag: "div",
17090                     style: "background-color:white;width:50%;margin:250 auto",
17091                     cn: [
17092                         {
17093                             tag: "img",
17094                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17095                         },
17096                         {
17097                             tag: "span",
17098                             html: "Loading"
17099                         }
17100                         
17101                     ]
17102                 }
17103             ]
17104         };
17105         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17106         
17107         var size = this.el.select('.fc-content', true).first().getSize();
17108         this.maskEl.setSize(size.width, size.height);
17109         this.maskEl.enableDisplayMode("block");
17110         if(!this.loadMask){
17111             this.maskEl.hide();
17112         }
17113         
17114         this.store = Roo.factory(this.store, Roo.data);
17115         this.store.on('load', this.onLoad, this);
17116         this.store.on('beforeload', this.onBeforeLoad, this);
17117         
17118         this.resize();
17119         
17120         this.cells = this.el.select('.fc-day',true);
17121         //Roo.log(this.cells);
17122         this.textNodes = this.el.query('.fc-day-number');
17123         this.cells.addClassOnOver('fc-state-hover');
17124         
17125         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17126         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17127         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17128         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17129         
17130         this.on('monthchange', this.onMonthChange, this);
17131         
17132         this.update(new Date().clearTime());
17133     },
17134     
17135     resize : function() {
17136         var sz  = this.el.getSize();
17137         
17138         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17139         this.el.select('.fc-day-content div',true).setHeight(34);
17140     },
17141     
17142     
17143     // private
17144     showPrevMonth : function(e){
17145         this.update(this.activeDate.add("mo", -1));
17146     },
17147     showToday : function(e){
17148         this.update(new Date().clearTime());
17149     },
17150     // private
17151     showNextMonth : function(e){
17152         this.update(this.activeDate.add("mo", 1));
17153     },
17154
17155     // private
17156     showPrevYear : function(){
17157         this.update(this.activeDate.add("y", -1));
17158     },
17159
17160     // private
17161     showNextYear : function(){
17162         this.update(this.activeDate.add("y", 1));
17163     },
17164
17165     
17166    // private
17167     update : function(date)
17168     {
17169         var vd = this.activeDate;
17170         this.activeDate = date;
17171 //        if(vd && this.el){
17172 //            var t = date.getTime();
17173 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17174 //                Roo.log('using add remove');
17175 //                
17176 //                this.fireEvent('monthchange', this, date);
17177 //                
17178 //                this.cells.removeClass("fc-state-highlight");
17179 //                this.cells.each(function(c){
17180 //                   if(c.dateValue == t){
17181 //                       c.addClass("fc-state-highlight");
17182 //                       setTimeout(function(){
17183 //                            try{c.dom.firstChild.focus();}catch(e){}
17184 //                       }, 50);
17185 //                       return false;
17186 //                   }
17187 //                   return true;
17188 //                });
17189 //                return;
17190 //            }
17191 //        }
17192         
17193         var days = date.getDaysInMonth();
17194         
17195         var firstOfMonth = date.getFirstDateOfMonth();
17196         var startingPos = firstOfMonth.getDay()-this.startDay;
17197         
17198         if(startingPos < this.startDay){
17199             startingPos += 7;
17200         }
17201         
17202         var pm = date.add(Date.MONTH, -1);
17203         var prevStart = pm.getDaysInMonth()-startingPos;
17204 //        
17205         this.cells = this.el.select('.fc-day',true);
17206         this.textNodes = this.el.query('.fc-day-number');
17207         this.cells.addClassOnOver('fc-state-hover');
17208         
17209         var cells = this.cells.elements;
17210         var textEls = this.textNodes;
17211         
17212         Roo.each(cells, function(cell){
17213             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17214         });
17215         
17216         days += startingPos;
17217
17218         // convert everything to numbers so it's fast
17219         var day = 86400000;
17220         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17221         //Roo.log(d);
17222         //Roo.log(pm);
17223         //Roo.log(prevStart);
17224         
17225         var today = new Date().clearTime().getTime();
17226         var sel = date.clearTime().getTime();
17227         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17228         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17229         var ddMatch = this.disabledDatesRE;
17230         var ddText = this.disabledDatesText;
17231         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17232         var ddaysText = this.disabledDaysText;
17233         var format = this.format;
17234         
17235         var setCellClass = function(cal, cell){
17236             cell.row = 0;
17237             cell.events = [];
17238             cell.more = [];
17239             //Roo.log('set Cell Class');
17240             cell.title = "";
17241             var t = d.getTime();
17242             
17243             //Roo.log(d);
17244             
17245             cell.dateValue = t;
17246             if(t == today){
17247                 cell.className += " fc-today";
17248                 cell.className += " fc-state-highlight";
17249                 cell.title = cal.todayText;
17250             }
17251             if(t == sel){
17252                 // disable highlight in other month..
17253                 //cell.className += " fc-state-highlight";
17254                 
17255             }
17256             // disabling
17257             if(t < min) {
17258                 cell.className = " fc-state-disabled";
17259                 cell.title = cal.minText;
17260                 return;
17261             }
17262             if(t > max) {
17263                 cell.className = " fc-state-disabled";
17264                 cell.title = cal.maxText;
17265                 return;
17266             }
17267             if(ddays){
17268                 if(ddays.indexOf(d.getDay()) != -1){
17269                     cell.title = ddaysText;
17270                     cell.className = " fc-state-disabled";
17271                 }
17272             }
17273             if(ddMatch && format){
17274                 var fvalue = d.dateFormat(format);
17275                 if(ddMatch.test(fvalue)){
17276                     cell.title = ddText.replace("%0", fvalue);
17277                     cell.className = " fc-state-disabled";
17278                 }
17279             }
17280             
17281             if (!cell.initialClassName) {
17282                 cell.initialClassName = cell.dom.className;
17283             }
17284             
17285             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17286         };
17287
17288         var i = 0;
17289         
17290         for(; i < startingPos; i++) {
17291             textEls[i].innerHTML = (++prevStart);
17292             d.setDate(d.getDate()+1);
17293             
17294             cells[i].className = "fc-past fc-other-month";
17295             setCellClass(this, cells[i]);
17296         }
17297         
17298         var intDay = 0;
17299         
17300         for(; i < days; i++){
17301             intDay = i - startingPos + 1;
17302             textEls[i].innerHTML = (intDay);
17303             d.setDate(d.getDate()+1);
17304             
17305             cells[i].className = ''; // "x-date-active";
17306             setCellClass(this, cells[i]);
17307         }
17308         var extraDays = 0;
17309         
17310         for(; i < 42; i++) {
17311             textEls[i].innerHTML = (++extraDays);
17312             d.setDate(d.getDate()+1);
17313             
17314             cells[i].className = "fc-future fc-other-month";
17315             setCellClass(this, cells[i]);
17316         }
17317         
17318         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17319         
17320         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17321         
17322         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17323         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17324         
17325         if(totalRows != 6){
17326             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17327             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17328         }
17329         
17330         this.fireEvent('monthchange', this, date);
17331         
17332         
17333         /*
17334         if(!this.internalRender){
17335             var main = this.el.dom.firstChild;
17336             var w = main.offsetWidth;
17337             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17338             Roo.fly(main).setWidth(w);
17339             this.internalRender = true;
17340             // opera does not respect the auto grow header center column
17341             // then, after it gets a width opera refuses to recalculate
17342             // without a second pass
17343             if(Roo.isOpera && !this.secondPass){
17344                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17345                 this.secondPass = true;
17346                 this.update.defer(10, this, [date]);
17347             }
17348         }
17349         */
17350         
17351     },
17352     
17353     findCell : function(dt) {
17354         dt = dt.clearTime().getTime();
17355         var ret = false;
17356         this.cells.each(function(c){
17357             //Roo.log("check " +c.dateValue + '?=' + dt);
17358             if(c.dateValue == dt){
17359                 ret = c;
17360                 return false;
17361             }
17362             return true;
17363         });
17364         
17365         return ret;
17366     },
17367     
17368     findCells : function(ev) {
17369         var s = ev.start.clone().clearTime().getTime();
17370        // Roo.log(s);
17371         var e= ev.end.clone().clearTime().getTime();
17372        // Roo.log(e);
17373         var ret = [];
17374         this.cells.each(function(c){
17375              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17376             
17377             if(c.dateValue > e){
17378                 return ;
17379             }
17380             if(c.dateValue < s){
17381                 return ;
17382             }
17383             ret.push(c);
17384         });
17385         
17386         return ret;    
17387     },
17388     
17389 //    findBestRow: function(cells)
17390 //    {
17391 //        var ret = 0;
17392 //        
17393 //        for (var i =0 ; i < cells.length;i++) {
17394 //            ret  = Math.max(cells[i].rows || 0,ret);
17395 //        }
17396 //        return ret;
17397 //        
17398 //    },
17399     
17400     
17401     addItem : function(ev)
17402     {
17403         // look for vertical location slot in
17404         var cells = this.findCells(ev);
17405         
17406 //        ev.row = this.findBestRow(cells);
17407         
17408         // work out the location.
17409         
17410         var crow = false;
17411         var rows = [];
17412         for(var i =0; i < cells.length; i++) {
17413             
17414             cells[i].row = cells[0].row;
17415             
17416             if(i == 0){
17417                 cells[i].row = cells[i].row + 1;
17418             }
17419             
17420             if (!crow) {
17421                 crow = {
17422                     start : cells[i],
17423                     end :  cells[i]
17424                 };
17425                 continue;
17426             }
17427             if (crow.start.getY() == cells[i].getY()) {
17428                 // on same row.
17429                 crow.end = cells[i];
17430                 continue;
17431             }
17432             // different row.
17433             rows.push(crow);
17434             crow = {
17435                 start: cells[i],
17436                 end : cells[i]
17437             };
17438             
17439         }
17440         
17441         rows.push(crow);
17442         ev.els = [];
17443         ev.rows = rows;
17444         ev.cells = cells;
17445         
17446         cells[0].events.push(ev);
17447         
17448         this.calevents.push(ev);
17449     },
17450     
17451     clearEvents: function() {
17452         
17453         if(!this.calevents){
17454             return;
17455         }
17456         
17457         Roo.each(this.cells.elements, function(c){
17458             c.row = 0;
17459             c.events = [];
17460             c.more = [];
17461         });
17462         
17463         Roo.each(this.calevents, function(e) {
17464             Roo.each(e.els, function(el) {
17465                 el.un('mouseenter' ,this.onEventEnter, this);
17466                 el.un('mouseleave' ,this.onEventLeave, this);
17467                 el.remove();
17468             },this);
17469         },this);
17470         
17471         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17472             e.remove();
17473         });
17474         
17475     },
17476     
17477     renderEvents: function()
17478     {   
17479         var _this = this;
17480         
17481         this.cells.each(function(c) {
17482             
17483             if(c.row < 5){
17484                 return;
17485             }
17486             
17487             var ev = c.events;
17488             
17489             var r = 4;
17490             if(c.row != c.events.length){
17491                 r = 4 - (4 - (c.row - c.events.length));
17492             }
17493             
17494             c.events = ev.slice(0, r);
17495             c.more = ev.slice(r);
17496             
17497             if(c.more.length && c.more.length == 1){
17498                 c.events.push(c.more.pop());
17499             }
17500             
17501             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17502             
17503         });
17504             
17505         this.cells.each(function(c) {
17506             
17507             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17508             
17509             
17510             for (var e = 0; e < c.events.length; e++){
17511                 var ev = c.events[e];
17512                 var rows = ev.rows;
17513                 
17514                 for(var i = 0; i < rows.length; i++) {
17515                 
17516                     // how many rows should it span..
17517
17518                     var  cfg = {
17519                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17520                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17521
17522                         unselectable : "on",
17523                         cn : [
17524                             {
17525                                 cls: 'fc-event-inner',
17526                                 cn : [
17527     //                                {
17528     //                                  tag:'span',
17529     //                                  cls: 'fc-event-time',
17530     //                                  html : cells.length > 1 ? '' : ev.time
17531     //                                },
17532                                     {
17533                                       tag:'span',
17534                                       cls: 'fc-event-title',
17535                                       html : String.format('{0}', ev.title)
17536                                     }
17537
17538
17539                                 ]
17540                             },
17541                             {
17542                                 cls: 'ui-resizable-handle ui-resizable-e',
17543                                 html : '&nbsp;&nbsp;&nbsp'
17544                             }
17545
17546                         ]
17547                     };
17548
17549                     if (i == 0) {
17550                         cfg.cls += ' fc-event-start';
17551                     }
17552                     if ((i+1) == rows.length) {
17553                         cfg.cls += ' fc-event-end';
17554                     }
17555
17556                     var ctr = _this.el.select('.fc-event-container',true).first();
17557                     var cg = ctr.createChild(cfg);
17558
17559                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17560                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17561
17562                     var r = (c.more.length) ? 1 : 0;
17563                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17564                     cg.setWidth(ebox.right - sbox.x -2);
17565
17566                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17567                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17568                     cg.on('click', _this.onEventClick, _this, ev);
17569
17570                     ev.els.push(cg);
17571                     
17572                 }
17573                 
17574             }
17575             
17576             
17577             if(c.more.length){
17578                 var  cfg = {
17579                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17580                     style : 'position: absolute',
17581                     unselectable : "on",
17582                     cn : [
17583                         {
17584                             cls: 'fc-event-inner',
17585                             cn : [
17586                                 {
17587                                   tag:'span',
17588                                   cls: 'fc-event-title',
17589                                   html : 'More'
17590                                 }
17591
17592
17593                             ]
17594                         },
17595                         {
17596                             cls: 'ui-resizable-handle ui-resizable-e',
17597                             html : '&nbsp;&nbsp;&nbsp'
17598                         }
17599
17600                     ]
17601                 };
17602
17603                 var ctr = _this.el.select('.fc-event-container',true).first();
17604                 var cg = ctr.createChild(cfg);
17605
17606                 var sbox = c.select('.fc-day-content',true).first().getBox();
17607                 var ebox = c.select('.fc-day-content',true).first().getBox();
17608                 //Roo.log(cg);
17609                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17610                 cg.setWidth(ebox.right - sbox.x -2);
17611
17612                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17613                 
17614             }
17615             
17616         });
17617         
17618         
17619         
17620     },
17621     
17622     onEventEnter: function (e, el,event,d) {
17623         this.fireEvent('evententer', this, el, event);
17624     },
17625     
17626     onEventLeave: function (e, el,event,d) {
17627         this.fireEvent('eventleave', this, el, event);
17628     },
17629     
17630     onEventClick: function (e, el,event,d) {
17631         this.fireEvent('eventclick', this, el, event);
17632     },
17633     
17634     onMonthChange: function () {
17635         this.store.load();
17636     },
17637     
17638     onMoreEventClick: function(e, el, more)
17639     {
17640         var _this = this;
17641         
17642         this.calpopover.placement = 'right';
17643         this.calpopover.setTitle('More');
17644         
17645         this.calpopover.setContent('');
17646         
17647         var ctr = this.calpopover.el.select('.popover-content', true).first();
17648         
17649         Roo.each(more, function(m){
17650             var cfg = {
17651                 cls : 'fc-event-hori fc-event-draggable',
17652                 html : m.title
17653             };
17654             var cg = ctr.createChild(cfg);
17655             
17656             cg.on('click', _this.onEventClick, _this, m);
17657         });
17658         
17659         this.calpopover.show(el);
17660         
17661         
17662     },
17663     
17664     onLoad: function () 
17665     {   
17666         this.calevents = [];
17667         var cal = this;
17668         
17669         if(this.store.getCount() > 0){
17670             this.store.data.each(function(d){
17671                cal.addItem({
17672                     id : d.data.id,
17673                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17674                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17675                     time : d.data.start_time,
17676                     title : d.data.title,
17677                     description : d.data.description,
17678                     venue : d.data.venue
17679                 });
17680             });
17681         }
17682         
17683         this.renderEvents();
17684         
17685         if(this.calevents.length && this.loadMask){
17686             this.maskEl.hide();
17687         }
17688     },
17689     
17690     onBeforeLoad: function()
17691     {
17692         this.clearEvents();
17693         if(this.loadMask){
17694             this.maskEl.show();
17695         }
17696     }
17697 });
17698
17699  
17700  /*
17701  * - LGPL
17702  *
17703  * element
17704  * 
17705  */
17706
17707 /**
17708  * @class Roo.bootstrap.Popover
17709  * @extends Roo.bootstrap.Component
17710  * Bootstrap Popover class
17711  * @cfg {String} html contents of the popover   (or false to use children..)
17712  * @cfg {String} title of popover (or false to hide)
17713  * @cfg {String} placement how it is placed
17714  * @cfg {String} trigger click || hover (or false to trigger manually)
17715  * @cfg {String} over what (parent or false to trigger manually.)
17716  * @cfg {Number} delay - delay before showing
17717  
17718  * @constructor
17719  * Create a new Popover
17720  * @param {Object} config The config object
17721  */
17722
17723 Roo.bootstrap.Popover = function(config){
17724     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17725     
17726     this.addEvents({
17727         // raw events
17728          /**
17729          * @event show
17730          * After the popover show
17731          * 
17732          * @param {Roo.bootstrap.Popover} this
17733          */
17734         "show" : true,
17735         /**
17736          * @event hide
17737          * After the popover hide
17738          * 
17739          * @param {Roo.bootstrap.Popover} this
17740          */
17741         "hide" : true
17742     });
17743 };
17744
17745 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17746     
17747     title: 'Fill in a title',
17748     html: false,
17749     
17750     placement : 'right',
17751     trigger : 'hover', // hover
17752     
17753     delay : 0,
17754     
17755     over: 'parent',
17756     
17757     can_build_overlaid : false,
17758     
17759     getChildContainer : function()
17760     {
17761         return this.el.select('.popover-content',true).first();
17762     },
17763     
17764     getAutoCreate : function(){
17765          
17766         var cfg = {
17767            cls : 'popover roo-dynamic',
17768            style: 'display:block',
17769            cn : [
17770                 {
17771                     cls : 'arrow'
17772                 },
17773                 {
17774                     cls : 'popover-inner',
17775                     cn : [
17776                         {
17777                             tag: 'h3',
17778                             cls: 'popover-title popover-header',
17779                             html : this.title
17780                         },
17781                         {
17782                             cls : 'popover-content popover-body',
17783                             html : this.html
17784                         }
17785                     ]
17786                     
17787                 }
17788            ]
17789         };
17790         
17791         return cfg;
17792     },
17793     setTitle: function(str)
17794     {
17795         this.title = str;
17796         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17797     },
17798     setContent: function(str)
17799     {
17800         this.html = str;
17801         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17802     },
17803     // as it get's added to the bottom of the page.
17804     onRender : function(ct, position)
17805     {
17806         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17807         if(!this.el){
17808             var cfg = Roo.apply({},  this.getAutoCreate());
17809             cfg.id = Roo.id();
17810             
17811             if (this.cls) {
17812                 cfg.cls += ' ' + this.cls;
17813             }
17814             if (this.style) {
17815                 cfg.style = this.style;
17816             }
17817             //Roo.log("adding to ");
17818             this.el = Roo.get(document.body).createChild(cfg, position);
17819 //            Roo.log(this.el);
17820         }
17821         this.initEvents();
17822     },
17823     
17824     initEvents : function()
17825     {
17826         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17827         this.el.enableDisplayMode('block');
17828         this.el.hide();
17829         if (this.over === false) {
17830             return; 
17831         }
17832         if (this.triggers === false) {
17833             return;
17834         }
17835         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17836         var triggers = this.trigger ? this.trigger.split(' ') : [];
17837         Roo.each(triggers, function(trigger) {
17838         
17839             if (trigger == 'click') {
17840                 on_el.on('click', this.toggle, this);
17841             } else if (trigger != 'manual') {
17842                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17843                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17844       
17845                 on_el.on(eventIn  ,this.enter, this);
17846                 on_el.on(eventOut, this.leave, this);
17847             }
17848         }, this);
17849         
17850     },
17851     
17852     
17853     // private
17854     timeout : null,
17855     hoverState : null,
17856     
17857     toggle : function () {
17858         this.hoverState == 'in' ? this.leave() : this.enter();
17859     },
17860     
17861     enter : function () {
17862         
17863         clearTimeout(this.timeout);
17864     
17865         this.hoverState = 'in';
17866     
17867         if (!this.delay || !this.delay.show) {
17868             this.show();
17869             return;
17870         }
17871         var _t = this;
17872         this.timeout = setTimeout(function () {
17873             if (_t.hoverState == 'in') {
17874                 _t.show();
17875             }
17876         }, this.delay.show)
17877     },
17878     
17879     leave : function() {
17880         clearTimeout(this.timeout);
17881     
17882         this.hoverState = 'out';
17883     
17884         if (!this.delay || !this.delay.hide) {
17885             this.hide();
17886             return;
17887         }
17888         var _t = this;
17889         this.timeout = setTimeout(function () {
17890             if (_t.hoverState == 'out') {
17891                 _t.hide();
17892             }
17893         }, this.delay.hide)
17894     },
17895     
17896     show : function (on_el)
17897     {
17898         if (!on_el) {
17899             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17900         }
17901         
17902         // set content.
17903         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17904         if (this.html !== false) {
17905             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17906         }
17907         this.el.removeClass([
17908             'fade','top','bottom', 'left', 'right','in',
17909             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17910         ]);
17911         if (!this.title.length) {
17912             this.el.select('.popover-title',true).hide();
17913         }
17914         
17915         var placement = typeof this.placement == 'function' ?
17916             this.placement.call(this, this.el, on_el) :
17917             this.placement;
17918             
17919         var autoToken = /\s?auto?\s?/i;
17920         var autoPlace = autoToken.test(placement);
17921         if (autoPlace) {
17922             placement = placement.replace(autoToken, '') || 'top';
17923         }
17924         
17925         //this.el.detach()
17926         //this.el.setXY([0,0]);
17927         this.el.show();
17928         this.el.dom.style.display='block';
17929         this.el.addClass(placement);
17930         
17931         //this.el.appendTo(on_el);
17932         
17933         var p = this.getPosition();
17934         var box = this.el.getBox();
17935         
17936         if (autoPlace) {
17937             // fixme..
17938         }
17939         var align = Roo.bootstrap.Popover.alignment[placement];
17940         
17941 //        Roo.log(align);
17942         this.el.alignTo(on_el, align[0],align[1]);
17943         //var arrow = this.el.select('.arrow',true).first();
17944         //arrow.set(align[2], 
17945         
17946         this.el.addClass('in');
17947         
17948         
17949         if (this.el.hasClass('fade')) {
17950             // fade it?
17951         }
17952         
17953         this.hoverState = 'in';
17954         
17955         this.fireEvent('show', this);
17956         
17957     },
17958     hide : function()
17959     {
17960         this.el.setXY([0,0]);
17961         this.el.removeClass('in');
17962         this.el.hide();
17963         this.hoverState = null;
17964         
17965         this.fireEvent('hide', this);
17966     }
17967     
17968 });
17969
17970 Roo.bootstrap.Popover.alignment = {
17971     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17972     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17973     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17974     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17975 };
17976
17977  /*
17978  * - LGPL
17979  *
17980  * Progress
17981  * 
17982  */
17983
17984 /**
17985  * @class Roo.bootstrap.Progress
17986  * @extends Roo.bootstrap.Component
17987  * Bootstrap Progress class
17988  * @cfg {Boolean} striped striped of the progress bar
17989  * @cfg {Boolean} active animated of the progress bar
17990  * 
17991  * 
17992  * @constructor
17993  * Create a new Progress
17994  * @param {Object} config The config object
17995  */
17996
17997 Roo.bootstrap.Progress = function(config){
17998     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17999 };
18000
18001 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18002     
18003     striped : false,
18004     active: false,
18005     
18006     getAutoCreate : function(){
18007         var cfg = {
18008             tag: 'div',
18009             cls: 'progress'
18010         };
18011         
18012         
18013         if(this.striped){
18014             cfg.cls += ' progress-striped';
18015         }
18016       
18017         if(this.active){
18018             cfg.cls += ' active';
18019         }
18020         
18021         
18022         return cfg;
18023     }
18024    
18025 });
18026
18027  
18028
18029  /*
18030  * - LGPL
18031  *
18032  * ProgressBar
18033  * 
18034  */
18035
18036 /**
18037  * @class Roo.bootstrap.ProgressBar
18038  * @extends Roo.bootstrap.Component
18039  * Bootstrap ProgressBar class
18040  * @cfg {Number} aria_valuenow aria-value now
18041  * @cfg {Number} aria_valuemin aria-value min
18042  * @cfg {Number} aria_valuemax aria-value max
18043  * @cfg {String} label label for the progress bar
18044  * @cfg {String} panel (success | info | warning | danger )
18045  * @cfg {String} role role of the progress bar
18046  * @cfg {String} sr_only text
18047  * 
18048  * 
18049  * @constructor
18050  * Create a new ProgressBar
18051  * @param {Object} config The config object
18052  */
18053
18054 Roo.bootstrap.ProgressBar = function(config){
18055     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18056 };
18057
18058 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18059     
18060     aria_valuenow : 0,
18061     aria_valuemin : 0,
18062     aria_valuemax : 100,
18063     label : false,
18064     panel : false,
18065     role : false,
18066     sr_only: false,
18067     
18068     getAutoCreate : function()
18069     {
18070         
18071         var cfg = {
18072             tag: 'div',
18073             cls: 'progress-bar',
18074             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18075         };
18076         
18077         if(this.sr_only){
18078             cfg.cn = {
18079                 tag: 'span',
18080                 cls: 'sr-only',
18081                 html: this.sr_only
18082             }
18083         }
18084         
18085         if(this.role){
18086             cfg.role = this.role;
18087         }
18088         
18089         if(this.aria_valuenow){
18090             cfg['aria-valuenow'] = this.aria_valuenow;
18091         }
18092         
18093         if(this.aria_valuemin){
18094             cfg['aria-valuemin'] = this.aria_valuemin;
18095         }
18096         
18097         if(this.aria_valuemax){
18098             cfg['aria-valuemax'] = this.aria_valuemax;
18099         }
18100         
18101         if(this.label && !this.sr_only){
18102             cfg.html = this.label;
18103         }
18104         
18105         if(this.panel){
18106             cfg.cls += ' progress-bar-' + this.panel;
18107         }
18108         
18109         return cfg;
18110     },
18111     
18112     update : function(aria_valuenow)
18113     {
18114         this.aria_valuenow = aria_valuenow;
18115         
18116         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18117     }
18118    
18119 });
18120
18121  
18122
18123  /*
18124  * - LGPL
18125  *
18126  * column
18127  * 
18128  */
18129
18130 /**
18131  * @class Roo.bootstrap.TabGroup
18132  * @extends Roo.bootstrap.Column
18133  * Bootstrap Column class
18134  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18135  * @cfg {Boolean} carousel true to make the group behave like a carousel
18136  * @cfg {Boolean} bullets show bullets for the panels
18137  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18138  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18139  * @cfg {Boolean} showarrow (true|false) show arrow default true
18140  * 
18141  * @constructor
18142  * Create a new TabGroup
18143  * @param {Object} config The config object
18144  */
18145
18146 Roo.bootstrap.TabGroup = function(config){
18147     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18148     if (!this.navId) {
18149         this.navId = Roo.id();
18150     }
18151     this.tabs = [];
18152     Roo.bootstrap.TabGroup.register(this);
18153     
18154 };
18155
18156 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18157     
18158     carousel : false,
18159     transition : false,
18160     bullets : 0,
18161     timer : 0,
18162     autoslide : false,
18163     slideFn : false,
18164     slideOnTouch : false,
18165     showarrow : true,
18166     
18167     getAutoCreate : function()
18168     {
18169         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18170         
18171         cfg.cls += ' tab-content';
18172         
18173         if (this.carousel) {
18174             cfg.cls += ' carousel slide';
18175             
18176             cfg.cn = [{
18177                cls : 'carousel-inner',
18178                cn : []
18179             }];
18180         
18181             if(this.bullets  && !Roo.isTouch){
18182                 
18183                 var bullets = {
18184                     cls : 'carousel-bullets',
18185                     cn : []
18186                 };
18187                
18188                 if(this.bullets_cls){
18189                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18190                 }
18191                 
18192                 bullets.cn.push({
18193                     cls : 'clear'
18194                 });
18195                 
18196                 cfg.cn[0].cn.push(bullets);
18197             }
18198             
18199             if(this.showarrow){
18200                 cfg.cn[0].cn.push({
18201                     tag : 'div',
18202                     class : 'carousel-arrow',
18203                     cn : [
18204                         {
18205                             tag : 'div',
18206                             class : 'carousel-prev',
18207                             cn : [
18208                                 {
18209                                     tag : 'i',
18210                                     class : 'fa fa-chevron-left'
18211                                 }
18212                             ]
18213                         },
18214                         {
18215                             tag : 'div',
18216                             class : 'carousel-next',
18217                             cn : [
18218                                 {
18219                                     tag : 'i',
18220                                     class : 'fa fa-chevron-right'
18221                                 }
18222                             ]
18223                         }
18224                     ]
18225                 });
18226             }
18227             
18228         }
18229         
18230         return cfg;
18231     },
18232     
18233     initEvents:  function()
18234     {
18235 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18236 //            this.el.on("touchstart", this.onTouchStart, this);
18237 //        }
18238         
18239         if(this.autoslide){
18240             var _this = this;
18241             
18242             this.slideFn = window.setInterval(function() {
18243                 _this.showPanelNext();
18244             }, this.timer);
18245         }
18246         
18247         if(this.showarrow){
18248             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18249             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18250         }
18251         
18252         
18253     },
18254     
18255 //    onTouchStart : function(e, el, o)
18256 //    {
18257 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18258 //            return;
18259 //        }
18260 //        
18261 //        this.showPanelNext();
18262 //    },
18263     
18264     
18265     getChildContainer : function()
18266     {
18267         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18268     },
18269     
18270     /**
18271     * register a Navigation item
18272     * @param {Roo.bootstrap.NavItem} the navitem to add
18273     */
18274     register : function(item)
18275     {
18276         this.tabs.push( item);
18277         item.navId = this.navId; // not really needed..
18278         this.addBullet();
18279     
18280     },
18281     
18282     getActivePanel : function()
18283     {
18284         var r = false;
18285         Roo.each(this.tabs, function(t) {
18286             if (t.active) {
18287                 r = t;
18288                 return false;
18289             }
18290             return null;
18291         });
18292         return r;
18293         
18294     },
18295     getPanelByName : function(n)
18296     {
18297         var r = false;
18298         Roo.each(this.tabs, function(t) {
18299             if (t.tabId == n) {
18300                 r = t;
18301                 return false;
18302             }
18303             return null;
18304         });
18305         return r;
18306     },
18307     indexOfPanel : function(p)
18308     {
18309         var r = false;
18310         Roo.each(this.tabs, function(t,i) {
18311             if (t.tabId == p.tabId) {
18312                 r = i;
18313                 return false;
18314             }
18315             return null;
18316         });
18317         return r;
18318     },
18319     /**
18320      * show a specific panel
18321      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18322      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18323      */
18324     showPanel : function (pan)
18325     {
18326         if(this.transition || typeof(pan) == 'undefined'){
18327             Roo.log("waiting for the transitionend");
18328             return;
18329         }
18330         
18331         if (typeof(pan) == 'number') {
18332             pan = this.tabs[pan];
18333         }
18334         
18335         if (typeof(pan) == 'string') {
18336             pan = this.getPanelByName(pan);
18337         }
18338         
18339         var cur = this.getActivePanel();
18340         
18341         if(!pan || !cur){
18342             Roo.log('pan or acitve pan is undefined');
18343             return false;
18344         }
18345         
18346         if (pan.tabId == this.getActivePanel().tabId) {
18347             return true;
18348         }
18349         
18350         if (false === cur.fireEvent('beforedeactivate')) {
18351             return false;
18352         }
18353         
18354         if(this.bullets > 0 && !Roo.isTouch){
18355             this.setActiveBullet(this.indexOfPanel(pan));
18356         }
18357         
18358         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18359             
18360             this.transition = true;
18361             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18362             var lr = dir == 'next' ? 'left' : 'right';
18363             pan.el.addClass(dir); // or prev
18364             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18365             cur.el.addClass(lr); // or right
18366             pan.el.addClass(lr);
18367             
18368             var _this = this;
18369             cur.el.on('transitionend', function() {
18370                 Roo.log("trans end?");
18371                 
18372                 pan.el.removeClass([lr,dir]);
18373                 pan.setActive(true);
18374                 
18375                 cur.el.removeClass([lr]);
18376                 cur.setActive(false);
18377                 
18378                 _this.transition = false;
18379                 
18380             }, this, { single:  true } );
18381             
18382             return true;
18383         }
18384         
18385         cur.setActive(false);
18386         pan.setActive(true);
18387         
18388         return true;
18389         
18390     },
18391     showPanelNext : function()
18392     {
18393         var i = this.indexOfPanel(this.getActivePanel());
18394         
18395         if (i >= this.tabs.length - 1 && !this.autoslide) {
18396             return;
18397         }
18398         
18399         if (i >= this.tabs.length - 1 && this.autoslide) {
18400             i = -1;
18401         }
18402         
18403         this.showPanel(this.tabs[i+1]);
18404     },
18405     
18406     showPanelPrev : function()
18407     {
18408         var i = this.indexOfPanel(this.getActivePanel());
18409         
18410         if (i  < 1 && !this.autoslide) {
18411             return;
18412         }
18413         
18414         if (i < 1 && this.autoslide) {
18415             i = this.tabs.length;
18416         }
18417         
18418         this.showPanel(this.tabs[i-1]);
18419     },
18420     
18421     
18422     addBullet: function()
18423     {
18424         if(!this.bullets || Roo.isTouch){
18425             return;
18426         }
18427         var ctr = this.el.select('.carousel-bullets',true).first();
18428         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18429         var bullet = ctr.createChild({
18430             cls : 'bullet bullet-' + i
18431         },ctr.dom.lastChild);
18432         
18433         
18434         var _this = this;
18435         
18436         bullet.on('click', (function(e, el, o, ii, t){
18437
18438             e.preventDefault();
18439
18440             this.showPanel(ii);
18441
18442             if(this.autoslide && this.slideFn){
18443                 clearInterval(this.slideFn);
18444                 this.slideFn = window.setInterval(function() {
18445                     _this.showPanelNext();
18446                 }, this.timer);
18447             }
18448
18449         }).createDelegate(this, [i, bullet], true));
18450                 
18451         
18452     },
18453      
18454     setActiveBullet : function(i)
18455     {
18456         if(Roo.isTouch){
18457             return;
18458         }
18459         
18460         Roo.each(this.el.select('.bullet', true).elements, function(el){
18461             el.removeClass('selected');
18462         });
18463
18464         var bullet = this.el.select('.bullet-' + i, true).first();
18465         
18466         if(!bullet){
18467             return;
18468         }
18469         
18470         bullet.addClass('selected');
18471     }
18472     
18473     
18474   
18475 });
18476
18477  
18478
18479  
18480  
18481 Roo.apply(Roo.bootstrap.TabGroup, {
18482     
18483     groups: {},
18484      /**
18485     * register a Navigation Group
18486     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18487     */
18488     register : function(navgrp)
18489     {
18490         this.groups[navgrp.navId] = navgrp;
18491         
18492     },
18493     /**
18494     * fetch a Navigation Group based on the navigation ID
18495     * if one does not exist , it will get created.
18496     * @param {string} the navgroup to add
18497     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18498     */
18499     get: function(navId) {
18500         if (typeof(this.groups[navId]) == 'undefined') {
18501             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18502         }
18503         return this.groups[navId] ;
18504     }
18505     
18506     
18507     
18508 });
18509
18510  /*
18511  * - LGPL
18512  *
18513  * TabPanel
18514  * 
18515  */
18516
18517 /**
18518  * @class Roo.bootstrap.TabPanel
18519  * @extends Roo.bootstrap.Component
18520  * Bootstrap TabPanel class
18521  * @cfg {Boolean} active panel active
18522  * @cfg {String} html panel content
18523  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18524  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18525  * @cfg {String} href click to link..
18526  * 
18527  * 
18528  * @constructor
18529  * Create a new TabPanel
18530  * @param {Object} config The config object
18531  */
18532
18533 Roo.bootstrap.TabPanel = function(config){
18534     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18535     this.addEvents({
18536         /**
18537              * @event changed
18538              * Fires when the active status changes
18539              * @param {Roo.bootstrap.TabPanel} this
18540              * @param {Boolean} state the new state
18541             
18542          */
18543         'changed': true,
18544         /**
18545              * @event beforedeactivate
18546              * Fires before a tab is de-activated - can be used to do validation on a form.
18547              * @param {Roo.bootstrap.TabPanel} this
18548              * @return {Boolean} false if there is an error
18549             
18550          */
18551         'beforedeactivate': true
18552      });
18553     
18554     this.tabId = this.tabId || Roo.id();
18555   
18556 };
18557
18558 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18559     
18560     active: false,
18561     html: false,
18562     tabId: false,
18563     navId : false,
18564     href : '',
18565     
18566     getAutoCreate : function(){
18567         var cfg = {
18568             tag: 'div',
18569             // item is needed for carousel - not sure if it has any effect otherwise
18570             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18571             html: this.html || ''
18572         };
18573         
18574         if(this.active){
18575             cfg.cls += ' active';
18576         }
18577         
18578         if(this.tabId){
18579             cfg.tabId = this.tabId;
18580         }
18581         
18582         
18583         return cfg;
18584     },
18585     
18586     initEvents:  function()
18587     {
18588         var p = this.parent();
18589         
18590         this.navId = this.navId || p.navId;
18591         
18592         if (typeof(this.navId) != 'undefined') {
18593             // not really needed.. but just in case.. parent should be a NavGroup.
18594             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18595             
18596             tg.register(this);
18597             
18598             var i = tg.tabs.length - 1;
18599             
18600             if(this.active && tg.bullets > 0 && i < tg.bullets){
18601                 tg.setActiveBullet(i);
18602             }
18603         }
18604         
18605         this.el.on('click', this.onClick, this);
18606         
18607         if(Roo.isTouch){
18608             this.el.on("touchstart", this.onTouchStart, this);
18609             this.el.on("touchmove", this.onTouchMove, this);
18610             this.el.on("touchend", this.onTouchEnd, this);
18611         }
18612         
18613     },
18614     
18615     onRender : function(ct, position)
18616     {
18617         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18618     },
18619     
18620     setActive : function(state)
18621     {
18622         Roo.log("panel - set active " + this.tabId + "=" + state);
18623         
18624         this.active = state;
18625         if (!state) {
18626             this.el.removeClass('active');
18627             
18628         } else  if (!this.el.hasClass('active')) {
18629             this.el.addClass('active');
18630         }
18631         
18632         this.fireEvent('changed', this, state);
18633     },
18634     
18635     onClick : function(e)
18636     {
18637         e.preventDefault();
18638         
18639         if(!this.href.length){
18640             return;
18641         }
18642         
18643         window.location.href = this.href;
18644     },
18645     
18646     startX : 0,
18647     startY : 0,
18648     endX : 0,
18649     endY : 0,
18650     swiping : false,
18651     
18652     onTouchStart : function(e)
18653     {
18654         this.swiping = false;
18655         
18656         this.startX = e.browserEvent.touches[0].clientX;
18657         this.startY = e.browserEvent.touches[0].clientY;
18658     },
18659     
18660     onTouchMove : function(e)
18661     {
18662         this.swiping = true;
18663         
18664         this.endX = e.browserEvent.touches[0].clientX;
18665         this.endY = e.browserEvent.touches[0].clientY;
18666     },
18667     
18668     onTouchEnd : function(e)
18669     {
18670         if(!this.swiping){
18671             this.onClick(e);
18672             return;
18673         }
18674         
18675         var tabGroup = this.parent();
18676         
18677         if(this.endX > this.startX){ // swiping right
18678             tabGroup.showPanelPrev();
18679             return;
18680         }
18681         
18682         if(this.startX > this.endX){ // swiping left
18683             tabGroup.showPanelNext();
18684             return;
18685         }
18686     }
18687     
18688     
18689 });
18690  
18691
18692  
18693
18694  /*
18695  * - LGPL
18696  *
18697  * DateField
18698  * 
18699  */
18700
18701 /**
18702  * @class Roo.bootstrap.DateField
18703  * @extends Roo.bootstrap.Input
18704  * Bootstrap DateField class
18705  * @cfg {Number} weekStart default 0
18706  * @cfg {String} viewMode default empty, (months|years)
18707  * @cfg {String} minViewMode default empty, (months|years)
18708  * @cfg {Number} startDate default -Infinity
18709  * @cfg {Number} endDate default Infinity
18710  * @cfg {Boolean} todayHighlight default false
18711  * @cfg {Boolean} todayBtn default false
18712  * @cfg {Boolean} calendarWeeks default false
18713  * @cfg {Object} daysOfWeekDisabled default empty
18714  * @cfg {Boolean} singleMode default false (true | false)
18715  * 
18716  * @cfg {Boolean} keyboardNavigation default true
18717  * @cfg {String} language default en
18718  * 
18719  * @constructor
18720  * Create a new DateField
18721  * @param {Object} config The config object
18722  */
18723
18724 Roo.bootstrap.DateField = function(config){
18725     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18726      this.addEvents({
18727             /**
18728              * @event show
18729              * Fires when this field show.
18730              * @param {Roo.bootstrap.DateField} this
18731              * @param {Mixed} date The date value
18732              */
18733             show : true,
18734             /**
18735              * @event show
18736              * Fires when this field hide.
18737              * @param {Roo.bootstrap.DateField} this
18738              * @param {Mixed} date The date value
18739              */
18740             hide : true,
18741             /**
18742              * @event select
18743              * Fires when select a date.
18744              * @param {Roo.bootstrap.DateField} this
18745              * @param {Mixed} date The date value
18746              */
18747             select : true,
18748             /**
18749              * @event beforeselect
18750              * Fires when before select a date.
18751              * @param {Roo.bootstrap.DateField} this
18752              * @param {Mixed} date The date value
18753              */
18754             beforeselect : true
18755         });
18756 };
18757
18758 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18759     
18760     /**
18761      * @cfg {String} format
18762      * The default date format string which can be overriden for localization support.  The format must be
18763      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18764      */
18765     format : "m/d/y",
18766     /**
18767      * @cfg {String} altFormats
18768      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18769      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18770      */
18771     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18772     
18773     weekStart : 0,
18774     
18775     viewMode : '',
18776     
18777     minViewMode : '',
18778     
18779     todayHighlight : false,
18780     
18781     todayBtn: false,
18782     
18783     language: 'en',
18784     
18785     keyboardNavigation: true,
18786     
18787     calendarWeeks: false,
18788     
18789     startDate: -Infinity,
18790     
18791     endDate: Infinity,
18792     
18793     daysOfWeekDisabled: [],
18794     
18795     _events: [],
18796     
18797     singleMode : false,
18798     
18799     UTCDate: function()
18800     {
18801         return new Date(Date.UTC.apply(Date, arguments));
18802     },
18803     
18804     UTCToday: function()
18805     {
18806         var today = new Date();
18807         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18808     },
18809     
18810     getDate: function() {
18811             var d = this.getUTCDate();
18812             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18813     },
18814     
18815     getUTCDate: function() {
18816             return this.date;
18817     },
18818     
18819     setDate: function(d) {
18820             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18821     },
18822     
18823     setUTCDate: function(d) {
18824             this.date = d;
18825             this.setValue(this.formatDate(this.date));
18826     },
18827         
18828     onRender: function(ct, position)
18829     {
18830         
18831         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18832         
18833         this.language = this.language || 'en';
18834         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18835         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18836         
18837         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18838         this.format = this.format || 'm/d/y';
18839         this.isInline = false;
18840         this.isInput = true;
18841         this.component = this.el.select('.add-on', true).first() || false;
18842         this.component = (this.component && this.component.length === 0) ? false : this.component;
18843         this.hasInput = this.component && this.inputEl().length;
18844         
18845         if (typeof(this.minViewMode === 'string')) {
18846             switch (this.minViewMode) {
18847                 case 'months':
18848                     this.minViewMode = 1;
18849                     break;
18850                 case 'years':
18851                     this.minViewMode = 2;
18852                     break;
18853                 default:
18854                     this.minViewMode = 0;
18855                     break;
18856             }
18857         }
18858         
18859         if (typeof(this.viewMode === 'string')) {
18860             switch (this.viewMode) {
18861                 case 'months':
18862                     this.viewMode = 1;
18863                     break;
18864                 case 'years':
18865                     this.viewMode = 2;
18866                     break;
18867                 default:
18868                     this.viewMode = 0;
18869                     break;
18870             }
18871         }
18872                 
18873         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18874         
18875 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18876         
18877         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18878         
18879         this.picker().on('mousedown', this.onMousedown, this);
18880         this.picker().on('click', this.onClick, this);
18881         
18882         this.picker().addClass('datepicker-dropdown');
18883         
18884         this.startViewMode = this.viewMode;
18885         
18886         if(this.singleMode){
18887             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18888                 v.setVisibilityMode(Roo.Element.DISPLAY);
18889                 v.hide();
18890             });
18891             
18892             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18893                 v.setStyle('width', '189px');
18894             });
18895         }
18896         
18897         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18898             if(!this.calendarWeeks){
18899                 v.remove();
18900                 return;
18901             }
18902             
18903             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18904             v.attr('colspan', function(i, val){
18905                 return parseInt(val) + 1;
18906             });
18907         });
18908                         
18909         
18910         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18911         
18912         this.setStartDate(this.startDate);
18913         this.setEndDate(this.endDate);
18914         
18915         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18916         
18917         this.fillDow();
18918         this.fillMonths();
18919         this.update();
18920         this.showMode();
18921         
18922         if(this.isInline) {
18923             this.showPopup();
18924         }
18925     },
18926     
18927     picker : function()
18928     {
18929         return this.pickerEl;
18930 //        return this.el.select('.datepicker', true).first();
18931     },
18932     
18933     fillDow: function()
18934     {
18935         var dowCnt = this.weekStart;
18936         
18937         var dow = {
18938             tag: 'tr',
18939             cn: [
18940                 
18941             ]
18942         };
18943         
18944         if(this.calendarWeeks){
18945             dow.cn.push({
18946                 tag: 'th',
18947                 cls: 'cw',
18948                 html: '&nbsp;'
18949             })
18950         }
18951         
18952         while (dowCnt < this.weekStart + 7) {
18953             dow.cn.push({
18954                 tag: 'th',
18955                 cls: 'dow',
18956                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18957             });
18958         }
18959         
18960         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18961     },
18962     
18963     fillMonths: function()
18964     {    
18965         var i = 0;
18966         var months = this.picker().select('>.datepicker-months td', true).first();
18967         
18968         months.dom.innerHTML = '';
18969         
18970         while (i < 12) {
18971             var month = {
18972                 tag: 'span',
18973                 cls: 'month',
18974                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18975             };
18976             
18977             months.createChild(month);
18978         }
18979         
18980     },
18981     
18982     update: function()
18983     {
18984         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;
18985         
18986         if (this.date < this.startDate) {
18987             this.viewDate = new Date(this.startDate);
18988         } else if (this.date > this.endDate) {
18989             this.viewDate = new Date(this.endDate);
18990         } else {
18991             this.viewDate = new Date(this.date);
18992         }
18993         
18994         this.fill();
18995     },
18996     
18997     fill: function() 
18998     {
18999         var d = new Date(this.viewDate),
19000                 year = d.getUTCFullYear(),
19001                 month = d.getUTCMonth(),
19002                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19003                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19004                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19005                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19006                 currentDate = this.date && this.date.valueOf(),
19007                 today = this.UTCToday();
19008         
19009         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19010         
19011 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19012         
19013 //        this.picker.select('>tfoot th.today').
19014 //                                              .text(dates[this.language].today)
19015 //                                              .toggle(this.todayBtn !== false);
19016     
19017         this.updateNavArrows();
19018         this.fillMonths();
19019                                                 
19020         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19021         
19022         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19023          
19024         prevMonth.setUTCDate(day);
19025         
19026         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19027         
19028         var nextMonth = new Date(prevMonth);
19029         
19030         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19031         
19032         nextMonth = nextMonth.valueOf();
19033         
19034         var fillMonths = false;
19035         
19036         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19037         
19038         while(prevMonth.valueOf() <= nextMonth) {
19039             var clsName = '';
19040             
19041             if (prevMonth.getUTCDay() === this.weekStart) {
19042                 if(fillMonths){
19043                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19044                 }
19045                     
19046                 fillMonths = {
19047                     tag: 'tr',
19048                     cn: []
19049                 };
19050                 
19051                 if(this.calendarWeeks){
19052                     // ISO 8601: First week contains first thursday.
19053                     // ISO also states week starts on Monday, but we can be more abstract here.
19054                     var
19055                     // Start of current week: based on weekstart/current date
19056                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19057                     // Thursday of this week
19058                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19059                     // First Thursday of year, year from thursday
19060                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19061                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19062                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19063                     
19064                     fillMonths.cn.push({
19065                         tag: 'td',
19066                         cls: 'cw',
19067                         html: calWeek
19068                     });
19069                 }
19070             }
19071             
19072             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19073                 clsName += ' old';
19074             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19075                 clsName += ' new';
19076             }
19077             if (this.todayHighlight &&
19078                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19079                 prevMonth.getUTCMonth() == today.getMonth() &&
19080                 prevMonth.getUTCDate() == today.getDate()) {
19081                 clsName += ' today';
19082             }
19083             
19084             if (currentDate && prevMonth.valueOf() === currentDate) {
19085                 clsName += ' active';
19086             }
19087             
19088             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19089                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19090                     clsName += ' disabled';
19091             }
19092             
19093             fillMonths.cn.push({
19094                 tag: 'td',
19095                 cls: 'day ' + clsName,
19096                 html: prevMonth.getDate()
19097             });
19098             
19099             prevMonth.setDate(prevMonth.getDate()+1);
19100         }
19101           
19102         var currentYear = this.date && this.date.getUTCFullYear();
19103         var currentMonth = this.date && this.date.getUTCMonth();
19104         
19105         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19106         
19107         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19108             v.removeClass('active');
19109             
19110             if(currentYear === year && k === currentMonth){
19111                 v.addClass('active');
19112             }
19113             
19114             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19115                 v.addClass('disabled');
19116             }
19117             
19118         });
19119         
19120         
19121         year = parseInt(year/10, 10) * 10;
19122         
19123         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19124         
19125         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19126         
19127         year -= 1;
19128         for (var i = -1; i < 11; i++) {
19129             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19130                 tag: 'span',
19131                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19132                 html: year
19133             });
19134             
19135             year += 1;
19136         }
19137     },
19138     
19139     showMode: function(dir) 
19140     {
19141         if (dir) {
19142             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19143         }
19144         
19145         Roo.each(this.picker().select('>div',true).elements, function(v){
19146             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19147             v.hide();
19148         });
19149         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19150     },
19151     
19152     place: function()
19153     {
19154         if(this.isInline) {
19155             return;
19156         }
19157         
19158         this.picker().removeClass(['bottom', 'top']);
19159         
19160         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19161             /*
19162              * place to the top of element!
19163              *
19164              */
19165             
19166             this.picker().addClass('top');
19167             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19168             
19169             return;
19170         }
19171         
19172         this.picker().addClass('bottom');
19173         
19174         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19175     },
19176     
19177     parseDate : function(value)
19178     {
19179         if(!value || value instanceof Date){
19180             return value;
19181         }
19182         var v = Date.parseDate(value, this.format);
19183         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19184             v = Date.parseDate(value, 'Y-m-d');
19185         }
19186         if(!v && this.altFormats){
19187             if(!this.altFormatsArray){
19188                 this.altFormatsArray = this.altFormats.split("|");
19189             }
19190             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19191                 v = Date.parseDate(value, this.altFormatsArray[i]);
19192             }
19193         }
19194         return v;
19195     },
19196     
19197     formatDate : function(date, fmt)
19198     {   
19199         return (!date || !(date instanceof Date)) ?
19200         date : date.dateFormat(fmt || this.format);
19201     },
19202     
19203     onFocus : function()
19204     {
19205         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19206         this.showPopup();
19207     },
19208     
19209     onBlur : function()
19210     {
19211         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19212         
19213         var d = this.inputEl().getValue();
19214         
19215         this.setValue(d);
19216                 
19217         this.hidePopup();
19218     },
19219     
19220     showPopup : function()
19221     {
19222         this.picker().show();
19223         this.update();
19224         this.place();
19225         
19226         this.fireEvent('showpopup', this, this.date);
19227     },
19228     
19229     hidePopup : function()
19230     {
19231         if(this.isInline) {
19232             return;
19233         }
19234         this.picker().hide();
19235         this.viewMode = this.startViewMode;
19236         this.showMode();
19237         
19238         this.fireEvent('hidepopup', this, this.date);
19239         
19240     },
19241     
19242     onMousedown: function(e)
19243     {
19244         e.stopPropagation();
19245         e.preventDefault();
19246     },
19247     
19248     keyup: function(e)
19249     {
19250         Roo.bootstrap.DateField.superclass.keyup.call(this);
19251         this.update();
19252     },
19253
19254     setValue: function(v)
19255     {
19256         if(this.fireEvent('beforeselect', this, v) !== false){
19257             var d = new Date(this.parseDate(v) ).clearTime();
19258         
19259             if(isNaN(d.getTime())){
19260                 this.date = this.viewDate = '';
19261                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19262                 return;
19263             }
19264
19265             v = this.formatDate(d);
19266
19267             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19268
19269             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19270
19271             this.update();
19272
19273             this.fireEvent('select', this, this.date);
19274         }
19275     },
19276     
19277     getValue: function()
19278     {
19279         return this.formatDate(this.date);
19280     },
19281     
19282     fireKey: function(e)
19283     {
19284         if (!this.picker().isVisible()){
19285             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19286                 this.showPopup();
19287             }
19288             return;
19289         }
19290         
19291         var dateChanged = false,
19292         dir, day, month,
19293         newDate, newViewDate;
19294         
19295         switch(e.keyCode){
19296             case 27: // escape
19297                 this.hidePopup();
19298                 e.preventDefault();
19299                 break;
19300             case 37: // left
19301             case 39: // right
19302                 if (!this.keyboardNavigation) {
19303                     break;
19304                 }
19305                 dir = e.keyCode == 37 ? -1 : 1;
19306                 
19307                 if (e.ctrlKey){
19308                     newDate = this.moveYear(this.date, dir);
19309                     newViewDate = this.moveYear(this.viewDate, dir);
19310                 } else if (e.shiftKey){
19311                     newDate = this.moveMonth(this.date, dir);
19312                     newViewDate = this.moveMonth(this.viewDate, dir);
19313                 } else {
19314                     newDate = new Date(this.date);
19315                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19316                     newViewDate = new Date(this.viewDate);
19317                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19318                 }
19319                 if (this.dateWithinRange(newDate)){
19320                     this.date = newDate;
19321                     this.viewDate = newViewDate;
19322                     this.setValue(this.formatDate(this.date));
19323 //                    this.update();
19324                     e.preventDefault();
19325                     dateChanged = true;
19326                 }
19327                 break;
19328             case 38: // up
19329             case 40: // down
19330                 if (!this.keyboardNavigation) {
19331                     break;
19332                 }
19333                 dir = e.keyCode == 38 ? -1 : 1;
19334                 if (e.ctrlKey){
19335                     newDate = this.moveYear(this.date, dir);
19336                     newViewDate = this.moveYear(this.viewDate, dir);
19337                 } else if (e.shiftKey){
19338                     newDate = this.moveMonth(this.date, dir);
19339                     newViewDate = this.moveMonth(this.viewDate, dir);
19340                 } else {
19341                     newDate = new Date(this.date);
19342                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19343                     newViewDate = new Date(this.viewDate);
19344                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19345                 }
19346                 if (this.dateWithinRange(newDate)){
19347                     this.date = newDate;
19348                     this.viewDate = newViewDate;
19349                     this.setValue(this.formatDate(this.date));
19350 //                    this.update();
19351                     e.preventDefault();
19352                     dateChanged = true;
19353                 }
19354                 break;
19355             case 13: // enter
19356                 this.setValue(this.formatDate(this.date));
19357                 this.hidePopup();
19358                 e.preventDefault();
19359                 break;
19360             case 9: // tab
19361                 this.setValue(this.formatDate(this.date));
19362                 this.hidePopup();
19363                 break;
19364             case 16: // shift
19365             case 17: // ctrl
19366             case 18: // alt
19367                 break;
19368             default :
19369                 this.hidePopup();
19370                 
19371         }
19372     },
19373     
19374     
19375     onClick: function(e) 
19376     {
19377         e.stopPropagation();
19378         e.preventDefault();
19379         
19380         var target = e.getTarget();
19381         
19382         if(target.nodeName.toLowerCase() === 'i'){
19383             target = Roo.get(target).dom.parentNode;
19384         }
19385         
19386         var nodeName = target.nodeName;
19387         var className = target.className;
19388         var html = target.innerHTML;
19389         //Roo.log(nodeName);
19390         
19391         switch(nodeName.toLowerCase()) {
19392             case 'th':
19393                 switch(className) {
19394                     case 'switch':
19395                         this.showMode(1);
19396                         break;
19397                     case 'prev':
19398                     case 'next':
19399                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19400                         switch(this.viewMode){
19401                                 case 0:
19402                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19403                                         break;
19404                                 case 1:
19405                                 case 2:
19406                                         this.viewDate = this.moveYear(this.viewDate, dir);
19407                                         break;
19408                         }
19409                         this.fill();
19410                         break;
19411                     case 'today':
19412                         var date = new Date();
19413                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19414 //                        this.fill()
19415                         this.setValue(this.formatDate(this.date));
19416                         
19417                         this.hidePopup();
19418                         break;
19419                 }
19420                 break;
19421             case 'span':
19422                 if (className.indexOf('disabled') < 0) {
19423                     this.viewDate.setUTCDate(1);
19424                     if (className.indexOf('month') > -1) {
19425                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19426                     } else {
19427                         var year = parseInt(html, 10) || 0;
19428                         this.viewDate.setUTCFullYear(year);
19429                         
19430                     }
19431                     
19432                     if(this.singleMode){
19433                         this.setValue(this.formatDate(this.viewDate));
19434                         this.hidePopup();
19435                         return;
19436                     }
19437                     
19438                     this.showMode(-1);
19439                     this.fill();
19440                 }
19441                 break;
19442                 
19443             case 'td':
19444                 //Roo.log(className);
19445                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19446                     var day = parseInt(html, 10) || 1;
19447                     var year = this.viewDate.getUTCFullYear(),
19448                         month = this.viewDate.getUTCMonth();
19449
19450                     if (className.indexOf('old') > -1) {
19451                         if(month === 0 ){
19452                             month = 11;
19453                             year -= 1;
19454                         }else{
19455                             month -= 1;
19456                         }
19457                     } else if (className.indexOf('new') > -1) {
19458                         if (month == 11) {
19459                             month = 0;
19460                             year += 1;
19461                         } else {
19462                             month += 1;
19463                         }
19464                     }
19465                     //Roo.log([year,month,day]);
19466                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19467                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19468 //                    this.fill();
19469                     //Roo.log(this.formatDate(this.date));
19470                     this.setValue(this.formatDate(this.date));
19471                     this.hidePopup();
19472                 }
19473                 break;
19474         }
19475     },
19476     
19477     setStartDate: function(startDate)
19478     {
19479         this.startDate = startDate || -Infinity;
19480         if (this.startDate !== -Infinity) {
19481             this.startDate = this.parseDate(this.startDate);
19482         }
19483         this.update();
19484         this.updateNavArrows();
19485     },
19486
19487     setEndDate: function(endDate)
19488     {
19489         this.endDate = endDate || Infinity;
19490         if (this.endDate !== Infinity) {
19491             this.endDate = this.parseDate(this.endDate);
19492         }
19493         this.update();
19494         this.updateNavArrows();
19495     },
19496     
19497     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19498     {
19499         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19500         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19501             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19502         }
19503         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19504             return parseInt(d, 10);
19505         });
19506         this.update();
19507         this.updateNavArrows();
19508     },
19509     
19510     updateNavArrows: function() 
19511     {
19512         if(this.singleMode){
19513             return;
19514         }
19515         
19516         var d = new Date(this.viewDate),
19517         year = d.getUTCFullYear(),
19518         month = d.getUTCMonth();
19519         
19520         Roo.each(this.picker().select('.prev', true).elements, function(v){
19521             v.show();
19522             switch (this.viewMode) {
19523                 case 0:
19524
19525                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19526                         v.hide();
19527                     }
19528                     break;
19529                 case 1:
19530                 case 2:
19531                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19532                         v.hide();
19533                     }
19534                     break;
19535             }
19536         });
19537         
19538         Roo.each(this.picker().select('.next', true).elements, function(v){
19539             v.show();
19540             switch (this.viewMode) {
19541                 case 0:
19542
19543                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19544                         v.hide();
19545                     }
19546                     break;
19547                 case 1:
19548                 case 2:
19549                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19550                         v.hide();
19551                     }
19552                     break;
19553             }
19554         })
19555     },
19556     
19557     moveMonth: function(date, dir)
19558     {
19559         if (!dir) {
19560             return date;
19561         }
19562         var new_date = new Date(date.valueOf()),
19563         day = new_date.getUTCDate(),
19564         month = new_date.getUTCMonth(),
19565         mag = Math.abs(dir),
19566         new_month, test;
19567         dir = dir > 0 ? 1 : -1;
19568         if (mag == 1){
19569             test = dir == -1
19570             // If going back one month, make sure month is not current month
19571             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19572             ? function(){
19573                 return new_date.getUTCMonth() == month;
19574             }
19575             // If going forward one month, make sure month is as expected
19576             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19577             : function(){
19578                 return new_date.getUTCMonth() != new_month;
19579             };
19580             new_month = month + dir;
19581             new_date.setUTCMonth(new_month);
19582             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19583             if (new_month < 0 || new_month > 11) {
19584                 new_month = (new_month + 12) % 12;
19585             }
19586         } else {
19587             // For magnitudes >1, move one month at a time...
19588             for (var i=0; i<mag; i++) {
19589                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19590                 new_date = this.moveMonth(new_date, dir);
19591             }
19592             // ...then reset the day, keeping it in the new month
19593             new_month = new_date.getUTCMonth();
19594             new_date.setUTCDate(day);
19595             test = function(){
19596                 return new_month != new_date.getUTCMonth();
19597             };
19598         }
19599         // Common date-resetting loop -- if date is beyond end of month, make it
19600         // end of month
19601         while (test()){
19602             new_date.setUTCDate(--day);
19603             new_date.setUTCMonth(new_month);
19604         }
19605         return new_date;
19606     },
19607
19608     moveYear: function(date, dir)
19609     {
19610         return this.moveMonth(date, dir*12);
19611     },
19612
19613     dateWithinRange: function(date)
19614     {
19615         return date >= this.startDate && date <= this.endDate;
19616     },
19617
19618     
19619     remove: function() 
19620     {
19621         this.picker().remove();
19622     },
19623     
19624     validateValue : function(value)
19625     {
19626         if(this.getVisibilityEl().hasClass('hidden')){
19627             return true;
19628         }
19629         
19630         if(value.length < 1)  {
19631             if(this.allowBlank){
19632                 return true;
19633             }
19634             return false;
19635         }
19636         
19637         if(value.length < this.minLength){
19638             return false;
19639         }
19640         if(value.length > this.maxLength){
19641             return false;
19642         }
19643         if(this.vtype){
19644             var vt = Roo.form.VTypes;
19645             if(!vt[this.vtype](value, this)){
19646                 return false;
19647             }
19648         }
19649         if(typeof this.validator == "function"){
19650             var msg = this.validator(value);
19651             if(msg !== true){
19652                 return false;
19653             }
19654         }
19655         
19656         if(this.regex && !this.regex.test(value)){
19657             return false;
19658         }
19659         
19660         if(typeof(this.parseDate(value)) == 'undefined'){
19661             return false;
19662         }
19663         
19664         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19665             return false;
19666         }      
19667         
19668         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19669             return false;
19670         } 
19671         
19672         
19673         return true;
19674     },
19675     
19676     reset : function()
19677     {
19678         this.date = this.viewDate = '';
19679         
19680         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19681     }
19682    
19683 });
19684
19685 Roo.apply(Roo.bootstrap.DateField,  {
19686     
19687     head : {
19688         tag: 'thead',
19689         cn: [
19690         {
19691             tag: 'tr',
19692             cn: [
19693             {
19694                 tag: 'th',
19695                 cls: 'prev',
19696                 html: '<i class="fa fa-arrow-left"/>'
19697             },
19698             {
19699                 tag: 'th',
19700                 cls: 'switch',
19701                 colspan: '5'
19702             },
19703             {
19704                 tag: 'th',
19705                 cls: 'next',
19706                 html: '<i class="fa fa-arrow-right"/>'
19707             }
19708
19709             ]
19710         }
19711         ]
19712     },
19713     
19714     content : {
19715         tag: 'tbody',
19716         cn: [
19717         {
19718             tag: 'tr',
19719             cn: [
19720             {
19721                 tag: 'td',
19722                 colspan: '7'
19723             }
19724             ]
19725         }
19726         ]
19727     },
19728     
19729     footer : {
19730         tag: 'tfoot',
19731         cn: [
19732         {
19733             tag: 'tr',
19734             cn: [
19735             {
19736                 tag: 'th',
19737                 colspan: '7',
19738                 cls: 'today'
19739             }
19740                     
19741             ]
19742         }
19743         ]
19744     },
19745     
19746     dates:{
19747         en: {
19748             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19749             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19750             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19751             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19752             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19753             today: "Today"
19754         }
19755     },
19756     
19757     modes: [
19758     {
19759         clsName: 'days',
19760         navFnc: 'Month',
19761         navStep: 1
19762     },
19763     {
19764         clsName: 'months',
19765         navFnc: 'FullYear',
19766         navStep: 1
19767     },
19768     {
19769         clsName: 'years',
19770         navFnc: 'FullYear',
19771         navStep: 10
19772     }]
19773 });
19774
19775 Roo.apply(Roo.bootstrap.DateField,  {
19776   
19777     template : {
19778         tag: 'div',
19779         cls: 'datepicker dropdown-menu roo-dynamic',
19780         cn: [
19781         {
19782             tag: 'div',
19783             cls: 'datepicker-days',
19784             cn: [
19785             {
19786                 tag: 'table',
19787                 cls: 'table-condensed',
19788                 cn:[
19789                 Roo.bootstrap.DateField.head,
19790                 {
19791                     tag: 'tbody'
19792                 },
19793                 Roo.bootstrap.DateField.footer
19794                 ]
19795             }
19796             ]
19797         },
19798         {
19799             tag: 'div',
19800             cls: 'datepicker-months',
19801             cn: [
19802             {
19803                 tag: 'table',
19804                 cls: 'table-condensed',
19805                 cn:[
19806                 Roo.bootstrap.DateField.head,
19807                 Roo.bootstrap.DateField.content,
19808                 Roo.bootstrap.DateField.footer
19809                 ]
19810             }
19811             ]
19812         },
19813         {
19814             tag: 'div',
19815             cls: 'datepicker-years',
19816             cn: [
19817             {
19818                 tag: 'table',
19819                 cls: 'table-condensed',
19820                 cn:[
19821                 Roo.bootstrap.DateField.head,
19822                 Roo.bootstrap.DateField.content,
19823                 Roo.bootstrap.DateField.footer
19824                 ]
19825             }
19826             ]
19827         }
19828         ]
19829     }
19830 });
19831
19832  
19833
19834  /*
19835  * - LGPL
19836  *
19837  * TimeField
19838  * 
19839  */
19840
19841 /**
19842  * @class Roo.bootstrap.TimeField
19843  * @extends Roo.bootstrap.Input
19844  * Bootstrap DateField class
19845  * 
19846  * 
19847  * @constructor
19848  * Create a new TimeField
19849  * @param {Object} config The config object
19850  */
19851
19852 Roo.bootstrap.TimeField = function(config){
19853     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19854     this.addEvents({
19855             /**
19856              * @event show
19857              * Fires when this field show.
19858              * @param {Roo.bootstrap.DateField} thisthis
19859              * @param {Mixed} date The date value
19860              */
19861             show : true,
19862             /**
19863              * @event show
19864              * Fires when this field hide.
19865              * @param {Roo.bootstrap.DateField} this
19866              * @param {Mixed} date The date value
19867              */
19868             hide : true,
19869             /**
19870              * @event select
19871              * Fires when select a date.
19872              * @param {Roo.bootstrap.DateField} this
19873              * @param {Mixed} date The date value
19874              */
19875             select : true
19876         });
19877 };
19878
19879 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19880     
19881     /**
19882      * @cfg {String} format
19883      * The default time format string which can be overriden for localization support.  The format must be
19884      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19885      */
19886     format : "H:i",
19887        
19888     onRender: function(ct, position)
19889     {
19890         
19891         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19892                 
19893         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19894         
19895         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19896         
19897         this.pop = this.picker().select('>.datepicker-time',true).first();
19898         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19899         
19900         this.picker().on('mousedown', this.onMousedown, this);
19901         this.picker().on('click', this.onClick, this);
19902         
19903         this.picker().addClass('datepicker-dropdown');
19904     
19905         this.fillTime();
19906         this.update();
19907             
19908         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19909         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19910         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19911         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19912         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19913         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19914
19915     },
19916     
19917     fireKey: function(e){
19918         if (!this.picker().isVisible()){
19919             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19920                 this.show();
19921             }
19922             return;
19923         }
19924
19925         e.preventDefault();
19926         
19927         switch(e.keyCode){
19928             case 27: // escape
19929                 this.hide();
19930                 break;
19931             case 37: // left
19932             case 39: // right
19933                 this.onTogglePeriod();
19934                 break;
19935             case 38: // up
19936                 this.onIncrementMinutes();
19937                 break;
19938             case 40: // down
19939                 this.onDecrementMinutes();
19940                 break;
19941             case 13: // enter
19942             case 9: // tab
19943                 this.setTime();
19944                 break;
19945         }
19946     },
19947     
19948     onClick: function(e) {
19949         e.stopPropagation();
19950         e.preventDefault();
19951     },
19952     
19953     picker : function()
19954     {
19955         return this.el.select('.datepicker', true).first();
19956     },
19957     
19958     fillTime: function()
19959     {    
19960         var time = this.pop.select('tbody', true).first();
19961         
19962         time.dom.innerHTML = '';
19963         
19964         time.createChild({
19965             tag: 'tr',
19966             cn: [
19967                 {
19968                     tag: 'td',
19969                     cn: [
19970                         {
19971                             tag: 'a',
19972                             href: '#',
19973                             cls: 'btn',
19974                             cn: [
19975                                 {
19976                                     tag: 'span',
19977                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19978                                 }
19979                             ]
19980                         } 
19981                     ]
19982                 },
19983                 {
19984                     tag: 'td',
19985                     cls: 'separator'
19986                 },
19987                 {
19988                     tag: 'td',
19989                     cn: [
19990                         {
19991                             tag: 'a',
19992                             href: '#',
19993                             cls: 'btn',
19994                             cn: [
19995                                 {
19996                                     tag: 'span',
19997                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19998                                 }
19999                             ]
20000                         }
20001                     ]
20002                 },
20003                 {
20004                     tag: 'td',
20005                     cls: 'separator'
20006                 }
20007             ]
20008         });
20009         
20010         time.createChild({
20011             tag: 'tr',
20012             cn: [
20013                 {
20014                     tag: 'td',
20015                     cn: [
20016                         {
20017                             tag: 'span',
20018                             cls: 'timepicker-hour',
20019                             html: '00'
20020                         }  
20021                     ]
20022                 },
20023                 {
20024                     tag: 'td',
20025                     cls: 'separator',
20026                     html: ':'
20027                 },
20028                 {
20029                     tag: 'td',
20030                     cn: [
20031                         {
20032                             tag: 'span',
20033                             cls: 'timepicker-minute',
20034                             html: '00'
20035                         }  
20036                     ]
20037                 },
20038                 {
20039                     tag: 'td',
20040                     cls: 'separator'
20041                 },
20042                 {
20043                     tag: 'td',
20044                     cn: [
20045                         {
20046                             tag: 'button',
20047                             type: 'button',
20048                             cls: 'btn btn-primary period',
20049                             html: 'AM'
20050                             
20051                         }
20052                     ]
20053                 }
20054             ]
20055         });
20056         
20057         time.createChild({
20058             tag: 'tr',
20059             cn: [
20060                 {
20061                     tag: 'td',
20062                     cn: [
20063                         {
20064                             tag: 'a',
20065                             href: '#',
20066                             cls: 'btn',
20067                             cn: [
20068                                 {
20069                                     tag: 'span',
20070                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20071                                 }
20072                             ]
20073                         }
20074                     ]
20075                 },
20076                 {
20077                     tag: 'td',
20078                     cls: 'separator'
20079                 },
20080                 {
20081                     tag: 'td',
20082                     cn: [
20083                         {
20084                             tag: 'a',
20085                             href: '#',
20086                             cls: 'btn',
20087                             cn: [
20088                                 {
20089                                     tag: 'span',
20090                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20091                                 }
20092                             ]
20093                         }
20094                     ]
20095                 },
20096                 {
20097                     tag: 'td',
20098                     cls: 'separator'
20099                 }
20100             ]
20101         });
20102         
20103     },
20104     
20105     update: function()
20106     {
20107         
20108         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20109         
20110         this.fill();
20111     },
20112     
20113     fill: function() 
20114     {
20115         var hours = this.time.getHours();
20116         var minutes = this.time.getMinutes();
20117         var period = 'AM';
20118         
20119         if(hours > 11){
20120             period = 'PM';
20121         }
20122         
20123         if(hours == 0){
20124             hours = 12;
20125         }
20126         
20127         
20128         if(hours > 12){
20129             hours = hours - 12;
20130         }
20131         
20132         if(hours < 10){
20133             hours = '0' + hours;
20134         }
20135         
20136         if(minutes < 10){
20137             minutes = '0' + minutes;
20138         }
20139         
20140         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20141         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20142         this.pop.select('button', true).first().dom.innerHTML = period;
20143         
20144     },
20145     
20146     place: function()
20147     {   
20148         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20149         
20150         var cls = ['bottom'];
20151         
20152         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20153             cls.pop();
20154             cls.push('top');
20155         }
20156         
20157         cls.push('right');
20158         
20159         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20160             cls.pop();
20161             cls.push('left');
20162         }
20163         
20164         this.picker().addClass(cls.join('-'));
20165         
20166         var _this = this;
20167         
20168         Roo.each(cls, function(c){
20169             if(c == 'bottom'){
20170                 _this.picker().setTop(_this.inputEl().getHeight());
20171                 return;
20172             }
20173             if(c == 'top'){
20174                 _this.picker().setTop(0 - _this.picker().getHeight());
20175                 return;
20176             }
20177             
20178             if(c == 'left'){
20179                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20180                 return;
20181             }
20182             if(c == 'right'){
20183                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20184                 return;
20185             }
20186         });
20187         
20188     },
20189   
20190     onFocus : function()
20191     {
20192         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20193         this.show();
20194     },
20195     
20196     onBlur : function()
20197     {
20198         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20199         this.hide();
20200     },
20201     
20202     show : function()
20203     {
20204         this.picker().show();
20205         this.pop.show();
20206         this.update();
20207         this.place();
20208         
20209         this.fireEvent('show', this, this.date);
20210     },
20211     
20212     hide : function()
20213     {
20214         this.picker().hide();
20215         this.pop.hide();
20216         
20217         this.fireEvent('hide', this, this.date);
20218     },
20219     
20220     setTime : function()
20221     {
20222         this.hide();
20223         this.setValue(this.time.format(this.format));
20224         
20225         this.fireEvent('select', this, this.date);
20226         
20227         
20228     },
20229     
20230     onMousedown: function(e){
20231         e.stopPropagation();
20232         e.preventDefault();
20233     },
20234     
20235     onIncrementHours: function()
20236     {
20237         Roo.log('onIncrementHours');
20238         this.time = this.time.add(Date.HOUR, 1);
20239         this.update();
20240         
20241     },
20242     
20243     onDecrementHours: function()
20244     {
20245         Roo.log('onDecrementHours');
20246         this.time = this.time.add(Date.HOUR, -1);
20247         this.update();
20248     },
20249     
20250     onIncrementMinutes: function()
20251     {
20252         Roo.log('onIncrementMinutes');
20253         this.time = this.time.add(Date.MINUTE, 1);
20254         this.update();
20255     },
20256     
20257     onDecrementMinutes: function()
20258     {
20259         Roo.log('onDecrementMinutes');
20260         this.time = this.time.add(Date.MINUTE, -1);
20261         this.update();
20262     },
20263     
20264     onTogglePeriod: function()
20265     {
20266         Roo.log('onTogglePeriod');
20267         this.time = this.time.add(Date.HOUR, 12);
20268         this.update();
20269     }
20270     
20271    
20272 });
20273
20274 Roo.apply(Roo.bootstrap.TimeField,  {
20275     
20276     content : {
20277         tag: 'tbody',
20278         cn: [
20279             {
20280                 tag: 'tr',
20281                 cn: [
20282                 {
20283                     tag: 'td',
20284                     colspan: '7'
20285                 }
20286                 ]
20287             }
20288         ]
20289     },
20290     
20291     footer : {
20292         tag: 'tfoot',
20293         cn: [
20294             {
20295                 tag: 'tr',
20296                 cn: [
20297                 {
20298                     tag: 'th',
20299                     colspan: '7',
20300                     cls: '',
20301                     cn: [
20302                         {
20303                             tag: 'button',
20304                             cls: 'btn btn-info ok',
20305                             html: 'OK'
20306                         }
20307                     ]
20308                 }
20309
20310                 ]
20311             }
20312         ]
20313     }
20314 });
20315
20316 Roo.apply(Roo.bootstrap.TimeField,  {
20317   
20318     template : {
20319         tag: 'div',
20320         cls: 'datepicker dropdown-menu',
20321         cn: [
20322             {
20323                 tag: 'div',
20324                 cls: 'datepicker-time',
20325                 cn: [
20326                 {
20327                     tag: 'table',
20328                     cls: 'table-condensed',
20329                     cn:[
20330                     Roo.bootstrap.TimeField.content,
20331                     Roo.bootstrap.TimeField.footer
20332                     ]
20333                 }
20334                 ]
20335             }
20336         ]
20337     }
20338 });
20339
20340  
20341
20342  /*
20343  * - LGPL
20344  *
20345  * MonthField
20346  * 
20347  */
20348
20349 /**
20350  * @class Roo.bootstrap.MonthField
20351  * @extends Roo.bootstrap.Input
20352  * Bootstrap MonthField class
20353  * 
20354  * @cfg {String} language default en
20355  * 
20356  * @constructor
20357  * Create a new MonthField
20358  * @param {Object} config The config object
20359  */
20360
20361 Roo.bootstrap.MonthField = function(config){
20362     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20363     
20364     this.addEvents({
20365         /**
20366          * @event show
20367          * Fires when this field show.
20368          * @param {Roo.bootstrap.MonthField} this
20369          * @param {Mixed} date The date value
20370          */
20371         show : true,
20372         /**
20373          * @event show
20374          * Fires when this field hide.
20375          * @param {Roo.bootstrap.MonthField} this
20376          * @param {Mixed} date The date value
20377          */
20378         hide : true,
20379         /**
20380          * @event select
20381          * Fires when select a date.
20382          * @param {Roo.bootstrap.MonthField} this
20383          * @param {String} oldvalue The old value
20384          * @param {String} newvalue The new value
20385          */
20386         select : true
20387     });
20388 };
20389
20390 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20391     
20392     onRender: function(ct, position)
20393     {
20394         
20395         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20396         
20397         this.language = this.language || 'en';
20398         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20399         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20400         
20401         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20402         this.isInline = false;
20403         this.isInput = true;
20404         this.component = this.el.select('.add-on', true).first() || false;
20405         this.component = (this.component && this.component.length === 0) ? false : this.component;
20406         this.hasInput = this.component && this.inputEL().length;
20407         
20408         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20409         
20410         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20411         
20412         this.picker().on('mousedown', this.onMousedown, this);
20413         this.picker().on('click', this.onClick, this);
20414         
20415         this.picker().addClass('datepicker-dropdown');
20416         
20417         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20418             v.setStyle('width', '189px');
20419         });
20420         
20421         this.fillMonths();
20422         
20423         this.update();
20424         
20425         if(this.isInline) {
20426             this.show();
20427         }
20428         
20429     },
20430     
20431     setValue: function(v, suppressEvent)
20432     {   
20433         var o = this.getValue();
20434         
20435         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20436         
20437         this.update();
20438
20439         if(suppressEvent !== true){
20440             this.fireEvent('select', this, o, v);
20441         }
20442         
20443     },
20444     
20445     getValue: function()
20446     {
20447         return this.value;
20448     },
20449     
20450     onClick: function(e) 
20451     {
20452         e.stopPropagation();
20453         e.preventDefault();
20454         
20455         var target = e.getTarget();
20456         
20457         if(target.nodeName.toLowerCase() === 'i'){
20458             target = Roo.get(target).dom.parentNode;
20459         }
20460         
20461         var nodeName = target.nodeName;
20462         var className = target.className;
20463         var html = target.innerHTML;
20464         
20465         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20466             return;
20467         }
20468         
20469         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20470         
20471         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20472         
20473         this.hide();
20474                         
20475     },
20476     
20477     picker : function()
20478     {
20479         return this.pickerEl;
20480     },
20481     
20482     fillMonths: function()
20483     {    
20484         var i = 0;
20485         var months = this.picker().select('>.datepicker-months td', true).first();
20486         
20487         months.dom.innerHTML = '';
20488         
20489         while (i < 12) {
20490             var month = {
20491                 tag: 'span',
20492                 cls: 'month',
20493                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20494             };
20495             
20496             months.createChild(month);
20497         }
20498         
20499     },
20500     
20501     update: function()
20502     {
20503         var _this = this;
20504         
20505         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20506             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20507         }
20508         
20509         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20510             e.removeClass('active');
20511             
20512             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20513                 e.addClass('active');
20514             }
20515         })
20516     },
20517     
20518     place: function()
20519     {
20520         if(this.isInline) {
20521             return;
20522         }
20523         
20524         this.picker().removeClass(['bottom', 'top']);
20525         
20526         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20527             /*
20528              * place to the top of element!
20529              *
20530              */
20531             
20532             this.picker().addClass('top');
20533             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20534             
20535             return;
20536         }
20537         
20538         this.picker().addClass('bottom');
20539         
20540         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20541     },
20542     
20543     onFocus : function()
20544     {
20545         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20546         this.show();
20547     },
20548     
20549     onBlur : function()
20550     {
20551         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20552         
20553         var d = this.inputEl().getValue();
20554         
20555         this.setValue(d);
20556                 
20557         this.hide();
20558     },
20559     
20560     show : function()
20561     {
20562         this.picker().show();
20563         this.picker().select('>.datepicker-months', true).first().show();
20564         this.update();
20565         this.place();
20566         
20567         this.fireEvent('show', this, this.date);
20568     },
20569     
20570     hide : function()
20571     {
20572         if(this.isInline) {
20573             return;
20574         }
20575         this.picker().hide();
20576         this.fireEvent('hide', this, this.date);
20577         
20578     },
20579     
20580     onMousedown: function(e)
20581     {
20582         e.stopPropagation();
20583         e.preventDefault();
20584     },
20585     
20586     keyup: function(e)
20587     {
20588         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20589         this.update();
20590     },
20591
20592     fireKey: function(e)
20593     {
20594         if (!this.picker().isVisible()){
20595             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20596                 this.show();
20597             }
20598             return;
20599         }
20600         
20601         var dir;
20602         
20603         switch(e.keyCode){
20604             case 27: // escape
20605                 this.hide();
20606                 e.preventDefault();
20607                 break;
20608             case 37: // left
20609             case 39: // right
20610                 dir = e.keyCode == 37 ? -1 : 1;
20611                 
20612                 this.vIndex = this.vIndex + dir;
20613                 
20614                 if(this.vIndex < 0){
20615                     this.vIndex = 0;
20616                 }
20617                 
20618                 if(this.vIndex > 11){
20619                     this.vIndex = 11;
20620                 }
20621                 
20622                 if(isNaN(this.vIndex)){
20623                     this.vIndex = 0;
20624                 }
20625                 
20626                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20627                 
20628                 break;
20629             case 38: // up
20630             case 40: // down
20631                 
20632                 dir = e.keyCode == 38 ? -1 : 1;
20633                 
20634                 this.vIndex = this.vIndex + dir * 4;
20635                 
20636                 if(this.vIndex < 0){
20637                     this.vIndex = 0;
20638                 }
20639                 
20640                 if(this.vIndex > 11){
20641                     this.vIndex = 11;
20642                 }
20643                 
20644                 if(isNaN(this.vIndex)){
20645                     this.vIndex = 0;
20646                 }
20647                 
20648                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20649                 break;
20650                 
20651             case 13: // enter
20652                 
20653                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20654                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20655                 }
20656                 
20657                 this.hide();
20658                 e.preventDefault();
20659                 break;
20660             case 9: // tab
20661                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20662                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20663                 }
20664                 this.hide();
20665                 break;
20666             case 16: // shift
20667             case 17: // ctrl
20668             case 18: // alt
20669                 break;
20670             default :
20671                 this.hide();
20672                 
20673         }
20674     },
20675     
20676     remove: function() 
20677     {
20678         this.picker().remove();
20679     }
20680    
20681 });
20682
20683 Roo.apply(Roo.bootstrap.MonthField,  {
20684     
20685     content : {
20686         tag: 'tbody',
20687         cn: [
20688         {
20689             tag: 'tr',
20690             cn: [
20691             {
20692                 tag: 'td',
20693                 colspan: '7'
20694             }
20695             ]
20696         }
20697         ]
20698     },
20699     
20700     dates:{
20701         en: {
20702             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20703             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20704         }
20705     }
20706 });
20707
20708 Roo.apply(Roo.bootstrap.MonthField,  {
20709   
20710     template : {
20711         tag: 'div',
20712         cls: 'datepicker dropdown-menu roo-dynamic',
20713         cn: [
20714             {
20715                 tag: 'div',
20716                 cls: 'datepicker-months',
20717                 cn: [
20718                 {
20719                     tag: 'table',
20720                     cls: 'table-condensed',
20721                     cn:[
20722                         Roo.bootstrap.DateField.content
20723                     ]
20724                 }
20725                 ]
20726             }
20727         ]
20728     }
20729 });
20730
20731  
20732
20733  
20734  /*
20735  * - LGPL
20736  *
20737  * CheckBox
20738  * 
20739  */
20740
20741 /**
20742  * @class Roo.bootstrap.CheckBox
20743  * @extends Roo.bootstrap.Input
20744  * Bootstrap CheckBox class
20745  * 
20746  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20747  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20748  * @cfg {String} boxLabel The text that appears beside the checkbox
20749  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20750  * @cfg {Boolean} checked initnal the element
20751  * @cfg {Boolean} inline inline the element (default false)
20752  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20753  * @cfg {String} tooltip label tooltip
20754  * 
20755  * @constructor
20756  * Create a new CheckBox
20757  * @param {Object} config The config object
20758  */
20759
20760 Roo.bootstrap.CheckBox = function(config){
20761     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20762    
20763     this.addEvents({
20764         /**
20765         * @event check
20766         * Fires when the element is checked or unchecked.
20767         * @param {Roo.bootstrap.CheckBox} this This input
20768         * @param {Boolean} checked The new checked value
20769         */
20770        check : true,
20771        /**
20772         * @event click
20773         * Fires when the element is click.
20774         * @param {Roo.bootstrap.CheckBox} this This input
20775         */
20776        click : true
20777     });
20778     
20779 };
20780
20781 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20782   
20783     inputType: 'checkbox',
20784     inputValue: 1,
20785     valueOff: 0,
20786     boxLabel: false,
20787     checked: false,
20788     weight : false,
20789     inline: false,
20790     tooltip : '',
20791     
20792     getAutoCreate : function()
20793     {
20794         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20795         
20796         var id = Roo.id();
20797         
20798         var cfg = {};
20799         
20800         cfg.cls = 'form-group ' + this.inputType; //input-group
20801         
20802         if(this.inline){
20803             cfg.cls += ' ' + this.inputType + '-inline';
20804         }
20805         
20806         var input =  {
20807             tag: 'input',
20808             id : id,
20809             type : this.inputType,
20810             value : this.inputValue,
20811             cls : 'roo-' + this.inputType, //'form-box',
20812             placeholder : this.placeholder || ''
20813             
20814         };
20815         
20816         if(this.inputType != 'radio'){
20817             var hidden =  {
20818                 tag: 'input',
20819                 type : 'hidden',
20820                 cls : 'roo-hidden-value',
20821                 value : this.checked ? this.inputValue : this.valueOff
20822             };
20823         }
20824         
20825             
20826         if (this.weight) { // Validity check?
20827             cfg.cls += " " + this.inputType + "-" + this.weight;
20828         }
20829         
20830         if (this.disabled) {
20831             input.disabled=true;
20832         }
20833         
20834         if(this.checked){
20835             input.checked = this.checked;
20836         }
20837         
20838         if (this.name) {
20839             
20840             input.name = this.name;
20841             
20842             if(this.inputType != 'radio'){
20843                 hidden.name = this.name;
20844                 input.name = '_hidden_' + this.name;
20845             }
20846         }
20847         
20848         if (this.size) {
20849             input.cls += ' input-' + this.size;
20850         }
20851         
20852         var settings=this;
20853         
20854         ['xs','sm','md','lg'].map(function(size){
20855             if (settings[size]) {
20856                 cfg.cls += ' col-' + size + '-' + settings[size];
20857             }
20858         });
20859         
20860         var inputblock = input;
20861          
20862         if (this.before || this.after) {
20863             
20864             inputblock = {
20865                 cls : 'input-group',
20866                 cn :  [] 
20867             };
20868             
20869             if (this.before) {
20870                 inputblock.cn.push({
20871                     tag :'span',
20872                     cls : 'input-group-addon',
20873                     html : this.before
20874                 });
20875             }
20876             
20877             inputblock.cn.push(input);
20878             
20879             if(this.inputType != 'radio'){
20880                 inputblock.cn.push(hidden);
20881             }
20882             
20883             if (this.after) {
20884                 inputblock.cn.push({
20885                     tag :'span',
20886                     cls : 'input-group-addon',
20887                     html : this.after
20888                 });
20889             }
20890             
20891         }
20892         
20893         if (align ==='left' && this.fieldLabel.length) {
20894 //                Roo.log("left and has label");
20895             cfg.cn = [
20896                 {
20897                     tag: 'label',
20898                     'for' :  id,
20899                     cls : 'control-label',
20900                     html : this.fieldLabel
20901                 },
20902                 {
20903                     cls : "", 
20904                     cn: [
20905                         inputblock
20906                     ]
20907                 }
20908             ];
20909             
20910             if(this.labelWidth > 12){
20911                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20912             }
20913             
20914             if(this.labelWidth < 13 && this.labelmd == 0){
20915                 this.labelmd = this.labelWidth;
20916             }
20917             
20918             if(this.labellg > 0){
20919                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20920                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20921             }
20922             
20923             if(this.labelmd > 0){
20924                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20925                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20926             }
20927             
20928             if(this.labelsm > 0){
20929                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20930                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20931             }
20932             
20933             if(this.labelxs > 0){
20934                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20935                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20936             }
20937             
20938         } else if ( this.fieldLabel.length) {
20939 //                Roo.log(" label");
20940                 cfg.cn = [
20941                    
20942                     {
20943                         tag: this.boxLabel ? 'span' : 'label',
20944                         'for': id,
20945                         cls: 'control-label box-input-label',
20946                         //cls : 'input-group-addon',
20947                         html : this.fieldLabel
20948                     },
20949                     
20950                     inputblock
20951                     
20952                 ];
20953
20954         } else {
20955             
20956 //                Roo.log(" no label && no align");
20957                 cfg.cn = [  inputblock ] ;
20958                 
20959                 
20960         }
20961         
20962         if(this.boxLabel){
20963              var boxLabelCfg = {
20964                 tag: 'label',
20965                 //'for': id, // box label is handled by onclick - so no for...
20966                 cls: 'box-label',
20967                 html: this.boxLabel
20968             };
20969             
20970             if(this.tooltip){
20971                 boxLabelCfg.tooltip = this.tooltip;
20972             }
20973              
20974             cfg.cn.push(boxLabelCfg);
20975         }
20976         
20977         if(this.inputType != 'radio'){
20978             cfg.cn.push(hidden);
20979         }
20980         
20981         return cfg;
20982         
20983     },
20984     
20985     /**
20986      * return the real input element.
20987      */
20988     inputEl: function ()
20989     {
20990         return this.el.select('input.roo-' + this.inputType,true).first();
20991     },
20992     hiddenEl: function ()
20993     {
20994         return this.el.select('input.roo-hidden-value',true).first();
20995     },
20996     
20997     labelEl: function()
20998     {
20999         return this.el.select('label.control-label',true).first();
21000     },
21001     /* depricated... */
21002     
21003     label: function()
21004     {
21005         return this.labelEl();
21006     },
21007     
21008     boxLabelEl: function()
21009     {
21010         return this.el.select('label.box-label',true).first();
21011     },
21012     
21013     initEvents : function()
21014     {
21015 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21016         
21017         this.inputEl().on('click', this.onClick,  this);
21018         
21019         if (this.boxLabel) { 
21020             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21021         }
21022         
21023         this.startValue = this.getValue();
21024         
21025         if(this.groupId){
21026             Roo.bootstrap.CheckBox.register(this);
21027         }
21028     },
21029     
21030     onClick : function(e)
21031     {   
21032         if(this.fireEvent('click', this, e) !== false){
21033             this.setChecked(!this.checked);
21034         }
21035         
21036     },
21037     
21038     setChecked : function(state,suppressEvent)
21039     {
21040         this.startValue = this.getValue();
21041
21042         if(this.inputType == 'radio'){
21043             
21044             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21045                 e.dom.checked = false;
21046             });
21047             
21048             this.inputEl().dom.checked = true;
21049             
21050             this.inputEl().dom.value = this.inputValue;
21051             
21052             if(suppressEvent !== true){
21053                 this.fireEvent('check', this, true);
21054             }
21055             
21056             this.validate();
21057             
21058             return;
21059         }
21060         
21061         this.checked = state;
21062         
21063         this.inputEl().dom.checked = state;
21064         
21065         
21066         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21067         
21068         if(suppressEvent !== true){
21069             this.fireEvent('check', this, state);
21070         }
21071         
21072         this.validate();
21073     },
21074     
21075     getValue : function()
21076     {
21077         if(this.inputType == 'radio'){
21078             return this.getGroupValue();
21079         }
21080         
21081         return this.hiddenEl().dom.value;
21082         
21083     },
21084     
21085     getGroupValue : function()
21086     {
21087         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21088             return '';
21089         }
21090         
21091         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21092     },
21093     
21094     setValue : function(v,suppressEvent)
21095     {
21096         if(this.inputType == 'radio'){
21097             this.setGroupValue(v, suppressEvent);
21098             return;
21099         }
21100         
21101         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21102         
21103         this.validate();
21104     },
21105     
21106     setGroupValue : function(v, suppressEvent)
21107     {
21108         this.startValue = this.getValue();
21109         
21110         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21111             e.dom.checked = false;
21112             
21113             if(e.dom.value == v){
21114                 e.dom.checked = true;
21115             }
21116         });
21117         
21118         if(suppressEvent !== true){
21119             this.fireEvent('check', this, true);
21120         }
21121
21122         this.validate();
21123         
21124         return;
21125     },
21126     
21127     validate : function()
21128     {
21129         if(this.getVisibilityEl().hasClass('hidden')){
21130             return true;
21131         }
21132         
21133         if(
21134                 this.disabled || 
21135                 (this.inputType == 'radio' && this.validateRadio()) ||
21136                 (this.inputType == 'checkbox' && this.validateCheckbox())
21137         ){
21138             this.markValid();
21139             return true;
21140         }
21141         
21142         this.markInvalid();
21143         return false;
21144     },
21145     
21146     validateRadio : function()
21147     {
21148         if(this.getVisibilityEl().hasClass('hidden')){
21149             return true;
21150         }
21151         
21152         if(this.allowBlank){
21153             return true;
21154         }
21155         
21156         var valid = false;
21157         
21158         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21159             if(!e.dom.checked){
21160                 return;
21161             }
21162             
21163             valid = true;
21164             
21165             return false;
21166         });
21167         
21168         return valid;
21169     },
21170     
21171     validateCheckbox : function()
21172     {
21173         if(!this.groupId){
21174             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21175             //return (this.getValue() == this.inputValue) ? true : false;
21176         }
21177         
21178         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21179         
21180         if(!group){
21181             return false;
21182         }
21183         
21184         var r = false;
21185         
21186         for(var i in group){
21187             if(group[i].el.isVisible(true)){
21188                 r = false;
21189                 break;
21190             }
21191             
21192             r = true;
21193         }
21194         
21195         for(var i in group){
21196             if(r){
21197                 break;
21198             }
21199             
21200             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21201         }
21202         
21203         return r;
21204     },
21205     
21206     /**
21207      * Mark this field as valid
21208      */
21209     markValid : function()
21210     {
21211         var _this = this;
21212         
21213         this.fireEvent('valid', this);
21214         
21215         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21216         
21217         if(this.groupId){
21218             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21219         }
21220         
21221         if(label){
21222             label.markValid();
21223         }
21224
21225         if(this.inputType == 'radio'){
21226             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21227                 var fg = e.findParent('.form-group', false, true);
21228                 if (Roo.bootstrap.version == 3) {
21229                     fg.removeClass([_this.invalidClass, _this.validClass]);
21230                     fg.addClass(_this.validClass);
21231                 } else {
21232                     fg.removeClass(['is-valid', 'is-invalid']);
21233                     fg.addClass('is-valid');
21234                 }
21235             });
21236             
21237             return;
21238         }
21239
21240         if(!this.groupId){
21241             var fg = this.el.findParent('.form-group', false, true);
21242             if (Roo.bootstrap.version == 3) {
21243                 fg.removeClass([this.invalidClass, this.validClass]);
21244                 fg.addClass(this.validClass);
21245             } else {
21246                 fg.removeClass(['is-valid', 'is-invalid']);
21247                 fg.addClass('is-valid');
21248             }
21249             return;
21250         }
21251         
21252         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21253         
21254         if(!group){
21255             return;
21256         }
21257         
21258         for(var i in group){
21259             var fg = group[i].el.findParent('.form-group', false, true);
21260             if (Roo.bootstrap.version == 3) {
21261                 fg.removeClass([this.invalidClass, this.validClass]);
21262                 fg.addClass(this.validClass);
21263             } else {
21264                 fg.removeClass(['is-valid', 'is-invalid']);
21265                 fg.addClass('is-valid');
21266             }
21267         }
21268     },
21269     
21270      /**
21271      * Mark this field as invalid
21272      * @param {String} msg The validation message
21273      */
21274     markInvalid : function(msg)
21275     {
21276         if(this.allowBlank){
21277             return;
21278         }
21279         
21280         var _this = this;
21281         
21282         this.fireEvent('invalid', this, msg);
21283         
21284         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21285         
21286         if(this.groupId){
21287             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21288         }
21289         
21290         if(label){
21291             label.markInvalid();
21292         }
21293             
21294         if(this.inputType == 'radio'){
21295             
21296             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21297                 var fg = e.findParent('.form-group', false, true);
21298                 if (Roo.bootstrap.version == 3) {
21299                     fg.removeClass([_this.invalidClass, _this.validClass]);
21300                     fg.addClass(_this.invalidClass);
21301                 } else {
21302                     fg.removeClass(['is-invalid', 'is-valid']);
21303                     fg.addClass('is-invalid');
21304                 }
21305             });
21306             
21307             return;
21308         }
21309         
21310         if(!this.groupId){
21311             var fg = this.el.findParent('.form-group', false, true);
21312             if (Roo.bootstrap.version == 3) {
21313                 fg.removeClass([_this.invalidClass, _this.validClass]);
21314                 fg.addClass(_this.invalidClass);
21315             } else {
21316                 fg.removeClass(['is-invalid', 'is-valid']);
21317                 fg.addClass('is-invalid');
21318             }
21319             return;
21320         }
21321         
21322         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21323         
21324         if(!group){
21325             return;
21326         }
21327         
21328         for(var i in group){
21329             var fg = group[i].el.findParent('.form-group', false, true);
21330             if (Roo.bootstrap.version == 3) {
21331                 fg.removeClass([_this.invalidClass, _this.validClass]);
21332                 fg.addClass(_this.invalidClass);
21333             } else {
21334                 fg.removeClass(['is-invalid', 'is-valid']);
21335                 fg.addClass('is-invalid');
21336             }
21337         }
21338         
21339     },
21340     
21341     clearInvalid : function()
21342     {
21343         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21344         
21345         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21346         
21347         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21348         
21349         if (label && label.iconEl) {
21350             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21351             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21352         }
21353     },
21354     
21355     disable : function()
21356     {
21357         if(this.inputType != 'radio'){
21358             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21359             return;
21360         }
21361         
21362         var _this = this;
21363         
21364         if(this.rendered){
21365             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21366                 _this.getActionEl().addClass(this.disabledClass);
21367                 e.dom.disabled = true;
21368             });
21369         }
21370         
21371         this.disabled = true;
21372         this.fireEvent("disable", this);
21373         return this;
21374     },
21375
21376     enable : function()
21377     {
21378         if(this.inputType != 'radio'){
21379             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21380             return;
21381         }
21382         
21383         var _this = this;
21384         
21385         if(this.rendered){
21386             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21387                 _this.getActionEl().removeClass(this.disabledClass);
21388                 e.dom.disabled = false;
21389             });
21390         }
21391         
21392         this.disabled = false;
21393         this.fireEvent("enable", this);
21394         return this;
21395     },
21396     
21397     setBoxLabel : function(v)
21398     {
21399         this.boxLabel = v;
21400         
21401         if(this.rendered){
21402             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21403         }
21404     }
21405
21406 });
21407
21408 Roo.apply(Roo.bootstrap.CheckBox, {
21409     
21410     groups: {},
21411     
21412      /**
21413     * register a CheckBox Group
21414     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21415     */
21416     register : function(checkbox)
21417     {
21418         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21419             this.groups[checkbox.groupId] = {};
21420         }
21421         
21422         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21423             return;
21424         }
21425         
21426         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21427         
21428     },
21429     /**
21430     * fetch a CheckBox Group based on the group ID
21431     * @param {string} the group ID
21432     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21433     */
21434     get: function(groupId) {
21435         if (typeof(this.groups[groupId]) == 'undefined') {
21436             return false;
21437         }
21438         
21439         return this.groups[groupId] ;
21440     }
21441     
21442     
21443 });
21444 /*
21445  * - LGPL
21446  *
21447  * RadioItem
21448  * 
21449  */
21450
21451 /**
21452  * @class Roo.bootstrap.Radio
21453  * @extends Roo.bootstrap.Component
21454  * Bootstrap Radio class
21455  * @cfg {String} boxLabel - the label associated
21456  * @cfg {String} value - the value of radio
21457  * 
21458  * @constructor
21459  * Create a new Radio
21460  * @param {Object} config The config object
21461  */
21462 Roo.bootstrap.Radio = function(config){
21463     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21464     
21465 };
21466
21467 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21468     
21469     boxLabel : '',
21470     
21471     value : '',
21472     
21473     getAutoCreate : function()
21474     {
21475         var cfg = {
21476             tag : 'div',
21477             cls : 'form-group radio',
21478             cn : [
21479                 {
21480                     tag : 'label',
21481                     cls : 'box-label',
21482                     html : this.boxLabel
21483                 }
21484             ]
21485         };
21486         
21487         return cfg;
21488     },
21489     
21490     initEvents : function() 
21491     {
21492         this.parent().register(this);
21493         
21494         this.el.on('click', this.onClick, this);
21495         
21496     },
21497     
21498     onClick : function(e)
21499     {
21500         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21501             this.setChecked(true);
21502         }
21503     },
21504     
21505     setChecked : function(state, suppressEvent)
21506     {
21507         this.parent().setValue(this.value, suppressEvent);
21508         
21509     },
21510     
21511     setBoxLabel : function(v)
21512     {
21513         this.boxLabel = v;
21514         
21515         if(this.rendered){
21516             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21517         }
21518     }
21519     
21520 });
21521  
21522
21523  /*
21524  * - LGPL
21525  *
21526  * Input
21527  * 
21528  */
21529
21530 /**
21531  * @class Roo.bootstrap.SecurePass
21532  * @extends Roo.bootstrap.Input
21533  * Bootstrap SecurePass class
21534  *
21535  * 
21536  * @constructor
21537  * Create a new SecurePass
21538  * @param {Object} config The config object
21539  */
21540  
21541 Roo.bootstrap.SecurePass = function (config) {
21542     // these go here, so the translation tool can replace them..
21543     this.errors = {
21544         PwdEmpty: "Please type a password, and then retype it to confirm.",
21545         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21546         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21547         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21548         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21549         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21550         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21551         TooWeak: "Your password is Too Weak."
21552     },
21553     this.meterLabel = "Password strength:";
21554     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21555     this.meterClass = [
21556         "roo-password-meter-tooweak", 
21557         "roo-password-meter-weak", 
21558         "roo-password-meter-medium", 
21559         "roo-password-meter-strong", 
21560         "roo-password-meter-grey"
21561     ];
21562     
21563     this.errors = {};
21564     
21565     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21566 }
21567
21568 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21569     /**
21570      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21571      * {
21572      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21573      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21574      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21575      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21576      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21577      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21578      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21579      * })
21580      */
21581     // private
21582     
21583     meterWidth: 300,
21584     errorMsg :'',    
21585     errors: false,
21586     imageRoot: '/',
21587     /**
21588      * @cfg {String/Object} Label for the strength meter (defaults to
21589      * 'Password strength:')
21590      */
21591     // private
21592     meterLabel: '',
21593     /**
21594      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21595      * ['Weak', 'Medium', 'Strong'])
21596      */
21597     // private    
21598     pwdStrengths: false,    
21599     // private
21600     strength: 0,
21601     // private
21602     _lastPwd: null,
21603     // private
21604     kCapitalLetter: 0,
21605     kSmallLetter: 1,
21606     kDigit: 2,
21607     kPunctuation: 3,
21608     
21609     insecure: false,
21610     // private
21611     initEvents: function ()
21612     {
21613         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21614
21615         if (this.el.is('input[type=password]') && Roo.isSafari) {
21616             this.el.on('keydown', this.SafariOnKeyDown, this);
21617         }
21618
21619         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21620     },
21621     // private
21622     onRender: function (ct, position)
21623     {
21624         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21625         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21626         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21627
21628         this.trigger.createChild({
21629                    cn: [
21630                     {
21631                     //id: 'PwdMeter',
21632                     tag: 'div',
21633                     cls: 'roo-password-meter-grey col-xs-12',
21634                     style: {
21635                         //width: 0,
21636                         //width: this.meterWidth + 'px'                                                
21637                         }
21638                     },
21639                     {                            
21640                          cls: 'roo-password-meter-text'                          
21641                     }
21642                 ]            
21643         });
21644
21645          
21646         if (this.hideTrigger) {
21647             this.trigger.setDisplayed(false);
21648         }
21649         this.setSize(this.width || '', this.height || '');
21650     },
21651     // private
21652     onDestroy: function ()
21653     {
21654         if (this.trigger) {
21655             this.trigger.removeAllListeners();
21656             this.trigger.remove();
21657         }
21658         if (this.wrap) {
21659             this.wrap.remove();
21660         }
21661         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21662     },
21663     // private
21664     checkStrength: function ()
21665     {
21666         var pwd = this.inputEl().getValue();
21667         if (pwd == this._lastPwd) {
21668             return;
21669         }
21670
21671         var strength;
21672         if (this.ClientSideStrongPassword(pwd)) {
21673             strength = 3;
21674         } else if (this.ClientSideMediumPassword(pwd)) {
21675             strength = 2;
21676         } else if (this.ClientSideWeakPassword(pwd)) {
21677             strength = 1;
21678         } else {
21679             strength = 0;
21680         }
21681         
21682         Roo.log('strength1: ' + strength);
21683         
21684         //var pm = this.trigger.child('div/div/div').dom;
21685         var pm = this.trigger.child('div/div');
21686         pm.removeClass(this.meterClass);
21687         pm.addClass(this.meterClass[strength]);
21688                 
21689         
21690         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21691                 
21692         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21693         
21694         this._lastPwd = pwd;
21695     },
21696     reset: function ()
21697     {
21698         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21699         
21700         this._lastPwd = '';
21701         
21702         var pm = this.trigger.child('div/div');
21703         pm.removeClass(this.meterClass);
21704         pm.addClass('roo-password-meter-grey');        
21705         
21706         
21707         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21708         
21709         pt.innerHTML = '';
21710         this.inputEl().dom.type='password';
21711     },
21712     // private
21713     validateValue: function (value)
21714     {
21715         
21716         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21717             return false;
21718         }
21719         if (value.length == 0) {
21720             if (this.allowBlank) {
21721                 this.clearInvalid();
21722                 return true;
21723             }
21724
21725             this.markInvalid(this.errors.PwdEmpty);
21726             this.errorMsg = this.errors.PwdEmpty;
21727             return false;
21728         }
21729         
21730         if(this.insecure){
21731             return true;
21732         }
21733         
21734         if ('[\x21-\x7e]*'.match(value)) {
21735             this.markInvalid(this.errors.PwdBadChar);
21736             this.errorMsg = this.errors.PwdBadChar;
21737             return false;
21738         }
21739         if (value.length < 6) {
21740             this.markInvalid(this.errors.PwdShort);
21741             this.errorMsg = this.errors.PwdShort;
21742             return false;
21743         }
21744         if (value.length > 16) {
21745             this.markInvalid(this.errors.PwdLong);
21746             this.errorMsg = this.errors.PwdLong;
21747             return false;
21748         }
21749         var strength;
21750         if (this.ClientSideStrongPassword(value)) {
21751             strength = 3;
21752         } else if (this.ClientSideMediumPassword(value)) {
21753             strength = 2;
21754         } else if (this.ClientSideWeakPassword(value)) {
21755             strength = 1;
21756         } else {
21757             strength = 0;
21758         }
21759
21760         
21761         if (strength < 2) {
21762             //this.markInvalid(this.errors.TooWeak);
21763             this.errorMsg = this.errors.TooWeak;
21764             //return false;
21765         }
21766         
21767         
21768         console.log('strength2: ' + strength);
21769         
21770         //var pm = this.trigger.child('div/div/div').dom;
21771         
21772         var pm = this.trigger.child('div/div');
21773         pm.removeClass(this.meterClass);
21774         pm.addClass(this.meterClass[strength]);
21775                 
21776         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21777                 
21778         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21779         
21780         this.errorMsg = ''; 
21781         return true;
21782     },
21783     // private
21784     CharacterSetChecks: function (type)
21785     {
21786         this.type = type;
21787         this.fResult = false;
21788     },
21789     // private
21790     isctype: function (character, type)
21791     {
21792         switch (type) {  
21793             case this.kCapitalLetter:
21794                 if (character >= 'A' && character <= 'Z') {
21795                     return true;
21796                 }
21797                 break;
21798             
21799             case this.kSmallLetter:
21800                 if (character >= 'a' && character <= 'z') {
21801                     return true;
21802                 }
21803                 break;
21804             
21805             case this.kDigit:
21806                 if (character >= '0' && character <= '9') {
21807                     return true;
21808                 }
21809                 break;
21810             
21811             case this.kPunctuation:
21812                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21813                     return true;
21814                 }
21815                 break;
21816             
21817             default:
21818                 return false;
21819         }
21820
21821     },
21822     // private
21823     IsLongEnough: function (pwd, size)
21824     {
21825         return !(pwd == null || isNaN(size) || pwd.length < size);
21826     },
21827     // private
21828     SpansEnoughCharacterSets: function (word, nb)
21829     {
21830         if (!this.IsLongEnough(word, nb))
21831         {
21832             return false;
21833         }
21834
21835         var characterSetChecks = new Array(
21836             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21837             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21838         );
21839         
21840         for (var index = 0; index < word.length; ++index) {
21841             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21842                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21843                     characterSetChecks[nCharSet].fResult = true;
21844                     break;
21845                 }
21846             }
21847         }
21848
21849         var nCharSets = 0;
21850         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21851             if (characterSetChecks[nCharSet].fResult) {
21852                 ++nCharSets;
21853             }
21854         }
21855
21856         if (nCharSets < nb) {
21857             return false;
21858         }
21859         return true;
21860     },
21861     // private
21862     ClientSideStrongPassword: function (pwd)
21863     {
21864         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21865     },
21866     // private
21867     ClientSideMediumPassword: function (pwd)
21868     {
21869         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21870     },
21871     // private
21872     ClientSideWeakPassword: function (pwd)
21873     {
21874         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21875     }
21876           
21877 })//<script type="text/javascript">
21878
21879 /*
21880  * Based  Ext JS Library 1.1.1
21881  * Copyright(c) 2006-2007, Ext JS, LLC.
21882  * LGPL
21883  *
21884  */
21885  
21886 /**
21887  * @class Roo.HtmlEditorCore
21888  * @extends Roo.Component
21889  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21890  *
21891  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21892  */
21893
21894 Roo.HtmlEditorCore = function(config){
21895     
21896     
21897     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21898     
21899     
21900     this.addEvents({
21901         /**
21902          * @event initialize
21903          * Fires when the editor is fully initialized (including the iframe)
21904          * @param {Roo.HtmlEditorCore} this
21905          */
21906         initialize: true,
21907         /**
21908          * @event activate
21909          * Fires when the editor is first receives the focus. Any insertion must wait
21910          * until after this event.
21911          * @param {Roo.HtmlEditorCore} this
21912          */
21913         activate: true,
21914          /**
21915          * @event beforesync
21916          * Fires before the textarea is updated with content from the editor iframe. Return false
21917          * to cancel the sync.
21918          * @param {Roo.HtmlEditorCore} this
21919          * @param {String} html
21920          */
21921         beforesync: true,
21922          /**
21923          * @event beforepush
21924          * Fires before the iframe editor is updated with content from the textarea. Return false
21925          * to cancel the push.
21926          * @param {Roo.HtmlEditorCore} this
21927          * @param {String} html
21928          */
21929         beforepush: true,
21930          /**
21931          * @event sync
21932          * Fires when the textarea is updated with content from the editor iframe.
21933          * @param {Roo.HtmlEditorCore} this
21934          * @param {String} html
21935          */
21936         sync: true,
21937          /**
21938          * @event push
21939          * Fires when the iframe editor is updated with content from the textarea.
21940          * @param {Roo.HtmlEditorCore} this
21941          * @param {String} html
21942          */
21943         push: true,
21944         
21945         /**
21946          * @event editorevent
21947          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21948          * @param {Roo.HtmlEditorCore} this
21949          */
21950         editorevent: true
21951         
21952     });
21953     
21954     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21955     
21956     // defaults : white / black...
21957     this.applyBlacklists();
21958     
21959     
21960     
21961 };
21962
21963
21964 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21965
21966
21967      /**
21968      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21969      */
21970     
21971     owner : false,
21972     
21973      /**
21974      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21975      *                        Roo.resizable.
21976      */
21977     resizable : false,
21978      /**
21979      * @cfg {Number} height (in pixels)
21980      */   
21981     height: 300,
21982    /**
21983      * @cfg {Number} width (in pixels)
21984      */   
21985     width: 500,
21986     
21987     /**
21988      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21989      * 
21990      */
21991     stylesheets: false,
21992     
21993     // id of frame..
21994     frameId: false,
21995     
21996     // private properties
21997     validationEvent : false,
21998     deferHeight: true,
21999     initialized : false,
22000     activated : false,
22001     sourceEditMode : false,
22002     onFocus : Roo.emptyFn,
22003     iframePad:3,
22004     hideMode:'offsets',
22005     
22006     clearUp: true,
22007     
22008     // blacklist + whitelisted elements..
22009     black: false,
22010     white: false,
22011      
22012     bodyCls : '',
22013
22014     /**
22015      * Protected method that will not generally be called directly. It
22016      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22017      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22018      */
22019     getDocMarkup : function(){
22020         // body styles..
22021         var st = '';
22022         
22023         // inherit styels from page...?? 
22024         if (this.stylesheets === false) {
22025             
22026             Roo.get(document.head).select('style').each(function(node) {
22027                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22028             });
22029             
22030             Roo.get(document.head).select('link').each(function(node) { 
22031                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22032             });
22033             
22034         } else if (!this.stylesheets.length) {
22035                 // simple..
22036                 st = '<style type="text/css">' +
22037                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22038                    '</style>';
22039         } else { 
22040             st = '<style type="text/css">' +
22041                     this.stylesheets +
22042                 '</style>';
22043         }
22044         
22045         st +=  '<style type="text/css">' +
22046             'IMG { cursor: pointer } ' +
22047         '</style>';
22048
22049         var cls = 'roo-htmleditor-body';
22050         
22051         if(this.bodyCls.length){
22052             cls += ' ' + this.bodyCls;
22053         }
22054         
22055         return '<html><head>' + st  +
22056             //<style type="text/css">' +
22057             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22058             //'</style>' +
22059             ' </head><body class="' +  cls + '"></body></html>';
22060     },
22061
22062     // private
22063     onRender : function(ct, position)
22064     {
22065         var _t = this;
22066         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22067         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22068         
22069         
22070         this.el.dom.style.border = '0 none';
22071         this.el.dom.setAttribute('tabIndex', -1);
22072         this.el.addClass('x-hidden hide');
22073         
22074         
22075         
22076         if(Roo.isIE){ // fix IE 1px bogus margin
22077             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22078         }
22079        
22080         
22081         this.frameId = Roo.id();
22082         
22083          
22084         
22085         var iframe = this.owner.wrap.createChild({
22086             tag: 'iframe',
22087             cls: 'form-control', // bootstrap..
22088             id: this.frameId,
22089             name: this.frameId,
22090             frameBorder : 'no',
22091             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22092         }, this.el
22093         );
22094         
22095         
22096         this.iframe = iframe.dom;
22097
22098          this.assignDocWin();
22099         
22100         this.doc.designMode = 'on';
22101        
22102         this.doc.open();
22103         this.doc.write(this.getDocMarkup());
22104         this.doc.close();
22105
22106         
22107         var task = { // must defer to wait for browser to be ready
22108             run : function(){
22109                 //console.log("run task?" + this.doc.readyState);
22110                 this.assignDocWin();
22111                 if(this.doc.body || this.doc.readyState == 'complete'){
22112                     try {
22113                         this.doc.designMode="on";
22114                     } catch (e) {
22115                         return;
22116                     }
22117                     Roo.TaskMgr.stop(task);
22118                     this.initEditor.defer(10, this);
22119                 }
22120             },
22121             interval : 10,
22122             duration: 10000,
22123             scope: this
22124         };
22125         Roo.TaskMgr.start(task);
22126
22127     },
22128
22129     // private
22130     onResize : function(w, h)
22131     {
22132          Roo.log('resize: ' +w + ',' + h );
22133         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22134         if(!this.iframe){
22135             return;
22136         }
22137         if(typeof w == 'number'){
22138             
22139             this.iframe.style.width = w + 'px';
22140         }
22141         if(typeof h == 'number'){
22142             
22143             this.iframe.style.height = h + 'px';
22144             if(this.doc){
22145                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22146             }
22147         }
22148         
22149     },
22150
22151     /**
22152      * Toggles the editor between standard and source edit mode.
22153      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22154      */
22155     toggleSourceEdit : function(sourceEditMode){
22156         
22157         this.sourceEditMode = sourceEditMode === true;
22158         
22159         if(this.sourceEditMode){
22160  
22161             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22162             
22163         }else{
22164             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22165             //this.iframe.className = '';
22166             this.deferFocus();
22167         }
22168         //this.setSize(this.owner.wrap.getSize());
22169         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22170     },
22171
22172     
22173   
22174
22175     /**
22176      * Protected method that will not generally be called directly. If you need/want
22177      * custom HTML cleanup, this is the method you should override.
22178      * @param {String} html The HTML to be cleaned
22179      * return {String} The cleaned HTML
22180      */
22181     cleanHtml : function(html){
22182         html = String(html);
22183         if(html.length > 5){
22184             if(Roo.isSafari){ // strip safari nonsense
22185                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22186             }
22187         }
22188         if(html == '&nbsp;'){
22189             html = '';
22190         }
22191         return html;
22192     },
22193
22194     /**
22195      * HTML Editor -> Textarea
22196      * Protected method that will not generally be called directly. Syncs the contents
22197      * of the editor iframe with the textarea.
22198      */
22199     syncValue : function(){
22200         if(this.initialized){
22201             var bd = (this.doc.body || this.doc.documentElement);
22202             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22203             var html = bd.innerHTML;
22204             if(Roo.isSafari){
22205                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22206                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22207                 if(m && m[1]){
22208                     html = '<div style="'+m[0]+'">' + html + '</div>';
22209                 }
22210             }
22211             html = this.cleanHtml(html);
22212             // fix up the special chars.. normaly like back quotes in word...
22213             // however we do not want to do this with chinese..
22214             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22215                 var cc = b.charCodeAt();
22216                 if (
22217                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22218                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22219                     (cc >= 0xf900 && cc < 0xfb00 )
22220                 ) {
22221                         return b;
22222                 }
22223                 return "&#"+cc+";" 
22224             });
22225             if(this.owner.fireEvent('beforesync', this, html) !== false){
22226                 this.el.dom.value = html;
22227                 this.owner.fireEvent('sync', this, html);
22228             }
22229         }
22230     },
22231
22232     /**
22233      * Protected method that will not generally be called directly. Pushes the value of the textarea
22234      * into the iframe editor.
22235      */
22236     pushValue : function(){
22237         if(this.initialized){
22238             var v = this.el.dom.value.trim();
22239             
22240 //            if(v.length < 1){
22241 //                v = '&#160;';
22242 //            }
22243             
22244             if(this.owner.fireEvent('beforepush', this, v) !== false){
22245                 var d = (this.doc.body || this.doc.documentElement);
22246                 d.innerHTML = v;
22247                 this.cleanUpPaste();
22248                 this.el.dom.value = d.innerHTML;
22249                 this.owner.fireEvent('push', this, v);
22250             }
22251         }
22252     },
22253
22254     // private
22255     deferFocus : function(){
22256         this.focus.defer(10, this);
22257     },
22258
22259     // doc'ed in Field
22260     focus : function(){
22261         if(this.win && !this.sourceEditMode){
22262             this.win.focus();
22263         }else{
22264             this.el.focus();
22265         }
22266     },
22267     
22268     assignDocWin: function()
22269     {
22270         var iframe = this.iframe;
22271         
22272          if(Roo.isIE){
22273             this.doc = iframe.contentWindow.document;
22274             this.win = iframe.contentWindow;
22275         } else {
22276 //            if (!Roo.get(this.frameId)) {
22277 //                return;
22278 //            }
22279 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22280 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22281             
22282             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22283                 return;
22284             }
22285             
22286             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22287             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22288         }
22289     },
22290     
22291     // private
22292     initEditor : function(){
22293         //console.log("INIT EDITOR");
22294         this.assignDocWin();
22295         
22296         
22297         
22298         this.doc.designMode="on";
22299         this.doc.open();
22300         this.doc.write(this.getDocMarkup());
22301         this.doc.close();
22302         
22303         var dbody = (this.doc.body || this.doc.documentElement);
22304         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22305         // this copies styles from the containing element into thsi one..
22306         // not sure why we need all of this..
22307         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22308         
22309         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22310         //ss['background-attachment'] = 'fixed'; // w3c
22311         dbody.bgProperties = 'fixed'; // ie
22312         //Roo.DomHelper.applyStyles(dbody, ss);
22313         Roo.EventManager.on(this.doc, {
22314             //'mousedown': this.onEditorEvent,
22315             'mouseup': this.onEditorEvent,
22316             'dblclick': this.onEditorEvent,
22317             'click': this.onEditorEvent,
22318             'keyup': this.onEditorEvent,
22319             buffer:100,
22320             scope: this
22321         });
22322         if(Roo.isGecko){
22323             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22324         }
22325         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22326             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22327         }
22328         this.initialized = true;
22329
22330         this.owner.fireEvent('initialize', this);
22331         this.pushValue();
22332     },
22333
22334     // private
22335     onDestroy : function(){
22336         
22337         
22338         
22339         if(this.rendered){
22340             
22341             //for (var i =0; i < this.toolbars.length;i++) {
22342             //    // fixme - ask toolbars for heights?
22343             //    this.toolbars[i].onDestroy();
22344            // }
22345             
22346             //this.wrap.dom.innerHTML = '';
22347             //this.wrap.remove();
22348         }
22349     },
22350
22351     // private
22352     onFirstFocus : function(){
22353         
22354         this.assignDocWin();
22355         
22356         
22357         this.activated = true;
22358          
22359     
22360         if(Roo.isGecko){ // prevent silly gecko errors
22361             this.win.focus();
22362             var s = this.win.getSelection();
22363             if(!s.focusNode || s.focusNode.nodeType != 3){
22364                 var r = s.getRangeAt(0);
22365                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22366                 r.collapse(true);
22367                 this.deferFocus();
22368             }
22369             try{
22370                 this.execCmd('useCSS', true);
22371                 this.execCmd('styleWithCSS', false);
22372             }catch(e){}
22373         }
22374         this.owner.fireEvent('activate', this);
22375     },
22376
22377     // private
22378     adjustFont: function(btn){
22379         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22380         //if(Roo.isSafari){ // safari
22381         //    adjust *= 2;
22382        // }
22383         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22384         if(Roo.isSafari){ // safari
22385             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22386             v =  (v < 10) ? 10 : v;
22387             v =  (v > 48) ? 48 : v;
22388             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22389             
22390         }
22391         
22392         
22393         v = Math.max(1, v+adjust);
22394         
22395         this.execCmd('FontSize', v  );
22396     },
22397
22398     onEditorEvent : function(e)
22399     {
22400         this.owner.fireEvent('editorevent', this, e);
22401       //  this.updateToolbar();
22402         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22403     },
22404
22405     insertTag : function(tg)
22406     {
22407         // could be a bit smarter... -> wrap the current selected tRoo..
22408         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22409             
22410             range = this.createRange(this.getSelection());
22411             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22412             wrappingNode.appendChild(range.extractContents());
22413             range.insertNode(wrappingNode);
22414
22415             return;
22416             
22417             
22418             
22419         }
22420         this.execCmd("formatblock",   tg);
22421         
22422     },
22423     
22424     insertText : function(txt)
22425     {
22426         
22427         
22428         var range = this.createRange();
22429         range.deleteContents();
22430                //alert(Sender.getAttribute('label'));
22431                
22432         range.insertNode(this.doc.createTextNode(txt));
22433     } ,
22434     
22435      
22436
22437     /**
22438      * Executes a Midas editor command on the editor document and performs necessary focus and
22439      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22440      * @param {String} cmd The Midas command
22441      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22442      */
22443     relayCmd : function(cmd, value){
22444         this.win.focus();
22445         this.execCmd(cmd, value);
22446         this.owner.fireEvent('editorevent', this);
22447         //this.updateToolbar();
22448         this.owner.deferFocus();
22449     },
22450
22451     /**
22452      * Executes a Midas editor command directly on the editor document.
22453      * For visual commands, you should use {@link #relayCmd} instead.
22454      * <b>This should only be called after the editor is initialized.</b>
22455      * @param {String} cmd The Midas command
22456      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22457      */
22458     execCmd : function(cmd, value){
22459         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22460         this.syncValue();
22461     },
22462  
22463  
22464    
22465     /**
22466      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22467      * to insert tRoo.
22468      * @param {String} text | dom node.. 
22469      */
22470     insertAtCursor : function(text)
22471     {
22472         
22473         if(!this.activated){
22474             return;
22475         }
22476         /*
22477         if(Roo.isIE){
22478             this.win.focus();
22479             var r = this.doc.selection.createRange();
22480             if(r){
22481                 r.collapse(true);
22482                 r.pasteHTML(text);
22483                 this.syncValue();
22484                 this.deferFocus();
22485             
22486             }
22487             return;
22488         }
22489         */
22490         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22491             this.win.focus();
22492             
22493             
22494             // from jquery ui (MIT licenced)
22495             var range, node;
22496             var win = this.win;
22497             
22498             if (win.getSelection && win.getSelection().getRangeAt) {
22499                 range = win.getSelection().getRangeAt(0);
22500                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22501                 range.insertNode(node);
22502             } else if (win.document.selection && win.document.selection.createRange) {
22503                 // no firefox support
22504                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22505                 win.document.selection.createRange().pasteHTML(txt);
22506             } else {
22507                 // no firefox support
22508                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22509                 this.execCmd('InsertHTML', txt);
22510             } 
22511             
22512             this.syncValue();
22513             
22514             this.deferFocus();
22515         }
22516     },
22517  // private
22518     mozKeyPress : function(e){
22519         if(e.ctrlKey){
22520             var c = e.getCharCode(), cmd;
22521           
22522             if(c > 0){
22523                 c = String.fromCharCode(c).toLowerCase();
22524                 switch(c){
22525                     case 'b':
22526                         cmd = 'bold';
22527                         break;
22528                     case 'i':
22529                         cmd = 'italic';
22530                         break;
22531                     
22532                     case 'u':
22533                         cmd = 'underline';
22534                         break;
22535                     
22536                     case 'v':
22537                         this.cleanUpPaste.defer(100, this);
22538                         return;
22539                         
22540                 }
22541                 if(cmd){
22542                     this.win.focus();
22543                     this.execCmd(cmd);
22544                     this.deferFocus();
22545                     e.preventDefault();
22546                 }
22547                 
22548             }
22549         }
22550     },
22551
22552     // private
22553     fixKeys : function(){ // load time branching for fastest keydown performance
22554         if(Roo.isIE){
22555             return function(e){
22556                 var k = e.getKey(), r;
22557                 if(k == e.TAB){
22558                     e.stopEvent();
22559                     r = this.doc.selection.createRange();
22560                     if(r){
22561                         r.collapse(true);
22562                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22563                         this.deferFocus();
22564                     }
22565                     return;
22566                 }
22567                 
22568                 if(k == e.ENTER){
22569                     r = this.doc.selection.createRange();
22570                     if(r){
22571                         var target = r.parentElement();
22572                         if(!target || target.tagName.toLowerCase() != 'li'){
22573                             e.stopEvent();
22574                             r.pasteHTML('<br />');
22575                             r.collapse(false);
22576                             r.select();
22577                         }
22578                     }
22579                 }
22580                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22581                     this.cleanUpPaste.defer(100, this);
22582                     return;
22583                 }
22584                 
22585                 
22586             };
22587         }else if(Roo.isOpera){
22588             return function(e){
22589                 var k = e.getKey();
22590                 if(k == e.TAB){
22591                     e.stopEvent();
22592                     this.win.focus();
22593                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22594                     this.deferFocus();
22595                 }
22596                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22597                     this.cleanUpPaste.defer(100, this);
22598                     return;
22599                 }
22600                 
22601             };
22602         }else if(Roo.isSafari){
22603             return function(e){
22604                 var k = e.getKey();
22605                 
22606                 if(k == e.TAB){
22607                     e.stopEvent();
22608                     this.execCmd('InsertText','\t');
22609                     this.deferFocus();
22610                     return;
22611                 }
22612                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22613                     this.cleanUpPaste.defer(100, this);
22614                     return;
22615                 }
22616                 
22617              };
22618         }
22619     }(),
22620     
22621     getAllAncestors: function()
22622     {
22623         var p = this.getSelectedNode();
22624         var a = [];
22625         if (!p) {
22626             a.push(p); // push blank onto stack..
22627             p = this.getParentElement();
22628         }
22629         
22630         
22631         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22632             a.push(p);
22633             p = p.parentNode;
22634         }
22635         a.push(this.doc.body);
22636         return a;
22637     },
22638     lastSel : false,
22639     lastSelNode : false,
22640     
22641     
22642     getSelection : function() 
22643     {
22644         this.assignDocWin();
22645         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22646     },
22647     
22648     getSelectedNode: function() 
22649     {
22650         // this may only work on Gecko!!!
22651         
22652         // should we cache this!!!!
22653         
22654         
22655         
22656          
22657         var range = this.createRange(this.getSelection()).cloneRange();
22658         
22659         if (Roo.isIE) {
22660             var parent = range.parentElement();
22661             while (true) {
22662                 var testRange = range.duplicate();
22663                 testRange.moveToElementText(parent);
22664                 if (testRange.inRange(range)) {
22665                     break;
22666                 }
22667                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22668                     break;
22669                 }
22670                 parent = parent.parentElement;
22671             }
22672             return parent;
22673         }
22674         
22675         // is ancestor a text element.
22676         var ac =  range.commonAncestorContainer;
22677         if (ac.nodeType == 3) {
22678             ac = ac.parentNode;
22679         }
22680         
22681         var ar = ac.childNodes;
22682          
22683         var nodes = [];
22684         var other_nodes = [];
22685         var has_other_nodes = false;
22686         for (var i=0;i<ar.length;i++) {
22687             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22688                 continue;
22689             }
22690             // fullly contained node.
22691             
22692             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22693                 nodes.push(ar[i]);
22694                 continue;
22695             }
22696             
22697             // probably selected..
22698             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22699                 other_nodes.push(ar[i]);
22700                 continue;
22701             }
22702             // outer..
22703             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22704                 continue;
22705             }
22706             
22707             
22708             has_other_nodes = true;
22709         }
22710         if (!nodes.length && other_nodes.length) {
22711             nodes= other_nodes;
22712         }
22713         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22714             return false;
22715         }
22716         
22717         return nodes[0];
22718     },
22719     createRange: function(sel)
22720     {
22721         // this has strange effects when using with 
22722         // top toolbar - not sure if it's a great idea.
22723         //this.editor.contentWindow.focus();
22724         if (typeof sel != "undefined") {
22725             try {
22726                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22727             } catch(e) {
22728                 return this.doc.createRange();
22729             }
22730         } else {
22731             return this.doc.createRange();
22732         }
22733     },
22734     getParentElement: function()
22735     {
22736         
22737         this.assignDocWin();
22738         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22739         
22740         var range = this.createRange(sel);
22741          
22742         try {
22743             var p = range.commonAncestorContainer;
22744             while (p.nodeType == 3) { // text node
22745                 p = p.parentNode;
22746             }
22747             return p;
22748         } catch (e) {
22749             return null;
22750         }
22751     
22752     },
22753     /***
22754      *
22755      * Range intersection.. the hard stuff...
22756      *  '-1' = before
22757      *  '0' = hits..
22758      *  '1' = after.
22759      *         [ -- selected range --- ]
22760      *   [fail]                        [fail]
22761      *
22762      *    basically..
22763      *      if end is before start or  hits it. fail.
22764      *      if start is after end or hits it fail.
22765      *
22766      *   if either hits (but other is outside. - then it's not 
22767      *   
22768      *    
22769      **/
22770     
22771     
22772     // @see http://www.thismuchiknow.co.uk/?p=64.
22773     rangeIntersectsNode : function(range, node)
22774     {
22775         var nodeRange = node.ownerDocument.createRange();
22776         try {
22777             nodeRange.selectNode(node);
22778         } catch (e) {
22779             nodeRange.selectNodeContents(node);
22780         }
22781     
22782         var rangeStartRange = range.cloneRange();
22783         rangeStartRange.collapse(true);
22784     
22785         var rangeEndRange = range.cloneRange();
22786         rangeEndRange.collapse(false);
22787     
22788         var nodeStartRange = nodeRange.cloneRange();
22789         nodeStartRange.collapse(true);
22790     
22791         var nodeEndRange = nodeRange.cloneRange();
22792         nodeEndRange.collapse(false);
22793     
22794         return rangeStartRange.compareBoundaryPoints(
22795                  Range.START_TO_START, nodeEndRange) == -1 &&
22796                rangeEndRange.compareBoundaryPoints(
22797                  Range.START_TO_START, nodeStartRange) == 1;
22798         
22799          
22800     },
22801     rangeCompareNode : function(range, node)
22802     {
22803         var nodeRange = node.ownerDocument.createRange();
22804         try {
22805             nodeRange.selectNode(node);
22806         } catch (e) {
22807             nodeRange.selectNodeContents(node);
22808         }
22809         
22810         
22811         range.collapse(true);
22812     
22813         nodeRange.collapse(true);
22814      
22815         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22816         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22817          
22818         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22819         
22820         var nodeIsBefore   =  ss == 1;
22821         var nodeIsAfter    = ee == -1;
22822         
22823         if (nodeIsBefore && nodeIsAfter) {
22824             return 0; // outer
22825         }
22826         if (!nodeIsBefore && nodeIsAfter) {
22827             return 1; //right trailed.
22828         }
22829         
22830         if (nodeIsBefore && !nodeIsAfter) {
22831             return 2;  // left trailed.
22832         }
22833         // fully contined.
22834         return 3;
22835     },
22836
22837     // private? - in a new class?
22838     cleanUpPaste :  function()
22839     {
22840         // cleans up the whole document..
22841         Roo.log('cleanuppaste');
22842         
22843         this.cleanUpChildren(this.doc.body);
22844         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22845         if (clean != this.doc.body.innerHTML) {
22846             this.doc.body.innerHTML = clean;
22847         }
22848         
22849     },
22850     
22851     cleanWordChars : function(input) {// change the chars to hex code
22852         var he = Roo.HtmlEditorCore;
22853         
22854         var output = input;
22855         Roo.each(he.swapCodes, function(sw) { 
22856             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22857             
22858             output = output.replace(swapper, sw[1]);
22859         });
22860         
22861         return output;
22862     },
22863     
22864     
22865     cleanUpChildren : function (n)
22866     {
22867         if (!n.childNodes.length) {
22868             return;
22869         }
22870         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22871            this.cleanUpChild(n.childNodes[i]);
22872         }
22873     },
22874     
22875     
22876         
22877     
22878     cleanUpChild : function (node)
22879     {
22880         var ed = this;
22881         //console.log(node);
22882         if (node.nodeName == "#text") {
22883             // clean up silly Windows -- stuff?
22884             return; 
22885         }
22886         if (node.nodeName == "#comment") {
22887             node.parentNode.removeChild(node);
22888             // clean up silly Windows -- stuff?
22889             return; 
22890         }
22891         var lcname = node.tagName.toLowerCase();
22892         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22893         // whitelist of tags..
22894         
22895         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22896             // remove node.
22897             node.parentNode.removeChild(node);
22898             return;
22899             
22900         }
22901         
22902         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22903         
22904         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22905         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22906         
22907         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22908         //    remove_keep_children = true;
22909         //}
22910         
22911         if (remove_keep_children) {
22912             this.cleanUpChildren(node);
22913             // inserts everything just before this node...
22914             while (node.childNodes.length) {
22915                 var cn = node.childNodes[0];
22916                 node.removeChild(cn);
22917                 node.parentNode.insertBefore(cn, node);
22918             }
22919             node.parentNode.removeChild(node);
22920             return;
22921         }
22922         
22923         if (!node.attributes || !node.attributes.length) {
22924             this.cleanUpChildren(node);
22925             return;
22926         }
22927         
22928         function cleanAttr(n,v)
22929         {
22930             
22931             if (v.match(/^\./) || v.match(/^\//)) {
22932                 return;
22933             }
22934             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22935                 return;
22936             }
22937             if (v.match(/^#/)) {
22938                 return;
22939             }
22940 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22941             node.removeAttribute(n);
22942             
22943         }
22944         
22945         var cwhite = this.cwhite;
22946         var cblack = this.cblack;
22947             
22948         function cleanStyle(n,v)
22949         {
22950             if (v.match(/expression/)) { //XSS?? should we even bother..
22951                 node.removeAttribute(n);
22952                 return;
22953             }
22954             
22955             var parts = v.split(/;/);
22956             var clean = [];
22957             
22958             Roo.each(parts, function(p) {
22959                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22960                 if (!p.length) {
22961                     return true;
22962                 }
22963                 var l = p.split(':').shift().replace(/\s+/g,'');
22964                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22965                 
22966                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22967 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22968                     //node.removeAttribute(n);
22969                     return true;
22970                 }
22971                 //Roo.log()
22972                 // only allow 'c whitelisted system attributes'
22973                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22974 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22975                     //node.removeAttribute(n);
22976                     return true;
22977                 }
22978                 
22979                 
22980                  
22981                 
22982                 clean.push(p);
22983                 return true;
22984             });
22985             if (clean.length) { 
22986                 node.setAttribute(n, clean.join(';'));
22987             } else {
22988                 node.removeAttribute(n);
22989             }
22990             
22991         }
22992         
22993         
22994         for (var i = node.attributes.length-1; i > -1 ; i--) {
22995             var a = node.attributes[i];
22996             //console.log(a);
22997             
22998             if (a.name.toLowerCase().substr(0,2)=='on')  {
22999                 node.removeAttribute(a.name);
23000                 continue;
23001             }
23002             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23003                 node.removeAttribute(a.name);
23004                 continue;
23005             }
23006             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23007                 cleanAttr(a.name,a.value); // fixme..
23008                 continue;
23009             }
23010             if (a.name == 'style') {
23011                 cleanStyle(a.name,a.value);
23012                 continue;
23013             }
23014             /// clean up MS crap..
23015             // tecnically this should be a list of valid class'es..
23016             
23017             
23018             if (a.name == 'class') {
23019                 if (a.value.match(/^Mso/)) {
23020                     node.className = '';
23021                 }
23022                 
23023                 if (a.value.match(/^body$/)) {
23024                     node.className = '';
23025                 }
23026                 continue;
23027             }
23028             
23029             // style cleanup!?
23030             // class cleanup?
23031             
23032         }
23033         
23034         
23035         this.cleanUpChildren(node);
23036         
23037         
23038     },
23039     
23040     /**
23041      * Clean up MS wordisms...
23042      */
23043     cleanWord : function(node)
23044     {
23045         
23046         
23047         if (!node) {
23048             this.cleanWord(this.doc.body);
23049             return;
23050         }
23051         if (node.nodeName == "#text") {
23052             // clean up silly Windows -- stuff?
23053             return; 
23054         }
23055         if (node.nodeName == "#comment") {
23056             node.parentNode.removeChild(node);
23057             // clean up silly Windows -- stuff?
23058             return; 
23059         }
23060         
23061         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23062             node.parentNode.removeChild(node);
23063             return;
23064         }
23065         
23066         // remove - but keep children..
23067         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23068             while (node.childNodes.length) {
23069                 var cn = node.childNodes[0];
23070                 node.removeChild(cn);
23071                 node.parentNode.insertBefore(cn, node);
23072             }
23073             node.parentNode.removeChild(node);
23074             this.iterateChildren(node, this.cleanWord);
23075             return;
23076         }
23077         // clean styles
23078         if (node.className.length) {
23079             
23080             var cn = node.className.split(/\W+/);
23081             var cna = [];
23082             Roo.each(cn, function(cls) {
23083                 if (cls.match(/Mso[a-zA-Z]+/)) {
23084                     return;
23085                 }
23086                 cna.push(cls);
23087             });
23088             node.className = cna.length ? cna.join(' ') : '';
23089             if (!cna.length) {
23090                 node.removeAttribute("class");
23091             }
23092         }
23093         
23094         if (node.hasAttribute("lang")) {
23095             node.removeAttribute("lang");
23096         }
23097         
23098         if (node.hasAttribute("style")) {
23099             
23100             var styles = node.getAttribute("style").split(";");
23101             var nstyle = [];
23102             Roo.each(styles, function(s) {
23103                 if (!s.match(/:/)) {
23104                     return;
23105                 }
23106                 var kv = s.split(":");
23107                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23108                     return;
23109                 }
23110                 // what ever is left... we allow.
23111                 nstyle.push(s);
23112             });
23113             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23114             if (!nstyle.length) {
23115                 node.removeAttribute('style');
23116             }
23117         }
23118         this.iterateChildren(node, this.cleanWord);
23119         
23120         
23121         
23122     },
23123     /**
23124      * iterateChildren of a Node, calling fn each time, using this as the scole..
23125      * @param {DomNode} node node to iterate children of.
23126      * @param {Function} fn method of this class to call on each item.
23127      */
23128     iterateChildren : function(node, fn)
23129     {
23130         if (!node.childNodes.length) {
23131                 return;
23132         }
23133         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23134            fn.call(this, node.childNodes[i])
23135         }
23136     },
23137     
23138     
23139     /**
23140      * cleanTableWidths.
23141      *
23142      * Quite often pasting from word etc.. results in tables with column and widths.
23143      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23144      *
23145      */
23146     cleanTableWidths : function(node)
23147     {
23148          
23149          
23150         if (!node) {
23151             this.cleanTableWidths(this.doc.body);
23152             return;
23153         }
23154         
23155         // ignore list...
23156         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23157             return; 
23158         }
23159         Roo.log(node.tagName);
23160         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23161             this.iterateChildren(node, this.cleanTableWidths);
23162             return;
23163         }
23164         if (node.hasAttribute('width')) {
23165             node.removeAttribute('width');
23166         }
23167         
23168          
23169         if (node.hasAttribute("style")) {
23170             // pretty basic...
23171             
23172             var styles = node.getAttribute("style").split(";");
23173             var nstyle = [];
23174             Roo.each(styles, function(s) {
23175                 if (!s.match(/:/)) {
23176                     return;
23177                 }
23178                 var kv = s.split(":");
23179                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23180                     return;
23181                 }
23182                 // what ever is left... we allow.
23183                 nstyle.push(s);
23184             });
23185             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23186             if (!nstyle.length) {
23187                 node.removeAttribute('style');
23188             }
23189         }
23190         
23191         this.iterateChildren(node, this.cleanTableWidths);
23192         
23193         
23194     },
23195     
23196     
23197     
23198     
23199     domToHTML : function(currentElement, depth, nopadtext) {
23200         
23201         depth = depth || 0;
23202         nopadtext = nopadtext || false;
23203     
23204         if (!currentElement) {
23205             return this.domToHTML(this.doc.body);
23206         }
23207         
23208         //Roo.log(currentElement);
23209         var j;
23210         var allText = false;
23211         var nodeName = currentElement.nodeName;
23212         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23213         
23214         if  (nodeName == '#text') {
23215             
23216             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23217         }
23218         
23219         
23220         var ret = '';
23221         if (nodeName != 'BODY') {
23222              
23223             var i = 0;
23224             // Prints the node tagName, such as <A>, <IMG>, etc
23225             if (tagName) {
23226                 var attr = [];
23227                 for(i = 0; i < currentElement.attributes.length;i++) {
23228                     // quoting?
23229                     var aname = currentElement.attributes.item(i).name;
23230                     if (!currentElement.attributes.item(i).value.length) {
23231                         continue;
23232                     }
23233                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23234                 }
23235                 
23236                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23237             } 
23238             else {
23239                 
23240                 // eack
23241             }
23242         } else {
23243             tagName = false;
23244         }
23245         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23246             return ret;
23247         }
23248         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23249             nopadtext = true;
23250         }
23251         
23252         
23253         // Traverse the tree
23254         i = 0;
23255         var currentElementChild = currentElement.childNodes.item(i);
23256         var allText = true;
23257         var innerHTML  = '';
23258         lastnode = '';
23259         while (currentElementChild) {
23260             // Formatting code (indent the tree so it looks nice on the screen)
23261             var nopad = nopadtext;
23262             if (lastnode == 'SPAN') {
23263                 nopad  = true;
23264             }
23265             // text
23266             if  (currentElementChild.nodeName == '#text') {
23267                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23268                 toadd = nopadtext ? toadd : toadd.trim();
23269                 if (!nopad && toadd.length > 80) {
23270                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23271                 }
23272                 innerHTML  += toadd;
23273                 
23274                 i++;
23275                 currentElementChild = currentElement.childNodes.item(i);
23276                 lastNode = '';
23277                 continue;
23278             }
23279             allText = false;
23280             
23281             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23282                 
23283             // Recursively traverse the tree structure of the child node
23284             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23285             lastnode = currentElementChild.nodeName;
23286             i++;
23287             currentElementChild=currentElement.childNodes.item(i);
23288         }
23289         
23290         ret += innerHTML;
23291         
23292         if (!allText) {
23293                 // The remaining code is mostly for formatting the tree
23294             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23295         }
23296         
23297         
23298         if (tagName) {
23299             ret+= "</"+tagName+">";
23300         }
23301         return ret;
23302         
23303     },
23304         
23305     applyBlacklists : function()
23306     {
23307         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23308         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23309         
23310         this.white = [];
23311         this.black = [];
23312         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23313             if (b.indexOf(tag) > -1) {
23314                 return;
23315             }
23316             this.white.push(tag);
23317             
23318         }, this);
23319         
23320         Roo.each(w, function(tag) {
23321             if (b.indexOf(tag) > -1) {
23322                 return;
23323             }
23324             if (this.white.indexOf(tag) > -1) {
23325                 return;
23326             }
23327             this.white.push(tag);
23328             
23329         }, this);
23330         
23331         
23332         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23333             if (w.indexOf(tag) > -1) {
23334                 return;
23335             }
23336             this.black.push(tag);
23337             
23338         }, this);
23339         
23340         Roo.each(b, function(tag) {
23341             if (w.indexOf(tag) > -1) {
23342                 return;
23343             }
23344             if (this.black.indexOf(tag) > -1) {
23345                 return;
23346             }
23347             this.black.push(tag);
23348             
23349         }, this);
23350         
23351         
23352         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23353         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23354         
23355         this.cwhite = [];
23356         this.cblack = [];
23357         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23358             if (b.indexOf(tag) > -1) {
23359                 return;
23360             }
23361             this.cwhite.push(tag);
23362             
23363         }, this);
23364         
23365         Roo.each(w, function(tag) {
23366             if (b.indexOf(tag) > -1) {
23367                 return;
23368             }
23369             if (this.cwhite.indexOf(tag) > -1) {
23370                 return;
23371             }
23372             this.cwhite.push(tag);
23373             
23374         }, this);
23375         
23376         
23377         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23378             if (w.indexOf(tag) > -1) {
23379                 return;
23380             }
23381             this.cblack.push(tag);
23382             
23383         }, this);
23384         
23385         Roo.each(b, function(tag) {
23386             if (w.indexOf(tag) > -1) {
23387                 return;
23388             }
23389             if (this.cblack.indexOf(tag) > -1) {
23390                 return;
23391             }
23392             this.cblack.push(tag);
23393             
23394         }, this);
23395     },
23396     
23397     setStylesheets : function(stylesheets)
23398     {
23399         if(typeof(stylesheets) == 'string'){
23400             Roo.get(this.iframe.contentDocument.head).createChild({
23401                 tag : 'link',
23402                 rel : 'stylesheet',
23403                 type : 'text/css',
23404                 href : stylesheets
23405             });
23406             
23407             return;
23408         }
23409         var _this = this;
23410      
23411         Roo.each(stylesheets, function(s) {
23412             if(!s.length){
23413                 return;
23414             }
23415             
23416             Roo.get(_this.iframe.contentDocument.head).createChild({
23417                 tag : 'link',
23418                 rel : 'stylesheet',
23419                 type : 'text/css',
23420                 href : s
23421             });
23422         });
23423
23424         
23425     },
23426     
23427     removeStylesheets : function()
23428     {
23429         var _this = this;
23430         
23431         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23432             s.remove();
23433         });
23434     },
23435     
23436     setStyle : function(style)
23437     {
23438         Roo.get(this.iframe.contentDocument.head).createChild({
23439             tag : 'style',
23440             type : 'text/css',
23441             html : style
23442         });
23443
23444         return;
23445     }
23446     
23447     // hide stuff that is not compatible
23448     /**
23449      * @event blur
23450      * @hide
23451      */
23452     /**
23453      * @event change
23454      * @hide
23455      */
23456     /**
23457      * @event focus
23458      * @hide
23459      */
23460     /**
23461      * @event specialkey
23462      * @hide
23463      */
23464     /**
23465      * @cfg {String} fieldClass @hide
23466      */
23467     /**
23468      * @cfg {String} focusClass @hide
23469      */
23470     /**
23471      * @cfg {String} autoCreate @hide
23472      */
23473     /**
23474      * @cfg {String} inputType @hide
23475      */
23476     /**
23477      * @cfg {String} invalidClass @hide
23478      */
23479     /**
23480      * @cfg {String} invalidText @hide
23481      */
23482     /**
23483      * @cfg {String} msgFx @hide
23484      */
23485     /**
23486      * @cfg {String} validateOnBlur @hide
23487      */
23488 });
23489
23490 Roo.HtmlEditorCore.white = [
23491         'area', 'br', 'img', 'input', 'hr', 'wbr',
23492         
23493        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23494        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23495        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23496        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23497        'table',   'ul',         'xmp', 
23498        
23499        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23500       'thead',   'tr', 
23501      
23502       'dir', 'menu', 'ol', 'ul', 'dl',
23503        
23504       'embed',  'object'
23505 ];
23506
23507
23508 Roo.HtmlEditorCore.black = [
23509     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23510         'applet', // 
23511         'base',   'basefont', 'bgsound', 'blink',  'body', 
23512         'frame',  'frameset', 'head',    'html',   'ilayer', 
23513         'iframe', 'layer',  'link',     'meta',    'object',   
23514         'script', 'style' ,'title',  'xml' // clean later..
23515 ];
23516 Roo.HtmlEditorCore.clean = [
23517     'script', 'style', 'title', 'xml'
23518 ];
23519 Roo.HtmlEditorCore.remove = [
23520     'font'
23521 ];
23522 // attributes..
23523
23524 Roo.HtmlEditorCore.ablack = [
23525     'on'
23526 ];
23527     
23528 Roo.HtmlEditorCore.aclean = [ 
23529     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23530 ];
23531
23532 // protocols..
23533 Roo.HtmlEditorCore.pwhite= [
23534         'http',  'https',  'mailto'
23535 ];
23536
23537 // white listed style attributes.
23538 Roo.HtmlEditorCore.cwhite= [
23539       //  'text-align', /// default is to allow most things..
23540       
23541          
23542 //        'font-size'//??
23543 ];
23544
23545 // black listed style attributes.
23546 Roo.HtmlEditorCore.cblack= [
23547       //  'font-size' -- this can be set by the project 
23548 ];
23549
23550
23551 Roo.HtmlEditorCore.swapCodes   =[ 
23552     [    8211, "--" ], 
23553     [    8212, "--" ], 
23554     [    8216,  "'" ],  
23555     [    8217, "'" ],  
23556     [    8220, '"' ],  
23557     [    8221, '"' ],  
23558     [    8226, "*" ],  
23559     [    8230, "..." ]
23560 ]; 
23561
23562     /*
23563  * - LGPL
23564  *
23565  * HtmlEditor
23566  * 
23567  */
23568
23569 /**
23570  * @class Roo.bootstrap.HtmlEditor
23571  * @extends Roo.bootstrap.TextArea
23572  * Bootstrap HtmlEditor class
23573
23574  * @constructor
23575  * Create a new HtmlEditor
23576  * @param {Object} config The config object
23577  */
23578
23579 Roo.bootstrap.HtmlEditor = function(config){
23580     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23581     if (!this.toolbars) {
23582         this.toolbars = [];
23583     }
23584     
23585     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23586     this.addEvents({
23587             /**
23588              * @event initialize
23589              * Fires when the editor is fully initialized (including the iframe)
23590              * @param {HtmlEditor} this
23591              */
23592             initialize: true,
23593             /**
23594              * @event activate
23595              * Fires when the editor is first receives the focus. Any insertion must wait
23596              * until after this event.
23597              * @param {HtmlEditor} this
23598              */
23599             activate: true,
23600              /**
23601              * @event beforesync
23602              * Fires before the textarea is updated with content from the editor iframe. Return false
23603              * to cancel the sync.
23604              * @param {HtmlEditor} this
23605              * @param {String} html
23606              */
23607             beforesync: true,
23608              /**
23609              * @event beforepush
23610              * Fires before the iframe editor is updated with content from the textarea. Return false
23611              * to cancel the push.
23612              * @param {HtmlEditor} this
23613              * @param {String} html
23614              */
23615             beforepush: true,
23616              /**
23617              * @event sync
23618              * Fires when the textarea is updated with content from the editor iframe.
23619              * @param {HtmlEditor} this
23620              * @param {String} html
23621              */
23622             sync: true,
23623              /**
23624              * @event push
23625              * Fires when the iframe editor is updated with content from the textarea.
23626              * @param {HtmlEditor} this
23627              * @param {String} html
23628              */
23629             push: true,
23630              /**
23631              * @event editmodechange
23632              * Fires when the editor switches edit modes
23633              * @param {HtmlEditor} this
23634              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23635              */
23636             editmodechange: true,
23637             /**
23638              * @event editorevent
23639              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23640              * @param {HtmlEditor} this
23641              */
23642             editorevent: true,
23643             /**
23644              * @event firstfocus
23645              * Fires when on first focus - needed by toolbars..
23646              * @param {HtmlEditor} this
23647              */
23648             firstfocus: true,
23649             /**
23650              * @event autosave
23651              * Auto save the htmlEditor value as a file into Events
23652              * @param {HtmlEditor} this
23653              */
23654             autosave: true,
23655             /**
23656              * @event savedpreview
23657              * preview the saved version of htmlEditor
23658              * @param {HtmlEditor} this
23659              */
23660             savedpreview: true
23661         });
23662 };
23663
23664
23665 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23666     
23667     
23668       /**
23669      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23670      */
23671     toolbars : false,
23672     
23673      /**
23674     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23675     */
23676     btns : [],
23677    
23678      /**
23679      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23680      *                        Roo.resizable.
23681      */
23682     resizable : false,
23683      /**
23684      * @cfg {Number} height (in pixels)
23685      */   
23686     height: 300,
23687    /**
23688      * @cfg {Number} width (in pixels)
23689      */   
23690     width: false,
23691     
23692     /**
23693      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23694      * 
23695      */
23696     stylesheets: false,
23697     
23698     // id of frame..
23699     frameId: false,
23700     
23701     // private properties
23702     validationEvent : false,
23703     deferHeight: true,
23704     initialized : false,
23705     activated : false,
23706     
23707     onFocus : Roo.emptyFn,
23708     iframePad:3,
23709     hideMode:'offsets',
23710     
23711     tbContainer : false,
23712     
23713     bodyCls : '',
23714     
23715     toolbarContainer :function() {
23716         return this.wrap.select('.x-html-editor-tb',true).first();
23717     },
23718
23719     /**
23720      * Protected method that will not generally be called directly. It
23721      * is called when the editor creates its toolbar. Override this method if you need to
23722      * add custom toolbar buttons.
23723      * @param {HtmlEditor} editor
23724      */
23725     createToolbar : function(){
23726         Roo.log('renewing');
23727         Roo.log("create toolbars");
23728         
23729         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23730         this.toolbars[0].render(this.toolbarContainer());
23731         
23732         return;
23733         
23734 //        if (!editor.toolbars || !editor.toolbars.length) {
23735 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23736 //        }
23737 //        
23738 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23739 //            editor.toolbars[i] = Roo.factory(
23740 //                    typeof(editor.toolbars[i]) == 'string' ?
23741 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23742 //                Roo.bootstrap.HtmlEditor);
23743 //            editor.toolbars[i].init(editor);
23744 //        }
23745     },
23746
23747      
23748     // private
23749     onRender : function(ct, position)
23750     {
23751        // Roo.log("Call onRender: " + this.xtype);
23752         var _t = this;
23753         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23754       
23755         this.wrap = this.inputEl().wrap({
23756             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23757         });
23758         
23759         this.editorcore.onRender(ct, position);
23760          
23761         if (this.resizable) {
23762             this.resizeEl = new Roo.Resizable(this.wrap, {
23763                 pinned : true,
23764                 wrap: true,
23765                 dynamic : true,
23766                 minHeight : this.height,
23767                 height: this.height,
23768                 handles : this.resizable,
23769                 width: this.width,
23770                 listeners : {
23771                     resize : function(r, w, h) {
23772                         _t.onResize(w,h); // -something
23773                     }
23774                 }
23775             });
23776             
23777         }
23778         this.createToolbar(this);
23779        
23780         
23781         if(!this.width && this.resizable){
23782             this.setSize(this.wrap.getSize());
23783         }
23784         if (this.resizeEl) {
23785             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23786             // should trigger onReize..
23787         }
23788         
23789     },
23790
23791     // private
23792     onResize : function(w, h)
23793     {
23794         Roo.log('resize: ' +w + ',' + h );
23795         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23796         var ew = false;
23797         var eh = false;
23798         
23799         if(this.inputEl() ){
23800             if(typeof w == 'number'){
23801                 var aw = w - this.wrap.getFrameWidth('lr');
23802                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23803                 ew = aw;
23804             }
23805             if(typeof h == 'number'){
23806                  var tbh = -11;  // fixme it needs to tool bar size!
23807                 for (var i =0; i < this.toolbars.length;i++) {
23808                     // fixme - ask toolbars for heights?
23809                     tbh += this.toolbars[i].el.getHeight();
23810                     //if (this.toolbars[i].footer) {
23811                     //    tbh += this.toolbars[i].footer.el.getHeight();
23812                     //}
23813                 }
23814               
23815                 
23816                 
23817                 
23818                 
23819                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23820                 ah -= 5; // knock a few pixes off for look..
23821                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23822                 var eh = ah;
23823             }
23824         }
23825         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23826         this.editorcore.onResize(ew,eh);
23827         
23828     },
23829
23830     /**
23831      * Toggles the editor between standard and source edit mode.
23832      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23833      */
23834     toggleSourceEdit : function(sourceEditMode)
23835     {
23836         this.editorcore.toggleSourceEdit(sourceEditMode);
23837         
23838         if(this.editorcore.sourceEditMode){
23839             Roo.log('editor - showing textarea');
23840             
23841 //            Roo.log('in');
23842 //            Roo.log(this.syncValue());
23843             this.syncValue();
23844             this.inputEl().removeClass(['hide', 'x-hidden']);
23845             this.inputEl().dom.removeAttribute('tabIndex');
23846             this.inputEl().focus();
23847         }else{
23848             Roo.log('editor - hiding textarea');
23849 //            Roo.log('out')
23850 //            Roo.log(this.pushValue()); 
23851             this.pushValue();
23852             
23853             this.inputEl().addClass(['hide', 'x-hidden']);
23854             this.inputEl().dom.setAttribute('tabIndex', -1);
23855             //this.deferFocus();
23856         }
23857          
23858         if(this.resizable){
23859             this.setSize(this.wrap.getSize());
23860         }
23861         
23862         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23863     },
23864  
23865     // private (for BoxComponent)
23866     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23867
23868     // private (for BoxComponent)
23869     getResizeEl : function(){
23870         return this.wrap;
23871     },
23872
23873     // private (for BoxComponent)
23874     getPositionEl : function(){
23875         return this.wrap;
23876     },
23877
23878     // private
23879     initEvents : function(){
23880         this.originalValue = this.getValue();
23881     },
23882
23883 //    /**
23884 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23885 //     * @method
23886 //     */
23887 //    markInvalid : Roo.emptyFn,
23888 //    /**
23889 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23890 //     * @method
23891 //     */
23892 //    clearInvalid : Roo.emptyFn,
23893
23894     setValue : function(v){
23895         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23896         this.editorcore.pushValue();
23897     },
23898
23899      
23900     // private
23901     deferFocus : function(){
23902         this.focus.defer(10, this);
23903     },
23904
23905     // doc'ed in Field
23906     focus : function(){
23907         this.editorcore.focus();
23908         
23909     },
23910       
23911
23912     // private
23913     onDestroy : function(){
23914         
23915         
23916         
23917         if(this.rendered){
23918             
23919             for (var i =0; i < this.toolbars.length;i++) {
23920                 // fixme - ask toolbars for heights?
23921                 this.toolbars[i].onDestroy();
23922             }
23923             
23924             this.wrap.dom.innerHTML = '';
23925             this.wrap.remove();
23926         }
23927     },
23928
23929     // private
23930     onFirstFocus : function(){
23931         //Roo.log("onFirstFocus");
23932         this.editorcore.onFirstFocus();
23933          for (var i =0; i < this.toolbars.length;i++) {
23934             this.toolbars[i].onFirstFocus();
23935         }
23936         
23937     },
23938     
23939     // private
23940     syncValue : function()
23941     {   
23942         this.editorcore.syncValue();
23943     },
23944     
23945     pushValue : function()
23946     {   
23947         this.editorcore.pushValue();
23948     }
23949      
23950     
23951     // hide stuff that is not compatible
23952     /**
23953      * @event blur
23954      * @hide
23955      */
23956     /**
23957      * @event change
23958      * @hide
23959      */
23960     /**
23961      * @event focus
23962      * @hide
23963      */
23964     /**
23965      * @event specialkey
23966      * @hide
23967      */
23968     /**
23969      * @cfg {String} fieldClass @hide
23970      */
23971     /**
23972      * @cfg {String} focusClass @hide
23973      */
23974     /**
23975      * @cfg {String} autoCreate @hide
23976      */
23977     /**
23978      * @cfg {String} inputType @hide
23979      */
23980      
23981     /**
23982      * @cfg {String} invalidText @hide
23983      */
23984     /**
23985      * @cfg {String} msgFx @hide
23986      */
23987     /**
23988      * @cfg {String} validateOnBlur @hide
23989      */
23990 });
23991  
23992     
23993    
23994    
23995    
23996       
23997 Roo.namespace('Roo.bootstrap.htmleditor');
23998 /**
23999  * @class Roo.bootstrap.HtmlEditorToolbar1
24000  * Basic Toolbar
24001  * 
24002  * Usage:
24003  *
24004  new Roo.bootstrap.HtmlEditor({
24005     ....
24006     toolbars : [
24007         new Roo.bootstrap.HtmlEditorToolbar1({
24008             disable : { fonts: 1 , format: 1, ..., ... , ...],
24009             btns : [ .... ]
24010         })
24011     }
24012      
24013  * 
24014  * @cfg {Object} disable List of elements to disable..
24015  * @cfg {Array} btns List of additional buttons.
24016  * 
24017  * 
24018  * NEEDS Extra CSS? 
24019  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24020  */
24021  
24022 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24023 {
24024     
24025     Roo.apply(this, config);
24026     
24027     // default disabled, based on 'good practice'..
24028     this.disable = this.disable || {};
24029     Roo.applyIf(this.disable, {
24030         fontSize : true,
24031         colors : true,
24032         specialElements : true
24033     });
24034     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24035     
24036     this.editor = config.editor;
24037     this.editorcore = config.editor.editorcore;
24038     
24039     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24040     
24041     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24042     // dont call parent... till later.
24043 }
24044 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24045      
24046     bar : true,
24047     
24048     editor : false,
24049     editorcore : false,
24050     
24051     
24052     formats : [
24053         "p" ,  
24054         "h1","h2","h3","h4","h5","h6", 
24055         "pre", "code", 
24056         "abbr", "acronym", "address", "cite", "samp", "var",
24057         'div','span'
24058     ],
24059     
24060     onRender : function(ct, position)
24061     {
24062        // Roo.log("Call onRender: " + this.xtype);
24063         
24064        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24065        Roo.log(this.el);
24066        this.el.dom.style.marginBottom = '0';
24067        var _this = this;
24068        var editorcore = this.editorcore;
24069        var editor= this.editor;
24070        
24071        var children = [];
24072        var btn = function(id,cmd , toggle, handler, html){
24073        
24074             var  event = toggle ? 'toggle' : 'click';
24075        
24076             var a = {
24077                 size : 'sm',
24078                 xtype: 'Button',
24079                 xns: Roo.bootstrap,
24080                 //glyphicon : id,
24081                 fa: id,
24082                 cmd : id || cmd,
24083                 enableToggle:toggle !== false,
24084                 html : html || '',
24085                 pressed : toggle ? false : null,
24086                 listeners : {}
24087             };
24088             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24089                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24090             };
24091             children.push(a);
24092             return a;
24093        }
24094        
24095     //    var cb_box = function...
24096         
24097         var style = {
24098                 xtype: 'Button',
24099                 size : 'sm',
24100                 xns: Roo.bootstrap,
24101                 fa : 'font',
24102                 //html : 'submit'
24103                 menu : {
24104                     xtype: 'Menu',
24105                     xns: Roo.bootstrap,
24106                     items:  []
24107                 }
24108         };
24109         Roo.each(this.formats, function(f) {
24110             style.menu.items.push({
24111                 xtype :'MenuItem',
24112                 xns: Roo.bootstrap,
24113                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24114                 tagname : f,
24115                 listeners : {
24116                     click : function()
24117                     {
24118                         editorcore.insertTag(this.tagname);
24119                         editor.focus();
24120                     }
24121                 }
24122                 
24123             });
24124         });
24125         children.push(style);   
24126         
24127         btn('bold',false,true);
24128         btn('italic',false,true);
24129         btn('align-left', 'justifyleft',true);
24130         btn('align-center', 'justifycenter',true);
24131         btn('align-right' , 'justifyright',true);
24132         btn('link', false, false, function(btn) {
24133             //Roo.log("create link?");
24134             var url = prompt(this.createLinkText, this.defaultLinkValue);
24135             if(url && url != 'http:/'+'/'){
24136                 this.editorcore.relayCmd('createlink', url);
24137             }
24138         }),
24139         btn('list','insertunorderedlist',true);
24140         btn('pencil', false,true, function(btn){
24141                 Roo.log(this);
24142                 this.toggleSourceEdit(btn.pressed);
24143         });
24144         
24145         if (this.editor.btns.length > 0) {
24146             for (var i = 0; i<this.editor.btns.length; i++) {
24147                 children.push(this.editor.btns[i]);
24148             }
24149         }
24150         
24151         /*
24152         var cog = {
24153                 xtype: 'Button',
24154                 size : 'sm',
24155                 xns: Roo.bootstrap,
24156                 glyphicon : 'cog',
24157                 //html : 'submit'
24158                 menu : {
24159                     xtype: 'Menu',
24160                     xns: Roo.bootstrap,
24161                     items:  []
24162                 }
24163         };
24164         
24165         cog.menu.items.push({
24166             xtype :'MenuItem',
24167             xns: Roo.bootstrap,
24168             html : Clean styles,
24169             tagname : f,
24170             listeners : {
24171                 click : function()
24172                 {
24173                     editorcore.insertTag(this.tagname);
24174                     editor.focus();
24175                 }
24176             }
24177             
24178         });
24179        */
24180         
24181          
24182        this.xtype = 'NavSimplebar';
24183         
24184         for(var i=0;i< children.length;i++) {
24185             
24186             this.buttons.add(this.addxtypeChild(children[i]));
24187             
24188         }
24189         
24190         editor.on('editorevent', this.updateToolbar, this);
24191     },
24192     onBtnClick : function(id)
24193     {
24194        this.editorcore.relayCmd(id);
24195        this.editorcore.focus();
24196     },
24197     
24198     /**
24199      * Protected method that will not generally be called directly. It triggers
24200      * a toolbar update by reading the markup state of the current selection in the editor.
24201      */
24202     updateToolbar: function(){
24203
24204         if(!this.editorcore.activated){
24205             this.editor.onFirstFocus(); // is this neeed?
24206             return;
24207         }
24208
24209         var btns = this.buttons; 
24210         var doc = this.editorcore.doc;
24211         btns.get('bold').setActive(doc.queryCommandState('bold'));
24212         btns.get('italic').setActive(doc.queryCommandState('italic'));
24213         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24214         
24215         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24216         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24217         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24218         
24219         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24220         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24221          /*
24222         
24223         var ans = this.editorcore.getAllAncestors();
24224         if (this.formatCombo) {
24225             
24226             
24227             var store = this.formatCombo.store;
24228             this.formatCombo.setValue("");
24229             for (var i =0; i < ans.length;i++) {
24230                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24231                     // select it..
24232                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24233                     break;
24234                 }
24235             }
24236         }
24237         
24238         
24239         
24240         // hides menus... - so this cant be on a menu...
24241         Roo.bootstrap.MenuMgr.hideAll();
24242         */
24243         Roo.bootstrap.MenuMgr.hideAll();
24244         //this.editorsyncValue();
24245     },
24246     onFirstFocus: function() {
24247         this.buttons.each(function(item){
24248            item.enable();
24249         });
24250     },
24251     toggleSourceEdit : function(sourceEditMode){
24252         
24253           
24254         if(sourceEditMode){
24255             Roo.log("disabling buttons");
24256            this.buttons.each( function(item){
24257                 if(item.cmd != 'pencil'){
24258                     item.disable();
24259                 }
24260             });
24261           
24262         }else{
24263             Roo.log("enabling buttons");
24264             if(this.editorcore.initialized){
24265                 this.buttons.each( function(item){
24266                     item.enable();
24267                 });
24268             }
24269             
24270         }
24271         Roo.log("calling toggole on editor");
24272         // tell the editor that it's been pressed..
24273         this.editor.toggleSourceEdit(sourceEditMode);
24274        
24275     }
24276 });
24277
24278
24279
24280
24281
24282 /**
24283  * @class Roo.bootstrap.Table.AbstractSelectionModel
24284  * @extends Roo.util.Observable
24285  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24286  * implemented by descendant classes.  This class should not be directly instantiated.
24287  * @constructor
24288  */
24289 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24290     this.locked = false;
24291     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24292 };
24293
24294
24295 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24296     /** @ignore Called by the grid automatically. Do not call directly. */
24297     init : function(grid){
24298         this.grid = grid;
24299         this.initEvents();
24300     },
24301
24302     /**
24303      * Locks the selections.
24304      */
24305     lock : function(){
24306         this.locked = true;
24307     },
24308
24309     /**
24310      * Unlocks the selections.
24311      */
24312     unlock : function(){
24313         this.locked = false;
24314     },
24315
24316     /**
24317      * Returns true if the selections are locked.
24318      * @return {Boolean}
24319      */
24320     isLocked : function(){
24321         return this.locked;
24322     }
24323 });
24324 /**
24325  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24326  * @class Roo.bootstrap.Table.RowSelectionModel
24327  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24328  * It supports multiple selections and keyboard selection/navigation. 
24329  * @constructor
24330  * @param {Object} config
24331  */
24332
24333 Roo.bootstrap.Table.RowSelectionModel = function(config){
24334     Roo.apply(this, config);
24335     this.selections = new Roo.util.MixedCollection(false, function(o){
24336         return o.id;
24337     });
24338
24339     this.last = false;
24340     this.lastActive = false;
24341
24342     this.addEvents({
24343         /**
24344              * @event selectionchange
24345              * Fires when the selection changes
24346              * @param {SelectionModel} this
24347              */
24348             "selectionchange" : true,
24349         /**
24350              * @event afterselectionchange
24351              * Fires after the selection changes (eg. by key press or clicking)
24352              * @param {SelectionModel} this
24353              */
24354             "afterselectionchange" : true,
24355         /**
24356              * @event beforerowselect
24357              * Fires when a row is selected being selected, return false to cancel.
24358              * @param {SelectionModel} this
24359              * @param {Number} rowIndex The selected index
24360              * @param {Boolean} keepExisting False if other selections will be cleared
24361              */
24362             "beforerowselect" : true,
24363         /**
24364              * @event rowselect
24365              * Fires when a row is selected.
24366              * @param {SelectionModel} this
24367              * @param {Number} rowIndex The selected index
24368              * @param {Roo.data.Record} r The record
24369              */
24370             "rowselect" : true,
24371         /**
24372              * @event rowdeselect
24373              * Fires when a row is deselected.
24374              * @param {SelectionModel} this
24375              * @param {Number} rowIndex The selected index
24376              */
24377         "rowdeselect" : true
24378     });
24379     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24380     this.locked = false;
24381  };
24382
24383 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24384     /**
24385      * @cfg {Boolean} singleSelect
24386      * True to allow selection of only one row at a time (defaults to false)
24387      */
24388     singleSelect : false,
24389
24390     // private
24391     initEvents : function()
24392     {
24393
24394         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24395         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24396         //}else{ // allow click to work like normal
24397          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24398         //}
24399         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24400         this.grid.on("rowclick", this.handleMouseDown, this);
24401         
24402         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24403             "up" : function(e){
24404                 if(!e.shiftKey){
24405                     this.selectPrevious(e.shiftKey);
24406                 }else if(this.last !== false && this.lastActive !== false){
24407                     var last = this.last;
24408                     this.selectRange(this.last,  this.lastActive-1);
24409                     this.grid.getView().focusRow(this.lastActive);
24410                     if(last !== false){
24411                         this.last = last;
24412                     }
24413                 }else{
24414                     this.selectFirstRow();
24415                 }
24416                 this.fireEvent("afterselectionchange", this);
24417             },
24418             "down" : function(e){
24419                 if(!e.shiftKey){
24420                     this.selectNext(e.shiftKey);
24421                 }else if(this.last !== false && this.lastActive !== false){
24422                     var last = this.last;
24423                     this.selectRange(this.last,  this.lastActive+1);
24424                     this.grid.getView().focusRow(this.lastActive);
24425                     if(last !== false){
24426                         this.last = last;
24427                     }
24428                 }else{
24429                     this.selectFirstRow();
24430                 }
24431                 this.fireEvent("afterselectionchange", this);
24432             },
24433             scope: this
24434         });
24435         this.grid.store.on('load', function(){
24436             this.selections.clear();
24437         },this);
24438         /*
24439         var view = this.grid.view;
24440         view.on("refresh", this.onRefresh, this);
24441         view.on("rowupdated", this.onRowUpdated, this);
24442         view.on("rowremoved", this.onRemove, this);
24443         */
24444     },
24445
24446     // private
24447     onRefresh : function()
24448     {
24449         var ds = this.grid.store, i, v = this.grid.view;
24450         var s = this.selections;
24451         s.each(function(r){
24452             if((i = ds.indexOfId(r.id)) != -1){
24453                 v.onRowSelect(i);
24454             }else{
24455                 s.remove(r);
24456             }
24457         });
24458     },
24459
24460     // private
24461     onRemove : function(v, index, r){
24462         this.selections.remove(r);
24463     },
24464
24465     // private
24466     onRowUpdated : function(v, index, r){
24467         if(this.isSelected(r)){
24468             v.onRowSelect(index);
24469         }
24470     },
24471
24472     /**
24473      * Select records.
24474      * @param {Array} records The records to select
24475      * @param {Boolean} keepExisting (optional) True to keep existing selections
24476      */
24477     selectRecords : function(records, keepExisting)
24478     {
24479         if(!keepExisting){
24480             this.clearSelections();
24481         }
24482             var ds = this.grid.store;
24483         for(var i = 0, len = records.length; i < len; i++){
24484             this.selectRow(ds.indexOf(records[i]), true);
24485         }
24486     },
24487
24488     /**
24489      * Gets the number of selected rows.
24490      * @return {Number}
24491      */
24492     getCount : function(){
24493         return this.selections.length;
24494     },
24495
24496     /**
24497      * Selects the first row in the grid.
24498      */
24499     selectFirstRow : function(){
24500         this.selectRow(0);
24501     },
24502
24503     /**
24504      * Select the last row.
24505      * @param {Boolean} keepExisting (optional) True to keep existing selections
24506      */
24507     selectLastRow : function(keepExisting){
24508         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24509         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24510     },
24511
24512     /**
24513      * Selects the row immediately following the last selected row.
24514      * @param {Boolean} keepExisting (optional) True to keep existing selections
24515      */
24516     selectNext : function(keepExisting)
24517     {
24518             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24519             this.selectRow(this.last+1, keepExisting);
24520             this.grid.getView().focusRow(this.last);
24521         }
24522     },
24523
24524     /**
24525      * Selects the row that precedes the last selected row.
24526      * @param {Boolean} keepExisting (optional) True to keep existing selections
24527      */
24528     selectPrevious : function(keepExisting){
24529         if(this.last){
24530             this.selectRow(this.last-1, keepExisting);
24531             this.grid.getView().focusRow(this.last);
24532         }
24533     },
24534
24535     /**
24536      * Returns the selected records
24537      * @return {Array} Array of selected records
24538      */
24539     getSelections : function(){
24540         return [].concat(this.selections.items);
24541     },
24542
24543     /**
24544      * Returns the first selected record.
24545      * @return {Record}
24546      */
24547     getSelected : function(){
24548         return this.selections.itemAt(0);
24549     },
24550
24551
24552     /**
24553      * Clears all selections.
24554      */
24555     clearSelections : function(fast)
24556     {
24557         if(this.locked) {
24558             return;
24559         }
24560         if(fast !== true){
24561                 var ds = this.grid.store;
24562             var s = this.selections;
24563             s.each(function(r){
24564                 this.deselectRow(ds.indexOfId(r.id));
24565             }, this);
24566             s.clear();
24567         }else{
24568             this.selections.clear();
24569         }
24570         this.last = false;
24571     },
24572
24573
24574     /**
24575      * Selects all rows.
24576      */
24577     selectAll : function(){
24578         if(this.locked) {
24579             return;
24580         }
24581         this.selections.clear();
24582         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24583             this.selectRow(i, true);
24584         }
24585     },
24586
24587     /**
24588      * Returns True if there is a selection.
24589      * @return {Boolean}
24590      */
24591     hasSelection : function(){
24592         return this.selections.length > 0;
24593     },
24594
24595     /**
24596      * Returns True if the specified row is selected.
24597      * @param {Number/Record} record The record or index of the record to check
24598      * @return {Boolean}
24599      */
24600     isSelected : function(index){
24601             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24602         return (r && this.selections.key(r.id) ? true : false);
24603     },
24604
24605     /**
24606      * Returns True if the specified record id is selected.
24607      * @param {String} id The id of record to check
24608      * @return {Boolean}
24609      */
24610     isIdSelected : function(id){
24611         return (this.selections.key(id) ? true : false);
24612     },
24613
24614
24615     // private
24616     handleMouseDBClick : function(e, t){
24617         
24618     },
24619     // private
24620     handleMouseDown : function(e, t)
24621     {
24622             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24623         if(this.isLocked() || rowIndex < 0 ){
24624             return;
24625         };
24626         if(e.shiftKey && this.last !== false){
24627             var last = this.last;
24628             this.selectRange(last, rowIndex, e.ctrlKey);
24629             this.last = last; // reset the last
24630             t.focus();
24631     
24632         }else{
24633             var isSelected = this.isSelected(rowIndex);
24634             //Roo.log("select row:" + rowIndex);
24635             if(isSelected){
24636                 this.deselectRow(rowIndex);
24637             } else {
24638                         this.selectRow(rowIndex, true);
24639             }
24640     
24641             /*
24642                 if(e.button !== 0 && isSelected){
24643                 alert('rowIndex 2: ' + rowIndex);
24644                     view.focusRow(rowIndex);
24645                 }else if(e.ctrlKey && isSelected){
24646                     this.deselectRow(rowIndex);
24647                 }else if(!isSelected){
24648                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24649                     view.focusRow(rowIndex);
24650                 }
24651             */
24652         }
24653         this.fireEvent("afterselectionchange", this);
24654     },
24655     // private
24656     handleDragableRowClick :  function(grid, rowIndex, e) 
24657     {
24658         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24659             this.selectRow(rowIndex, false);
24660             grid.view.focusRow(rowIndex);
24661              this.fireEvent("afterselectionchange", this);
24662         }
24663     },
24664     
24665     /**
24666      * Selects multiple rows.
24667      * @param {Array} rows Array of the indexes of the row to select
24668      * @param {Boolean} keepExisting (optional) True to keep existing selections
24669      */
24670     selectRows : function(rows, keepExisting){
24671         if(!keepExisting){
24672             this.clearSelections();
24673         }
24674         for(var i = 0, len = rows.length; i < len; i++){
24675             this.selectRow(rows[i], true);
24676         }
24677     },
24678
24679     /**
24680      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24681      * @param {Number} startRow The index of the first row in the range
24682      * @param {Number} endRow The index of the last row in the range
24683      * @param {Boolean} keepExisting (optional) True to retain existing selections
24684      */
24685     selectRange : function(startRow, endRow, keepExisting){
24686         if(this.locked) {
24687             return;
24688         }
24689         if(!keepExisting){
24690             this.clearSelections();
24691         }
24692         if(startRow <= endRow){
24693             for(var i = startRow; i <= endRow; i++){
24694                 this.selectRow(i, true);
24695             }
24696         }else{
24697             for(var i = startRow; i >= endRow; i--){
24698                 this.selectRow(i, true);
24699             }
24700         }
24701     },
24702
24703     /**
24704      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24705      * @param {Number} startRow The index of the first row in the range
24706      * @param {Number} endRow The index of the last row in the range
24707      */
24708     deselectRange : function(startRow, endRow, preventViewNotify){
24709         if(this.locked) {
24710             return;
24711         }
24712         for(var i = startRow; i <= endRow; i++){
24713             this.deselectRow(i, preventViewNotify);
24714         }
24715     },
24716
24717     /**
24718      * Selects a row.
24719      * @param {Number} row The index of the row to select
24720      * @param {Boolean} keepExisting (optional) True to keep existing selections
24721      */
24722     selectRow : function(index, keepExisting, preventViewNotify)
24723     {
24724             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24725             return;
24726         }
24727         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24728             if(!keepExisting || this.singleSelect){
24729                 this.clearSelections();
24730             }
24731             
24732             var r = this.grid.store.getAt(index);
24733             //console.log('selectRow - record id :' + r.id);
24734             
24735             this.selections.add(r);
24736             this.last = this.lastActive = index;
24737             if(!preventViewNotify){
24738                 var proxy = new Roo.Element(
24739                                 this.grid.getRowDom(index)
24740                 );
24741                 proxy.addClass('bg-info info');
24742             }
24743             this.fireEvent("rowselect", this, index, r);
24744             this.fireEvent("selectionchange", this);
24745         }
24746     },
24747
24748     /**
24749      * Deselects a row.
24750      * @param {Number} row The index of the row to deselect
24751      */
24752     deselectRow : function(index, preventViewNotify)
24753     {
24754         if(this.locked) {
24755             return;
24756         }
24757         if(this.last == index){
24758             this.last = false;
24759         }
24760         if(this.lastActive == index){
24761             this.lastActive = false;
24762         }
24763         
24764         var r = this.grid.store.getAt(index);
24765         if (!r) {
24766             return;
24767         }
24768         
24769         this.selections.remove(r);
24770         //.console.log('deselectRow - record id :' + r.id);
24771         if(!preventViewNotify){
24772         
24773             var proxy = new Roo.Element(
24774                 this.grid.getRowDom(index)
24775             );
24776             proxy.removeClass('bg-info info');
24777         }
24778         this.fireEvent("rowdeselect", this, index);
24779         this.fireEvent("selectionchange", this);
24780     },
24781
24782     // private
24783     restoreLast : function(){
24784         if(this._last){
24785             this.last = this._last;
24786         }
24787     },
24788
24789     // private
24790     acceptsNav : function(row, col, cm){
24791         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24792     },
24793
24794     // private
24795     onEditorKey : function(field, e){
24796         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24797         if(k == e.TAB){
24798             e.stopEvent();
24799             ed.completeEdit();
24800             if(e.shiftKey){
24801                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24802             }else{
24803                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24804             }
24805         }else if(k == e.ENTER && !e.ctrlKey){
24806             e.stopEvent();
24807             ed.completeEdit();
24808             if(e.shiftKey){
24809                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24810             }else{
24811                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24812             }
24813         }else if(k == e.ESC){
24814             ed.cancelEdit();
24815         }
24816         if(newCell){
24817             g.startEditing(newCell[0], newCell[1]);
24818         }
24819     }
24820 });
24821 /*
24822  * Based on:
24823  * Ext JS Library 1.1.1
24824  * Copyright(c) 2006-2007, Ext JS, LLC.
24825  *
24826  * Originally Released Under LGPL - original licence link has changed is not relivant.
24827  *
24828  * Fork - LGPL
24829  * <script type="text/javascript">
24830  */
24831  
24832 /**
24833  * @class Roo.bootstrap.PagingToolbar
24834  * @extends Roo.bootstrap.NavSimplebar
24835  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24836  * @constructor
24837  * Create a new PagingToolbar
24838  * @param {Object} config The config object
24839  * @param {Roo.data.Store} store
24840  */
24841 Roo.bootstrap.PagingToolbar = function(config)
24842 {
24843     // old args format still supported... - xtype is prefered..
24844         // created from xtype...
24845     
24846     this.ds = config.dataSource;
24847     
24848     if (config.store && !this.ds) {
24849         this.store= Roo.factory(config.store, Roo.data);
24850         this.ds = this.store;
24851         this.ds.xmodule = this.xmodule || false;
24852     }
24853     
24854     this.toolbarItems = [];
24855     if (config.items) {
24856         this.toolbarItems = config.items;
24857     }
24858     
24859     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24860     
24861     this.cursor = 0;
24862     
24863     if (this.ds) { 
24864         this.bind(this.ds);
24865     }
24866     
24867     if (Roo.bootstrap.version == 4) {
24868         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24869     } else {
24870         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24871     }
24872     
24873 };
24874
24875 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24876     /**
24877      * @cfg {Roo.data.Store} dataSource
24878      * The underlying data store providing the paged data
24879      */
24880     /**
24881      * @cfg {String/HTMLElement/Element} container
24882      * container The id or element that will contain the toolbar
24883      */
24884     /**
24885      * @cfg {Boolean} displayInfo
24886      * True to display the displayMsg (defaults to false)
24887      */
24888     /**
24889      * @cfg {Number} pageSize
24890      * The number of records to display per page (defaults to 20)
24891      */
24892     pageSize: 20,
24893     /**
24894      * @cfg {String} displayMsg
24895      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24896      */
24897     displayMsg : 'Displaying {0} - {1} of {2}',
24898     /**
24899      * @cfg {String} emptyMsg
24900      * The message to display when no records are found (defaults to "No data to display")
24901      */
24902     emptyMsg : 'No data to display',
24903     /**
24904      * Customizable piece of the default paging text (defaults to "Page")
24905      * @type String
24906      */
24907     beforePageText : "Page",
24908     /**
24909      * Customizable piece of the default paging text (defaults to "of %0")
24910      * @type String
24911      */
24912     afterPageText : "of {0}",
24913     /**
24914      * Customizable piece of the default paging text (defaults to "First Page")
24915      * @type String
24916      */
24917     firstText : "First Page",
24918     /**
24919      * Customizable piece of the default paging text (defaults to "Previous Page")
24920      * @type String
24921      */
24922     prevText : "Previous Page",
24923     /**
24924      * Customizable piece of the default paging text (defaults to "Next Page")
24925      * @type String
24926      */
24927     nextText : "Next Page",
24928     /**
24929      * Customizable piece of the default paging text (defaults to "Last Page")
24930      * @type String
24931      */
24932     lastText : "Last Page",
24933     /**
24934      * Customizable piece of the default paging text (defaults to "Refresh")
24935      * @type String
24936      */
24937     refreshText : "Refresh",
24938
24939     buttons : false,
24940     // private
24941     onRender : function(ct, position) 
24942     {
24943         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24944         this.navgroup.parentId = this.id;
24945         this.navgroup.onRender(this.el, null);
24946         // add the buttons to the navgroup
24947         
24948         if(this.displayInfo){
24949             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24950             this.displayEl = this.el.select('.x-paging-info', true).first();
24951 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24952 //            this.displayEl = navel.el.select('span',true).first();
24953         }
24954         
24955         var _this = this;
24956         
24957         if(this.buttons){
24958             Roo.each(_this.buttons, function(e){ // this might need to use render????
24959                Roo.factory(e).render(_this.el);
24960             });
24961         }
24962             
24963         Roo.each(_this.toolbarItems, function(e) {
24964             _this.navgroup.addItem(e);
24965         });
24966         
24967         
24968         this.first = this.navgroup.addItem({
24969             tooltip: this.firstText,
24970             cls: "prev btn-outline-secondary",
24971             html : ' <i class="fa fa-step-backward"></i>',
24972             disabled: true,
24973             preventDefault: true,
24974             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24975         });
24976         
24977         this.prev =  this.navgroup.addItem({
24978             tooltip: this.prevText,
24979             cls: "prev btn-outline-secondary",
24980             html : ' <i class="fa fa-backward"></i>',
24981             disabled: true,
24982             preventDefault: true,
24983             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24984         });
24985     //this.addSeparator();
24986         
24987         
24988         var field = this.navgroup.addItem( {
24989             tagtype : 'span',
24990             cls : 'x-paging-position  btn-outline-secondary',
24991              disabled: true,
24992             html : this.beforePageText  +
24993                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24994                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24995          } ); //?? escaped?
24996         
24997         this.field = field.el.select('input', true).first();
24998         this.field.on("keydown", this.onPagingKeydown, this);
24999         this.field.on("focus", function(){this.dom.select();});
25000     
25001     
25002         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25003         //this.field.setHeight(18);
25004         //this.addSeparator();
25005         this.next = this.navgroup.addItem({
25006             tooltip: this.nextText,
25007             cls: "next btn-outline-secondary",
25008             html : ' <i class="fa fa-forward"></i>',
25009             disabled: true,
25010             preventDefault: true,
25011             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25012         });
25013         this.last = this.navgroup.addItem({
25014             tooltip: this.lastText,
25015             html : ' <i class="fa fa-step-forward"></i>',
25016             cls: "next btn-outline-secondary",
25017             disabled: true,
25018             preventDefault: true,
25019             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25020         });
25021     //this.addSeparator();
25022         this.loading = this.navgroup.addItem({
25023             tooltip: this.refreshText,
25024             cls: "btn-outline-secondary",
25025             html : ' <i class="fa fa-refresh"></i>',
25026             preventDefault: true,
25027             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25028         });
25029         
25030     },
25031
25032     // private
25033     updateInfo : function(){
25034         if(this.displayEl){
25035             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25036             var msg = count == 0 ?
25037                 this.emptyMsg :
25038                 String.format(
25039                     this.displayMsg,
25040                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25041                 );
25042             this.displayEl.update(msg);
25043         }
25044     },
25045
25046     // private
25047     onLoad : function(ds, r, o)
25048     {
25049         this.cursor = o.params.start ? o.params.start : 0;
25050         
25051         var d = this.getPageData(),
25052             ap = d.activePage,
25053             ps = d.pages;
25054         
25055         
25056         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25057         this.field.dom.value = ap;
25058         this.first.setDisabled(ap == 1);
25059         this.prev.setDisabled(ap == 1);
25060         this.next.setDisabled(ap == ps);
25061         this.last.setDisabled(ap == ps);
25062         this.loading.enable();
25063         this.updateInfo();
25064     },
25065
25066     // private
25067     getPageData : function(){
25068         var total = this.ds.getTotalCount();
25069         return {
25070             total : total,
25071             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25072             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25073         };
25074     },
25075
25076     // private
25077     onLoadError : function(){
25078         this.loading.enable();
25079     },
25080
25081     // private
25082     onPagingKeydown : function(e){
25083         var k = e.getKey();
25084         var d = this.getPageData();
25085         if(k == e.RETURN){
25086             var v = this.field.dom.value, pageNum;
25087             if(!v || isNaN(pageNum = parseInt(v, 10))){
25088                 this.field.dom.value = d.activePage;
25089                 return;
25090             }
25091             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25092             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25093             e.stopEvent();
25094         }
25095         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))
25096         {
25097           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25098           this.field.dom.value = pageNum;
25099           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25100           e.stopEvent();
25101         }
25102         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25103         {
25104           var v = this.field.dom.value, pageNum; 
25105           var increment = (e.shiftKey) ? 10 : 1;
25106           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25107                 increment *= -1;
25108           }
25109           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25110             this.field.dom.value = d.activePage;
25111             return;
25112           }
25113           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25114           {
25115             this.field.dom.value = parseInt(v, 10) + increment;
25116             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25117             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25118           }
25119           e.stopEvent();
25120         }
25121     },
25122
25123     // private
25124     beforeLoad : function(){
25125         if(this.loading){
25126             this.loading.disable();
25127         }
25128     },
25129
25130     // private
25131     onClick : function(which){
25132         
25133         var ds = this.ds;
25134         if (!ds) {
25135             return;
25136         }
25137         
25138         switch(which){
25139             case "first":
25140                 ds.load({params:{start: 0, limit: this.pageSize}});
25141             break;
25142             case "prev":
25143                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25144             break;
25145             case "next":
25146                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25147             break;
25148             case "last":
25149                 var total = ds.getTotalCount();
25150                 var extra = total % this.pageSize;
25151                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25152                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25153             break;
25154             case "refresh":
25155                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25156             break;
25157         }
25158     },
25159
25160     /**
25161      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25162      * @param {Roo.data.Store} store The data store to unbind
25163      */
25164     unbind : function(ds){
25165         ds.un("beforeload", this.beforeLoad, this);
25166         ds.un("load", this.onLoad, this);
25167         ds.un("loadexception", this.onLoadError, this);
25168         ds.un("remove", this.updateInfo, this);
25169         ds.un("add", this.updateInfo, this);
25170         this.ds = undefined;
25171     },
25172
25173     /**
25174      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25175      * @param {Roo.data.Store} store The data store to bind
25176      */
25177     bind : function(ds){
25178         ds.on("beforeload", this.beforeLoad, this);
25179         ds.on("load", this.onLoad, this);
25180         ds.on("loadexception", this.onLoadError, this);
25181         ds.on("remove", this.updateInfo, this);
25182         ds.on("add", this.updateInfo, this);
25183         this.ds = ds;
25184     }
25185 });/*
25186  * - LGPL
25187  *
25188  * element
25189  * 
25190  */
25191
25192 /**
25193  * @class Roo.bootstrap.MessageBar
25194  * @extends Roo.bootstrap.Component
25195  * Bootstrap MessageBar class
25196  * @cfg {String} html contents of the MessageBar
25197  * @cfg {String} weight (info | success | warning | danger) default info
25198  * @cfg {String} beforeClass insert the bar before the given class
25199  * @cfg {Boolean} closable (true | false) default false
25200  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25201  * 
25202  * @constructor
25203  * Create a new Element
25204  * @param {Object} config The config object
25205  */
25206
25207 Roo.bootstrap.MessageBar = function(config){
25208     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25209 };
25210
25211 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25212     
25213     html: '',
25214     weight: 'info',
25215     closable: false,
25216     fixed: false,
25217     beforeClass: 'bootstrap-sticky-wrap',
25218     
25219     getAutoCreate : function(){
25220         
25221         var cfg = {
25222             tag: 'div',
25223             cls: 'alert alert-dismissable alert-' + this.weight,
25224             cn: [
25225                 {
25226                     tag: 'span',
25227                     cls: 'message',
25228                     html: this.html || ''
25229                 }
25230             ]
25231         };
25232         
25233         if(this.fixed){
25234             cfg.cls += ' alert-messages-fixed';
25235         }
25236         
25237         if(this.closable){
25238             cfg.cn.push({
25239                 tag: 'button',
25240                 cls: 'close',
25241                 html: 'x'
25242             });
25243         }
25244         
25245         return cfg;
25246     },
25247     
25248     onRender : function(ct, position)
25249     {
25250         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25251         
25252         if(!this.el){
25253             var cfg = Roo.apply({},  this.getAutoCreate());
25254             cfg.id = Roo.id();
25255             
25256             if (this.cls) {
25257                 cfg.cls += ' ' + this.cls;
25258             }
25259             if (this.style) {
25260                 cfg.style = this.style;
25261             }
25262             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25263             
25264             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25265         }
25266         
25267         this.el.select('>button.close').on('click', this.hide, this);
25268         
25269     },
25270     
25271     show : function()
25272     {
25273         if (!this.rendered) {
25274             this.render();
25275         }
25276         
25277         this.el.show();
25278         
25279         this.fireEvent('show', this);
25280         
25281     },
25282     
25283     hide : function()
25284     {
25285         if (!this.rendered) {
25286             this.render();
25287         }
25288         
25289         this.el.hide();
25290         
25291         this.fireEvent('hide', this);
25292     },
25293     
25294     update : function()
25295     {
25296 //        var e = this.el.dom.firstChild;
25297 //        
25298 //        if(this.closable){
25299 //            e = e.nextSibling;
25300 //        }
25301 //        
25302 //        e.data = this.html || '';
25303
25304         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25305     }
25306    
25307 });
25308
25309  
25310
25311      /*
25312  * - LGPL
25313  *
25314  * Graph
25315  * 
25316  */
25317
25318
25319 /**
25320  * @class Roo.bootstrap.Graph
25321  * @extends Roo.bootstrap.Component
25322  * Bootstrap Graph class
25323 > Prameters
25324  -sm {number} sm 4
25325  -md {number} md 5
25326  @cfg {String} graphtype  bar | vbar | pie
25327  @cfg {number} g_x coodinator | centre x (pie)
25328  @cfg {number} g_y coodinator | centre y (pie)
25329  @cfg {number} g_r radius (pie)
25330  @cfg {number} g_height height of the chart (respected by all elements in the set)
25331  @cfg {number} g_width width of the chart (respected by all elements in the set)
25332  @cfg {Object} title The title of the chart
25333     
25334  -{Array}  values
25335  -opts (object) options for the chart 
25336      o {
25337      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25338      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25339      o vgutter (number)
25340      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.
25341      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25342      o to
25343      o stretch (boolean)
25344      o }
25345  -opts (object) options for the pie
25346      o{
25347      o cut
25348      o startAngle (number)
25349      o endAngle (number)
25350      } 
25351  *
25352  * @constructor
25353  * Create a new Input
25354  * @param {Object} config The config object
25355  */
25356
25357 Roo.bootstrap.Graph = function(config){
25358     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25359     
25360     this.addEvents({
25361         // img events
25362         /**
25363          * @event click
25364          * The img click event for the img.
25365          * @param {Roo.EventObject} e
25366          */
25367         "click" : true
25368     });
25369 };
25370
25371 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25372     
25373     sm: 4,
25374     md: 5,
25375     graphtype: 'bar',
25376     g_height: 250,
25377     g_width: 400,
25378     g_x: 50,
25379     g_y: 50,
25380     g_r: 30,
25381     opts:{
25382         //g_colors: this.colors,
25383         g_type: 'soft',
25384         g_gutter: '20%'
25385
25386     },
25387     title : false,
25388
25389     getAutoCreate : function(){
25390         
25391         var cfg = {
25392             tag: 'div',
25393             html : null
25394         };
25395         
25396         
25397         return  cfg;
25398     },
25399
25400     onRender : function(ct,position){
25401         
25402         
25403         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25404         
25405         if (typeof(Raphael) == 'undefined') {
25406             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25407             return;
25408         }
25409         
25410         this.raphael = Raphael(this.el.dom);
25411         
25412                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25413                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25414                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25415                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25416                 /*
25417                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25418                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25419                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25420                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25421                 
25422                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25423                 r.barchart(330, 10, 300, 220, data1);
25424                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25425                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25426                 */
25427                 
25428                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25429                 // r.barchart(30, 30, 560, 250,  xdata, {
25430                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25431                 //     axis : "0 0 1 1",
25432                 //     axisxlabels :  xdata
25433                 //     //yvalues : cols,
25434                    
25435                 // });
25436 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25437 //        
25438 //        this.load(null,xdata,{
25439 //                axis : "0 0 1 1",
25440 //                axisxlabels :  xdata
25441 //                });
25442
25443     },
25444
25445     load : function(graphtype,xdata,opts)
25446     {
25447         this.raphael.clear();
25448         if(!graphtype) {
25449             graphtype = this.graphtype;
25450         }
25451         if(!opts){
25452             opts = this.opts;
25453         }
25454         var r = this.raphael,
25455             fin = function () {
25456                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25457             },
25458             fout = function () {
25459                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25460             },
25461             pfin = function() {
25462                 this.sector.stop();
25463                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25464
25465                 if (this.label) {
25466                     this.label[0].stop();
25467                     this.label[0].attr({ r: 7.5 });
25468                     this.label[1].attr({ "font-weight": 800 });
25469                 }
25470             },
25471             pfout = function() {
25472                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25473
25474                 if (this.label) {
25475                     this.label[0].animate({ r: 5 }, 500, "bounce");
25476                     this.label[1].attr({ "font-weight": 400 });
25477                 }
25478             };
25479
25480         switch(graphtype){
25481             case 'bar':
25482                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25483                 break;
25484             case 'hbar':
25485                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25486                 break;
25487             case 'pie':
25488 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25489 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25490 //            
25491                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25492                 
25493                 break;
25494
25495         }
25496         
25497         if(this.title){
25498             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25499         }
25500         
25501     },
25502     
25503     setTitle: function(o)
25504     {
25505         this.title = o;
25506     },
25507     
25508     initEvents: function() {
25509         
25510         if(!this.href){
25511             this.el.on('click', this.onClick, this);
25512         }
25513     },
25514     
25515     onClick : function(e)
25516     {
25517         Roo.log('img onclick');
25518         this.fireEvent('click', this, e);
25519     }
25520    
25521 });
25522
25523  
25524 /*
25525  * - LGPL
25526  *
25527  * numberBox
25528  * 
25529  */
25530 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25531
25532 /**
25533  * @class Roo.bootstrap.dash.NumberBox
25534  * @extends Roo.bootstrap.Component
25535  * Bootstrap NumberBox class
25536  * @cfg {String} headline Box headline
25537  * @cfg {String} content Box content
25538  * @cfg {String} icon Box icon
25539  * @cfg {String} footer Footer text
25540  * @cfg {String} fhref Footer href
25541  * 
25542  * @constructor
25543  * Create a new NumberBox
25544  * @param {Object} config The config object
25545  */
25546
25547
25548 Roo.bootstrap.dash.NumberBox = function(config){
25549     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25550     
25551 };
25552
25553 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25554     
25555     headline : '',
25556     content : '',
25557     icon : '',
25558     footer : '',
25559     fhref : '',
25560     ficon : '',
25561     
25562     getAutoCreate : function(){
25563         
25564         var cfg = {
25565             tag : 'div',
25566             cls : 'small-box ',
25567             cn : [
25568                 {
25569                     tag : 'div',
25570                     cls : 'inner',
25571                     cn :[
25572                         {
25573                             tag : 'h3',
25574                             cls : 'roo-headline',
25575                             html : this.headline
25576                         },
25577                         {
25578                             tag : 'p',
25579                             cls : 'roo-content',
25580                             html : this.content
25581                         }
25582                     ]
25583                 }
25584             ]
25585         };
25586         
25587         if(this.icon){
25588             cfg.cn.push({
25589                 tag : 'div',
25590                 cls : 'icon',
25591                 cn :[
25592                     {
25593                         tag : 'i',
25594                         cls : 'ion ' + this.icon
25595                     }
25596                 ]
25597             });
25598         }
25599         
25600         if(this.footer){
25601             var footer = {
25602                 tag : 'a',
25603                 cls : 'small-box-footer',
25604                 href : this.fhref || '#',
25605                 html : this.footer
25606             };
25607             
25608             cfg.cn.push(footer);
25609             
25610         }
25611         
25612         return  cfg;
25613     },
25614
25615     onRender : function(ct,position){
25616         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25617
25618
25619        
25620                 
25621     },
25622
25623     setHeadline: function (value)
25624     {
25625         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25626     },
25627     
25628     setFooter: function (value, href)
25629     {
25630         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25631         
25632         if(href){
25633             this.el.select('a.small-box-footer',true).first().attr('href', href);
25634         }
25635         
25636     },
25637
25638     setContent: function (value)
25639     {
25640         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25641     },
25642
25643     initEvents: function() 
25644     {   
25645         
25646     }
25647     
25648 });
25649
25650  
25651 /*
25652  * - LGPL
25653  *
25654  * TabBox
25655  * 
25656  */
25657 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25658
25659 /**
25660  * @class Roo.bootstrap.dash.TabBox
25661  * @extends Roo.bootstrap.Component
25662  * Bootstrap TabBox class
25663  * @cfg {String} title Title of the TabBox
25664  * @cfg {String} icon Icon of the TabBox
25665  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25666  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25667  * 
25668  * @constructor
25669  * Create a new TabBox
25670  * @param {Object} config The config object
25671  */
25672
25673
25674 Roo.bootstrap.dash.TabBox = function(config){
25675     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25676     this.addEvents({
25677         // raw events
25678         /**
25679          * @event addpane
25680          * When a pane is added
25681          * @param {Roo.bootstrap.dash.TabPane} pane
25682          */
25683         "addpane" : true,
25684         /**
25685          * @event activatepane
25686          * When a pane is activated
25687          * @param {Roo.bootstrap.dash.TabPane} pane
25688          */
25689         "activatepane" : true
25690         
25691          
25692     });
25693     
25694     this.panes = [];
25695 };
25696
25697 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25698
25699     title : '',
25700     icon : false,
25701     showtabs : true,
25702     tabScrollable : false,
25703     
25704     getChildContainer : function()
25705     {
25706         return this.el.select('.tab-content', true).first();
25707     },
25708     
25709     getAutoCreate : function(){
25710         
25711         var header = {
25712             tag: 'li',
25713             cls: 'pull-left header',
25714             html: this.title,
25715             cn : []
25716         };
25717         
25718         if(this.icon){
25719             header.cn.push({
25720                 tag: 'i',
25721                 cls: 'fa ' + this.icon
25722             });
25723         }
25724         
25725         var h = {
25726             tag: 'ul',
25727             cls: 'nav nav-tabs pull-right',
25728             cn: [
25729                 header
25730             ]
25731         };
25732         
25733         if(this.tabScrollable){
25734             h = {
25735                 tag: 'div',
25736                 cls: 'tab-header',
25737                 cn: [
25738                     {
25739                         tag: 'ul',
25740                         cls: 'nav nav-tabs pull-right',
25741                         cn: [
25742                             header
25743                         ]
25744                     }
25745                 ]
25746             };
25747         }
25748         
25749         var cfg = {
25750             tag: 'div',
25751             cls: 'nav-tabs-custom',
25752             cn: [
25753                 h,
25754                 {
25755                     tag: 'div',
25756                     cls: 'tab-content no-padding',
25757                     cn: []
25758                 }
25759             ]
25760         };
25761
25762         return  cfg;
25763     },
25764     initEvents : function()
25765     {
25766         //Roo.log('add add pane handler');
25767         this.on('addpane', this.onAddPane, this);
25768     },
25769      /**
25770      * Updates the box title
25771      * @param {String} html to set the title to.
25772      */
25773     setTitle : function(value)
25774     {
25775         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25776     },
25777     onAddPane : function(pane)
25778     {
25779         this.panes.push(pane);
25780         //Roo.log('addpane');
25781         //Roo.log(pane);
25782         // tabs are rendere left to right..
25783         if(!this.showtabs){
25784             return;
25785         }
25786         
25787         var ctr = this.el.select('.nav-tabs', true).first();
25788          
25789          
25790         var existing = ctr.select('.nav-tab',true);
25791         var qty = existing.getCount();;
25792         
25793         
25794         var tab = ctr.createChild({
25795             tag : 'li',
25796             cls : 'nav-tab' + (qty ? '' : ' active'),
25797             cn : [
25798                 {
25799                     tag : 'a',
25800                     href:'#',
25801                     html : pane.title
25802                 }
25803             ]
25804         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25805         pane.tab = tab;
25806         
25807         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25808         if (!qty) {
25809             pane.el.addClass('active');
25810         }
25811         
25812                 
25813     },
25814     onTabClick : function(ev,un,ob,pane)
25815     {
25816         //Roo.log('tab - prev default');
25817         ev.preventDefault();
25818         
25819         
25820         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25821         pane.tab.addClass('active');
25822         //Roo.log(pane.title);
25823         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25824         // technically we should have a deactivate event.. but maybe add later.
25825         // and it should not de-activate the selected tab...
25826         this.fireEvent('activatepane', pane);
25827         pane.el.addClass('active');
25828         pane.fireEvent('activate');
25829         
25830         
25831     },
25832     
25833     getActivePane : function()
25834     {
25835         var r = false;
25836         Roo.each(this.panes, function(p) {
25837             if(p.el.hasClass('active')){
25838                 r = p;
25839                 return false;
25840             }
25841             
25842             return;
25843         });
25844         
25845         return r;
25846     }
25847     
25848     
25849 });
25850
25851  
25852 /*
25853  * - LGPL
25854  *
25855  * Tab pane
25856  * 
25857  */
25858 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25859 /**
25860  * @class Roo.bootstrap.TabPane
25861  * @extends Roo.bootstrap.Component
25862  * Bootstrap TabPane class
25863  * @cfg {Boolean} active (false | true) Default false
25864  * @cfg {String} title title of panel
25865
25866  * 
25867  * @constructor
25868  * Create a new TabPane
25869  * @param {Object} config The config object
25870  */
25871
25872 Roo.bootstrap.dash.TabPane = function(config){
25873     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25874     
25875     this.addEvents({
25876         // raw events
25877         /**
25878          * @event activate
25879          * When a pane is activated
25880          * @param {Roo.bootstrap.dash.TabPane} pane
25881          */
25882         "activate" : true
25883          
25884     });
25885 };
25886
25887 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25888     
25889     active : false,
25890     title : '',
25891     
25892     // the tabBox that this is attached to.
25893     tab : false,
25894      
25895     getAutoCreate : function() 
25896     {
25897         var cfg = {
25898             tag: 'div',
25899             cls: 'tab-pane'
25900         };
25901         
25902         if(this.active){
25903             cfg.cls += ' active';
25904         }
25905         
25906         return cfg;
25907     },
25908     initEvents  : function()
25909     {
25910         //Roo.log('trigger add pane handler');
25911         this.parent().fireEvent('addpane', this)
25912     },
25913     
25914      /**
25915      * Updates the tab title 
25916      * @param {String} html to set the title to.
25917      */
25918     setTitle: function(str)
25919     {
25920         if (!this.tab) {
25921             return;
25922         }
25923         this.title = str;
25924         this.tab.select('a', true).first().dom.innerHTML = str;
25925         
25926     }
25927     
25928     
25929     
25930 });
25931
25932  
25933
25934
25935  /*
25936  * - LGPL
25937  *
25938  * menu
25939  * 
25940  */
25941 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25942
25943 /**
25944  * @class Roo.bootstrap.menu.Menu
25945  * @extends Roo.bootstrap.Component
25946  * Bootstrap Menu class - container for Menu
25947  * @cfg {String} html Text of the menu
25948  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25949  * @cfg {String} icon Font awesome icon
25950  * @cfg {String} pos Menu align to (top | bottom) default bottom
25951  * 
25952  * 
25953  * @constructor
25954  * Create a new Menu
25955  * @param {Object} config The config object
25956  */
25957
25958
25959 Roo.bootstrap.menu.Menu = function(config){
25960     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25961     
25962     this.addEvents({
25963         /**
25964          * @event beforeshow
25965          * Fires before this menu is displayed
25966          * @param {Roo.bootstrap.menu.Menu} this
25967          */
25968         beforeshow : true,
25969         /**
25970          * @event beforehide
25971          * Fires before this menu is hidden
25972          * @param {Roo.bootstrap.menu.Menu} this
25973          */
25974         beforehide : true,
25975         /**
25976          * @event show
25977          * Fires after this menu is displayed
25978          * @param {Roo.bootstrap.menu.Menu} this
25979          */
25980         show : true,
25981         /**
25982          * @event hide
25983          * Fires after this menu is hidden
25984          * @param {Roo.bootstrap.menu.Menu} this
25985          */
25986         hide : true,
25987         /**
25988          * @event click
25989          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25990          * @param {Roo.bootstrap.menu.Menu} this
25991          * @param {Roo.EventObject} e
25992          */
25993         click : true
25994     });
25995     
25996 };
25997
25998 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25999     
26000     submenu : false,
26001     html : '',
26002     weight : 'default',
26003     icon : false,
26004     pos : 'bottom',
26005     
26006     
26007     getChildContainer : function() {
26008         if(this.isSubMenu){
26009             return this.el;
26010         }
26011         
26012         return this.el.select('ul.dropdown-menu', true).first();  
26013     },
26014     
26015     getAutoCreate : function()
26016     {
26017         var text = [
26018             {
26019                 tag : 'span',
26020                 cls : 'roo-menu-text',
26021                 html : this.html
26022             }
26023         ];
26024         
26025         if(this.icon){
26026             text.unshift({
26027                 tag : 'i',
26028                 cls : 'fa ' + this.icon
26029             })
26030         }
26031         
26032         
26033         var cfg = {
26034             tag : 'div',
26035             cls : 'btn-group',
26036             cn : [
26037                 {
26038                     tag : 'button',
26039                     cls : 'dropdown-button btn btn-' + this.weight,
26040                     cn : text
26041                 },
26042                 {
26043                     tag : 'button',
26044                     cls : 'dropdown-toggle btn btn-' + this.weight,
26045                     cn : [
26046                         {
26047                             tag : 'span',
26048                             cls : 'caret'
26049                         }
26050                     ]
26051                 },
26052                 {
26053                     tag : 'ul',
26054                     cls : 'dropdown-menu'
26055                 }
26056             ]
26057             
26058         };
26059         
26060         if(this.pos == 'top'){
26061             cfg.cls += ' dropup';
26062         }
26063         
26064         if(this.isSubMenu){
26065             cfg = {
26066                 tag : 'ul',
26067                 cls : 'dropdown-menu'
26068             }
26069         }
26070         
26071         return cfg;
26072     },
26073     
26074     onRender : function(ct, position)
26075     {
26076         this.isSubMenu = ct.hasClass('dropdown-submenu');
26077         
26078         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26079     },
26080     
26081     initEvents : function() 
26082     {
26083         if(this.isSubMenu){
26084             return;
26085         }
26086         
26087         this.hidden = true;
26088         
26089         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26090         this.triggerEl.on('click', this.onTriggerPress, this);
26091         
26092         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26093         this.buttonEl.on('click', this.onClick, this);
26094         
26095     },
26096     
26097     list : function()
26098     {
26099         if(this.isSubMenu){
26100             return this.el;
26101         }
26102         
26103         return this.el.select('ul.dropdown-menu', true).first();
26104     },
26105     
26106     onClick : function(e)
26107     {
26108         this.fireEvent("click", this, e);
26109     },
26110     
26111     onTriggerPress  : function(e)
26112     {   
26113         if (this.isVisible()) {
26114             this.hide();
26115         } else {
26116             this.show();
26117         }
26118     },
26119     
26120     isVisible : function(){
26121         return !this.hidden;
26122     },
26123     
26124     show : function()
26125     {
26126         this.fireEvent("beforeshow", this);
26127         
26128         this.hidden = false;
26129         this.el.addClass('open');
26130         
26131         Roo.get(document).on("mouseup", this.onMouseUp, this);
26132         
26133         this.fireEvent("show", this);
26134         
26135         
26136     },
26137     
26138     hide : function()
26139     {
26140         this.fireEvent("beforehide", this);
26141         
26142         this.hidden = true;
26143         this.el.removeClass('open');
26144         
26145         Roo.get(document).un("mouseup", this.onMouseUp);
26146         
26147         this.fireEvent("hide", this);
26148     },
26149     
26150     onMouseUp : function()
26151     {
26152         this.hide();
26153     }
26154     
26155 });
26156
26157  
26158  /*
26159  * - LGPL
26160  *
26161  * menu item
26162  * 
26163  */
26164 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26165
26166 /**
26167  * @class Roo.bootstrap.menu.Item
26168  * @extends Roo.bootstrap.Component
26169  * Bootstrap MenuItem class
26170  * @cfg {Boolean} submenu (true | false) default false
26171  * @cfg {String} html text of the item
26172  * @cfg {String} href the link
26173  * @cfg {Boolean} disable (true | false) default false
26174  * @cfg {Boolean} preventDefault (true | false) default true
26175  * @cfg {String} icon Font awesome icon
26176  * @cfg {String} pos Submenu align to (left | right) default right 
26177  * 
26178  * 
26179  * @constructor
26180  * Create a new Item
26181  * @param {Object} config The config object
26182  */
26183
26184
26185 Roo.bootstrap.menu.Item = function(config){
26186     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26187     this.addEvents({
26188         /**
26189          * @event mouseover
26190          * Fires when the mouse is hovering over this menu
26191          * @param {Roo.bootstrap.menu.Item} this
26192          * @param {Roo.EventObject} e
26193          */
26194         mouseover : true,
26195         /**
26196          * @event mouseout
26197          * Fires when the mouse exits this menu
26198          * @param {Roo.bootstrap.menu.Item} this
26199          * @param {Roo.EventObject} e
26200          */
26201         mouseout : true,
26202         // raw events
26203         /**
26204          * @event click
26205          * The raw click event for the entire grid.
26206          * @param {Roo.EventObject} e
26207          */
26208         click : true
26209     });
26210 };
26211
26212 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26213     
26214     submenu : false,
26215     href : '',
26216     html : '',
26217     preventDefault: true,
26218     disable : false,
26219     icon : false,
26220     pos : 'right',
26221     
26222     getAutoCreate : function()
26223     {
26224         var text = [
26225             {
26226                 tag : 'span',
26227                 cls : 'roo-menu-item-text',
26228                 html : this.html
26229             }
26230         ];
26231         
26232         if(this.icon){
26233             text.unshift({
26234                 tag : 'i',
26235                 cls : 'fa ' + this.icon
26236             })
26237         }
26238         
26239         var cfg = {
26240             tag : 'li',
26241             cn : [
26242                 {
26243                     tag : 'a',
26244                     href : this.href || '#',
26245                     cn : text
26246                 }
26247             ]
26248         };
26249         
26250         if(this.disable){
26251             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26252         }
26253         
26254         if(this.submenu){
26255             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26256             
26257             if(this.pos == 'left'){
26258                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26259             }
26260         }
26261         
26262         return cfg;
26263     },
26264     
26265     initEvents : function() 
26266     {
26267         this.el.on('mouseover', this.onMouseOver, this);
26268         this.el.on('mouseout', this.onMouseOut, this);
26269         
26270         this.el.select('a', true).first().on('click', this.onClick, this);
26271         
26272     },
26273     
26274     onClick : function(e)
26275     {
26276         if(this.preventDefault){
26277             e.preventDefault();
26278         }
26279         
26280         this.fireEvent("click", this, e);
26281     },
26282     
26283     onMouseOver : function(e)
26284     {
26285         if(this.submenu && this.pos == 'left'){
26286             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26287         }
26288         
26289         this.fireEvent("mouseover", this, e);
26290     },
26291     
26292     onMouseOut : function(e)
26293     {
26294         this.fireEvent("mouseout", this, e);
26295     }
26296 });
26297
26298  
26299
26300  /*
26301  * - LGPL
26302  *
26303  * menu separator
26304  * 
26305  */
26306 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26307
26308 /**
26309  * @class Roo.bootstrap.menu.Separator
26310  * @extends Roo.bootstrap.Component
26311  * Bootstrap Separator class
26312  * 
26313  * @constructor
26314  * Create a new Separator
26315  * @param {Object} config The config object
26316  */
26317
26318
26319 Roo.bootstrap.menu.Separator = function(config){
26320     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26321 };
26322
26323 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26324     
26325     getAutoCreate : function(){
26326         var cfg = {
26327             tag : 'li',
26328             cls: 'divider'
26329         };
26330         
26331         return cfg;
26332     }
26333    
26334 });
26335
26336  
26337
26338  /*
26339  * - LGPL
26340  *
26341  * Tooltip
26342  * 
26343  */
26344
26345 /**
26346  * @class Roo.bootstrap.Tooltip
26347  * Bootstrap Tooltip class
26348  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26349  * to determine which dom element triggers the tooltip.
26350  * 
26351  * It needs to add support for additional attributes like tooltip-position
26352  * 
26353  * @constructor
26354  * Create a new Toolti
26355  * @param {Object} config The config object
26356  */
26357
26358 Roo.bootstrap.Tooltip = function(config){
26359     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26360     
26361     this.alignment = Roo.bootstrap.Tooltip.alignment;
26362     
26363     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26364         this.alignment = config.alignment;
26365     }
26366     
26367 };
26368
26369 Roo.apply(Roo.bootstrap.Tooltip, {
26370     /**
26371      * @function init initialize tooltip monitoring.
26372      * @static
26373      */
26374     currentEl : false,
26375     currentTip : false,
26376     currentRegion : false,
26377     
26378     //  init : delay?
26379     
26380     init : function()
26381     {
26382         Roo.get(document).on('mouseover', this.enter ,this);
26383         Roo.get(document).on('mouseout', this.leave, this);
26384          
26385         
26386         this.currentTip = new Roo.bootstrap.Tooltip();
26387     },
26388     
26389     enter : function(ev)
26390     {
26391         var dom = ev.getTarget();
26392         
26393         //Roo.log(['enter',dom]);
26394         var el = Roo.fly(dom);
26395         if (this.currentEl) {
26396             //Roo.log(dom);
26397             //Roo.log(this.currentEl);
26398             //Roo.log(this.currentEl.contains(dom));
26399             if (this.currentEl == el) {
26400                 return;
26401             }
26402             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26403                 return;
26404             }
26405
26406         }
26407         
26408         if (this.currentTip.el) {
26409             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26410         }    
26411         //Roo.log(ev);
26412         
26413         if(!el || el.dom == document){
26414             return;
26415         }
26416         
26417         var bindEl = el;
26418         
26419         // you can not look for children, as if el is the body.. then everythign is the child..
26420         if (!el.attr('tooltip')) { //
26421             if (!el.select("[tooltip]").elements.length) {
26422                 return;
26423             }
26424             // is the mouse over this child...?
26425             bindEl = el.select("[tooltip]").first();
26426             var xy = ev.getXY();
26427             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26428                 //Roo.log("not in region.");
26429                 return;
26430             }
26431             //Roo.log("child element over..");
26432             
26433         }
26434         this.currentEl = bindEl;
26435         this.currentTip.bind(bindEl);
26436         this.currentRegion = Roo.lib.Region.getRegion(dom);
26437         this.currentTip.enter();
26438         
26439     },
26440     leave : function(ev)
26441     {
26442         var dom = ev.getTarget();
26443         //Roo.log(['leave',dom]);
26444         if (!this.currentEl) {
26445             return;
26446         }
26447         
26448         
26449         if (dom != this.currentEl.dom) {
26450             return;
26451         }
26452         var xy = ev.getXY();
26453         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26454             return;
26455         }
26456         // only activate leave if mouse cursor is outside... bounding box..
26457         
26458         
26459         
26460         
26461         if (this.currentTip) {
26462             this.currentTip.leave();
26463         }
26464         //Roo.log('clear currentEl');
26465         this.currentEl = false;
26466         
26467         
26468     },
26469     alignment : {
26470         'left' : ['r-l', [-2,0], 'right'],
26471         'right' : ['l-r', [2,0], 'left'],
26472         'bottom' : ['t-b', [0,2], 'top'],
26473         'top' : [ 'b-t', [0,-2], 'bottom']
26474     }
26475     
26476 });
26477
26478
26479 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26480     
26481     
26482     bindEl : false,
26483     
26484     delay : null, // can be { show : 300 , hide: 500}
26485     
26486     timeout : null,
26487     
26488     hoverState : null, //???
26489     
26490     placement : 'bottom', 
26491     
26492     alignment : false,
26493     
26494     getAutoCreate : function(){
26495     
26496         var cfg = {
26497            cls : 'tooltip',
26498            role : 'tooltip',
26499            cn : [
26500                 {
26501                     cls : 'tooltip-arrow'
26502                 },
26503                 {
26504                     cls : 'tooltip-inner'
26505                 }
26506            ]
26507         };
26508         
26509         return cfg;
26510     },
26511     bind : function(el)
26512     {
26513         this.bindEl = el;
26514     },
26515       
26516     
26517     enter : function () {
26518        
26519         if (this.timeout != null) {
26520             clearTimeout(this.timeout);
26521         }
26522         
26523         this.hoverState = 'in';
26524          //Roo.log("enter - show");
26525         if (!this.delay || !this.delay.show) {
26526             this.show();
26527             return;
26528         }
26529         var _t = this;
26530         this.timeout = setTimeout(function () {
26531             if (_t.hoverState == 'in') {
26532                 _t.show();
26533             }
26534         }, this.delay.show);
26535     },
26536     leave : function()
26537     {
26538         clearTimeout(this.timeout);
26539     
26540         this.hoverState = 'out';
26541          if (!this.delay || !this.delay.hide) {
26542             this.hide();
26543             return;
26544         }
26545        
26546         var _t = this;
26547         this.timeout = setTimeout(function () {
26548             //Roo.log("leave - timeout");
26549             
26550             if (_t.hoverState == 'out') {
26551                 _t.hide();
26552                 Roo.bootstrap.Tooltip.currentEl = false;
26553             }
26554         }, delay);
26555     },
26556     
26557     show : function (msg)
26558     {
26559         if (!this.el) {
26560             this.render(document.body);
26561         }
26562         // set content.
26563         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26564         
26565         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26566         
26567         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26568         
26569         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26570         
26571         var placement = typeof this.placement == 'function' ?
26572             this.placement.call(this, this.el, on_el) :
26573             this.placement;
26574             
26575         var autoToken = /\s?auto?\s?/i;
26576         var autoPlace = autoToken.test(placement);
26577         if (autoPlace) {
26578             placement = placement.replace(autoToken, '') || 'top';
26579         }
26580         
26581         //this.el.detach()
26582         //this.el.setXY([0,0]);
26583         this.el.show();
26584         //this.el.dom.style.display='block';
26585         
26586         //this.el.appendTo(on_el);
26587         
26588         var p = this.getPosition();
26589         var box = this.el.getBox();
26590         
26591         if (autoPlace) {
26592             // fixme..
26593         }
26594         
26595         var align = this.alignment[placement];
26596         
26597         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26598         
26599         if(placement == 'top' || placement == 'bottom'){
26600             if(xy[0] < 0){
26601                 placement = 'right';
26602             }
26603             
26604             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26605                 placement = 'left';
26606             }
26607             
26608             var scroll = Roo.select('body', true).first().getScroll();
26609             
26610             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26611                 placement = 'top';
26612             }
26613             
26614             align = this.alignment[placement];
26615         }
26616         
26617         this.el.alignTo(this.bindEl, align[0],align[1]);
26618         //var arrow = this.el.select('.arrow',true).first();
26619         //arrow.set(align[2], 
26620         
26621         this.el.addClass(placement);
26622         
26623         this.el.addClass('in fade');
26624         
26625         this.hoverState = null;
26626         
26627         if (this.el.hasClass('fade')) {
26628             // fade it?
26629         }
26630         
26631     },
26632     hide : function()
26633     {
26634          
26635         if (!this.el) {
26636             return;
26637         }
26638         //this.el.setXY([0,0]);
26639         this.el.removeClass('in');
26640         //this.el.hide();
26641         
26642     }
26643     
26644 });
26645  
26646
26647  /*
26648  * - LGPL
26649  *
26650  * Location Picker
26651  * 
26652  */
26653
26654 /**
26655  * @class Roo.bootstrap.LocationPicker
26656  * @extends Roo.bootstrap.Component
26657  * Bootstrap LocationPicker class
26658  * @cfg {Number} latitude Position when init default 0
26659  * @cfg {Number} longitude Position when init default 0
26660  * @cfg {Number} zoom default 15
26661  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26662  * @cfg {Boolean} mapTypeControl default false
26663  * @cfg {Boolean} disableDoubleClickZoom default false
26664  * @cfg {Boolean} scrollwheel default true
26665  * @cfg {Boolean} streetViewControl default false
26666  * @cfg {Number} radius default 0
26667  * @cfg {String} locationName
26668  * @cfg {Boolean} draggable default true
26669  * @cfg {Boolean} enableAutocomplete default false
26670  * @cfg {Boolean} enableReverseGeocode default true
26671  * @cfg {String} markerTitle
26672  * 
26673  * @constructor
26674  * Create a new LocationPicker
26675  * @param {Object} config The config object
26676  */
26677
26678
26679 Roo.bootstrap.LocationPicker = function(config){
26680     
26681     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26682     
26683     this.addEvents({
26684         /**
26685          * @event initial
26686          * Fires when the picker initialized.
26687          * @param {Roo.bootstrap.LocationPicker} this
26688          * @param {Google Location} location
26689          */
26690         initial : true,
26691         /**
26692          * @event positionchanged
26693          * Fires when the picker position changed.
26694          * @param {Roo.bootstrap.LocationPicker} this
26695          * @param {Google Location} location
26696          */
26697         positionchanged : true,
26698         /**
26699          * @event resize
26700          * Fires when the map resize.
26701          * @param {Roo.bootstrap.LocationPicker} this
26702          */
26703         resize : true,
26704         /**
26705          * @event show
26706          * Fires when the map show.
26707          * @param {Roo.bootstrap.LocationPicker} this
26708          */
26709         show : true,
26710         /**
26711          * @event hide
26712          * Fires when the map hide.
26713          * @param {Roo.bootstrap.LocationPicker} this
26714          */
26715         hide : true,
26716         /**
26717          * @event mapClick
26718          * Fires when click the map.
26719          * @param {Roo.bootstrap.LocationPicker} this
26720          * @param {Map event} e
26721          */
26722         mapClick : true,
26723         /**
26724          * @event mapRightClick
26725          * Fires when right click the map.
26726          * @param {Roo.bootstrap.LocationPicker} this
26727          * @param {Map event} e
26728          */
26729         mapRightClick : true,
26730         /**
26731          * @event markerClick
26732          * Fires when click the marker.
26733          * @param {Roo.bootstrap.LocationPicker} this
26734          * @param {Map event} e
26735          */
26736         markerClick : true,
26737         /**
26738          * @event markerRightClick
26739          * Fires when right click the marker.
26740          * @param {Roo.bootstrap.LocationPicker} this
26741          * @param {Map event} e
26742          */
26743         markerRightClick : true,
26744         /**
26745          * @event OverlayViewDraw
26746          * Fires when OverlayView Draw
26747          * @param {Roo.bootstrap.LocationPicker} this
26748          */
26749         OverlayViewDraw : true,
26750         /**
26751          * @event OverlayViewOnAdd
26752          * Fires when OverlayView Draw
26753          * @param {Roo.bootstrap.LocationPicker} this
26754          */
26755         OverlayViewOnAdd : true,
26756         /**
26757          * @event OverlayViewOnRemove
26758          * Fires when OverlayView Draw
26759          * @param {Roo.bootstrap.LocationPicker} this
26760          */
26761         OverlayViewOnRemove : true,
26762         /**
26763          * @event OverlayViewShow
26764          * Fires when OverlayView Draw
26765          * @param {Roo.bootstrap.LocationPicker} this
26766          * @param {Pixel} cpx
26767          */
26768         OverlayViewShow : true,
26769         /**
26770          * @event OverlayViewHide
26771          * Fires when OverlayView Draw
26772          * @param {Roo.bootstrap.LocationPicker} this
26773          */
26774         OverlayViewHide : true,
26775         /**
26776          * @event loadexception
26777          * Fires when load google lib failed.
26778          * @param {Roo.bootstrap.LocationPicker} this
26779          */
26780         loadexception : true
26781     });
26782         
26783 };
26784
26785 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26786     
26787     gMapContext: false,
26788     
26789     latitude: 0,
26790     longitude: 0,
26791     zoom: 15,
26792     mapTypeId: false,
26793     mapTypeControl: false,
26794     disableDoubleClickZoom: false,
26795     scrollwheel: true,
26796     streetViewControl: false,
26797     radius: 0,
26798     locationName: '',
26799     draggable: true,
26800     enableAutocomplete: false,
26801     enableReverseGeocode: true,
26802     markerTitle: '',
26803     
26804     getAutoCreate: function()
26805     {
26806
26807         var cfg = {
26808             tag: 'div',
26809             cls: 'roo-location-picker'
26810         };
26811         
26812         return cfg
26813     },
26814     
26815     initEvents: function(ct, position)
26816     {       
26817         if(!this.el.getWidth() || this.isApplied()){
26818             return;
26819         }
26820         
26821         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26822         
26823         this.initial();
26824     },
26825     
26826     initial: function()
26827     {
26828         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26829             this.fireEvent('loadexception', this);
26830             return;
26831         }
26832         
26833         if(!this.mapTypeId){
26834             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26835         }
26836         
26837         this.gMapContext = this.GMapContext();
26838         
26839         this.initOverlayView();
26840         
26841         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26842         
26843         var _this = this;
26844                 
26845         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26846             _this.setPosition(_this.gMapContext.marker.position);
26847         });
26848         
26849         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26850             _this.fireEvent('mapClick', this, event);
26851             
26852         });
26853
26854         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26855             _this.fireEvent('mapRightClick', this, event);
26856             
26857         });
26858         
26859         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26860             _this.fireEvent('markerClick', this, event);
26861             
26862         });
26863
26864         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26865             _this.fireEvent('markerRightClick', this, event);
26866             
26867         });
26868         
26869         this.setPosition(this.gMapContext.location);
26870         
26871         this.fireEvent('initial', this, this.gMapContext.location);
26872     },
26873     
26874     initOverlayView: function()
26875     {
26876         var _this = this;
26877         
26878         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26879             
26880             draw: function()
26881             {
26882                 _this.fireEvent('OverlayViewDraw', _this);
26883             },
26884             
26885             onAdd: function()
26886             {
26887                 _this.fireEvent('OverlayViewOnAdd', _this);
26888             },
26889             
26890             onRemove: function()
26891             {
26892                 _this.fireEvent('OverlayViewOnRemove', _this);
26893             },
26894             
26895             show: function(cpx)
26896             {
26897                 _this.fireEvent('OverlayViewShow', _this, cpx);
26898             },
26899             
26900             hide: function()
26901             {
26902                 _this.fireEvent('OverlayViewHide', _this);
26903             }
26904             
26905         });
26906     },
26907     
26908     fromLatLngToContainerPixel: function(event)
26909     {
26910         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26911     },
26912     
26913     isApplied: function() 
26914     {
26915         return this.getGmapContext() == false ? false : true;
26916     },
26917     
26918     getGmapContext: function() 
26919     {
26920         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26921     },
26922     
26923     GMapContext: function() 
26924     {
26925         var position = new google.maps.LatLng(this.latitude, this.longitude);
26926         
26927         var _map = new google.maps.Map(this.el.dom, {
26928             center: position,
26929             zoom: this.zoom,
26930             mapTypeId: this.mapTypeId,
26931             mapTypeControl: this.mapTypeControl,
26932             disableDoubleClickZoom: this.disableDoubleClickZoom,
26933             scrollwheel: this.scrollwheel,
26934             streetViewControl: this.streetViewControl,
26935             locationName: this.locationName,
26936             draggable: this.draggable,
26937             enableAutocomplete: this.enableAutocomplete,
26938             enableReverseGeocode: this.enableReverseGeocode
26939         });
26940         
26941         var _marker = new google.maps.Marker({
26942             position: position,
26943             map: _map,
26944             title: this.markerTitle,
26945             draggable: this.draggable
26946         });
26947         
26948         return {
26949             map: _map,
26950             marker: _marker,
26951             circle: null,
26952             location: position,
26953             radius: this.radius,
26954             locationName: this.locationName,
26955             addressComponents: {
26956                 formatted_address: null,
26957                 addressLine1: null,
26958                 addressLine2: null,
26959                 streetName: null,
26960                 streetNumber: null,
26961                 city: null,
26962                 district: null,
26963                 state: null,
26964                 stateOrProvince: null
26965             },
26966             settings: this,
26967             domContainer: this.el.dom,
26968             geodecoder: new google.maps.Geocoder()
26969         };
26970     },
26971     
26972     drawCircle: function(center, radius, options) 
26973     {
26974         if (this.gMapContext.circle != null) {
26975             this.gMapContext.circle.setMap(null);
26976         }
26977         if (radius > 0) {
26978             radius *= 1;
26979             options = Roo.apply({}, options, {
26980                 strokeColor: "#0000FF",
26981                 strokeOpacity: .35,
26982                 strokeWeight: 2,
26983                 fillColor: "#0000FF",
26984                 fillOpacity: .2
26985             });
26986             
26987             options.map = this.gMapContext.map;
26988             options.radius = radius;
26989             options.center = center;
26990             this.gMapContext.circle = new google.maps.Circle(options);
26991             return this.gMapContext.circle;
26992         }
26993         
26994         return null;
26995     },
26996     
26997     setPosition: function(location) 
26998     {
26999         this.gMapContext.location = location;
27000         this.gMapContext.marker.setPosition(location);
27001         this.gMapContext.map.panTo(location);
27002         this.drawCircle(location, this.gMapContext.radius, {});
27003         
27004         var _this = this;
27005         
27006         if (this.gMapContext.settings.enableReverseGeocode) {
27007             this.gMapContext.geodecoder.geocode({
27008                 latLng: this.gMapContext.location
27009             }, function(results, status) {
27010                 
27011                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27012                     _this.gMapContext.locationName = results[0].formatted_address;
27013                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27014                     
27015                     _this.fireEvent('positionchanged', this, location);
27016                 }
27017             });
27018             
27019             return;
27020         }
27021         
27022         this.fireEvent('positionchanged', this, location);
27023     },
27024     
27025     resize: function()
27026     {
27027         google.maps.event.trigger(this.gMapContext.map, "resize");
27028         
27029         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27030         
27031         this.fireEvent('resize', this);
27032     },
27033     
27034     setPositionByLatLng: function(latitude, longitude)
27035     {
27036         this.setPosition(new google.maps.LatLng(latitude, longitude));
27037     },
27038     
27039     getCurrentPosition: function() 
27040     {
27041         return {
27042             latitude: this.gMapContext.location.lat(),
27043             longitude: this.gMapContext.location.lng()
27044         };
27045     },
27046     
27047     getAddressName: function() 
27048     {
27049         return this.gMapContext.locationName;
27050     },
27051     
27052     getAddressComponents: function() 
27053     {
27054         return this.gMapContext.addressComponents;
27055     },
27056     
27057     address_component_from_google_geocode: function(address_components) 
27058     {
27059         var result = {};
27060         
27061         for (var i = 0; i < address_components.length; i++) {
27062             var component = address_components[i];
27063             if (component.types.indexOf("postal_code") >= 0) {
27064                 result.postalCode = component.short_name;
27065             } else if (component.types.indexOf("street_number") >= 0) {
27066                 result.streetNumber = component.short_name;
27067             } else if (component.types.indexOf("route") >= 0) {
27068                 result.streetName = component.short_name;
27069             } else if (component.types.indexOf("neighborhood") >= 0) {
27070                 result.city = component.short_name;
27071             } else if (component.types.indexOf("locality") >= 0) {
27072                 result.city = component.short_name;
27073             } else if (component.types.indexOf("sublocality") >= 0) {
27074                 result.district = component.short_name;
27075             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27076                 result.stateOrProvince = component.short_name;
27077             } else if (component.types.indexOf("country") >= 0) {
27078                 result.country = component.short_name;
27079             }
27080         }
27081         
27082         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27083         result.addressLine2 = "";
27084         return result;
27085     },
27086     
27087     setZoomLevel: function(zoom)
27088     {
27089         this.gMapContext.map.setZoom(zoom);
27090     },
27091     
27092     show: function()
27093     {
27094         if(!this.el){
27095             return;
27096         }
27097         
27098         this.el.show();
27099         
27100         this.resize();
27101         
27102         this.fireEvent('show', this);
27103     },
27104     
27105     hide: function()
27106     {
27107         if(!this.el){
27108             return;
27109         }
27110         
27111         this.el.hide();
27112         
27113         this.fireEvent('hide', this);
27114     }
27115     
27116 });
27117
27118 Roo.apply(Roo.bootstrap.LocationPicker, {
27119     
27120     OverlayView : function(map, options)
27121     {
27122         options = options || {};
27123         
27124         this.setMap(map);
27125     }
27126     
27127     
27128 });/*
27129  * - LGPL
27130  *
27131  * Alert
27132  * 
27133  */
27134
27135 /**
27136  * @class Roo.bootstrap.Alert
27137  * @extends Roo.bootstrap.Component
27138  * Bootstrap Alert class
27139  * @cfg {String} title The title of alert
27140  * @cfg {String} html The content of alert
27141  * @cfg {String} weight (  success | info | warning | danger )
27142  * @cfg {String} faicon font-awesomeicon
27143  * 
27144  * @constructor
27145  * Create a new alert
27146  * @param {Object} config The config object
27147  */
27148
27149
27150 Roo.bootstrap.Alert = function(config){
27151     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27152     
27153 };
27154
27155 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27156     
27157     title: '',
27158     html: '',
27159     weight: false,
27160     faicon: false,
27161     
27162     getAutoCreate : function()
27163     {
27164         
27165         var cfg = {
27166             tag : 'div',
27167             cls : 'alert',
27168             cn : [
27169                 {
27170                     tag : 'i',
27171                     cls : 'roo-alert-icon'
27172                     
27173                 },
27174                 {
27175                     tag : 'b',
27176                     cls : 'roo-alert-title',
27177                     html : this.title
27178                 },
27179                 {
27180                     tag : 'span',
27181                     cls : 'roo-alert-text',
27182                     html : this.html
27183                 }
27184             ]
27185         };
27186         
27187         if(this.faicon){
27188             cfg.cn[0].cls += ' fa ' + this.faicon;
27189         }
27190         
27191         if(this.weight){
27192             cfg.cls += ' alert-' + this.weight;
27193         }
27194         
27195         return cfg;
27196     },
27197     
27198     initEvents: function() 
27199     {
27200         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27201     },
27202     
27203     setTitle : function(str)
27204     {
27205         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27206     },
27207     
27208     setText : function(str)
27209     {
27210         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27211     },
27212     
27213     setWeight : function(weight)
27214     {
27215         if(this.weight){
27216             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27217         }
27218         
27219         this.weight = weight;
27220         
27221         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27222     },
27223     
27224     setIcon : function(icon)
27225     {
27226         if(this.faicon){
27227             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27228         }
27229         
27230         this.faicon = icon;
27231         
27232         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27233     },
27234     
27235     hide: function() 
27236     {
27237         this.el.hide();   
27238     },
27239     
27240     show: function() 
27241     {  
27242         this.el.show();   
27243     }
27244     
27245 });
27246
27247  
27248 /*
27249 * Licence: LGPL
27250 */
27251
27252 /**
27253  * @class Roo.bootstrap.UploadCropbox
27254  * @extends Roo.bootstrap.Component
27255  * Bootstrap UploadCropbox class
27256  * @cfg {String} emptyText show when image has been loaded
27257  * @cfg {String} rotateNotify show when image too small to rotate
27258  * @cfg {Number} errorTimeout default 3000
27259  * @cfg {Number} minWidth default 300
27260  * @cfg {Number} minHeight default 300
27261  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27262  * @cfg {Boolean} isDocument (true|false) default false
27263  * @cfg {String} url action url
27264  * @cfg {String} paramName default 'imageUpload'
27265  * @cfg {String} method default POST
27266  * @cfg {Boolean} loadMask (true|false) default true
27267  * @cfg {Boolean} loadingText default 'Loading...'
27268  * 
27269  * @constructor
27270  * Create a new UploadCropbox
27271  * @param {Object} config The config object
27272  */
27273
27274 Roo.bootstrap.UploadCropbox = function(config){
27275     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27276     
27277     this.addEvents({
27278         /**
27279          * @event beforeselectfile
27280          * Fire before select file
27281          * @param {Roo.bootstrap.UploadCropbox} this
27282          */
27283         "beforeselectfile" : true,
27284         /**
27285          * @event initial
27286          * Fire after initEvent
27287          * @param {Roo.bootstrap.UploadCropbox} this
27288          */
27289         "initial" : true,
27290         /**
27291          * @event crop
27292          * Fire after initEvent
27293          * @param {Roo.bootstrap.UploadCropbox} this
27294          * @param {String} data
27295          */
27296         "crop" : true,
27297         /**
27298          * @event prepare
27299          * Fire when preparing the file data
27300          * @param {Roo.bootstrap.UploadCropbox} this
27301          * @param {Object} file
27302          */
27303         "prepare" : true,
27304         /**
27305          * @event exception
27306          * Fire when get exception
27307          * @param {Roo.bootstrap.UploadCropbox} this
27308          * @param {XMLHttpRequest} xhr
27309          */
27310         "exception" : true,
27311         /**
27312          * @event beforeloadcanvas
27313          * Fire before load the canvas
27314          * @param {Roo.bootstrap.UploadCropbox} this
27315          * @param {String} src
27316          */
27317         "beforeloadcanvas" : true,
27318         /**
27319          * @event trash
27320          * Fire when trash image
27321          * @param {Roo.bootstrap.UploadCropbox} this
27322          */
27323         "trash" : true,
27324         /**
27325          * @event download
27326          * Fire when download the image
27327          * @param {Roo.bootstrap.UploadCropbox} this
27328          */
27329         "download" : true,
27330         /**
27331          * @event footerbuttonclick
27332          * Fire when footerbuttonclick
27333          * @param {Roo.bootstrap.UploadCropbox} this
27334          * @param {String} type
27335          */
27336         "footerbuttonclick" : true,
27337         /**
27338          * @event resize
27339          * Fire when resize
27340          * @param {Roo.bootstrap.UploadCropbox} this
27341          */
27342         "resize" : true,
27343         /**
27344          * @event rotate
27345          * Fire when rotate the image
27346          * @param {Roo.bootstrap.UploadCropbox} this
27347          * @param {String} pos
27348          */
27349         "rotate" : true,
27350         /**
27351          * @event inspect
27352          * Fire when inspect the file
27353          * @param {Roo.bootstrap.UploadCropbox} this
27354          * @param {Object} file
27355          */
27356         "inspect" : true,
27357         /**
27358          * @event upload
27359          * Fire when xhr upload the file
27360          * @param {Roo.bootstrap.UploadCropbox} this
27361          * @param {Object} data
27362          */
27363         "upload" : true,
27364         /**
27365          * @event arrange
27366          * Fire when arrange the file data
27367          * @param {Roo.bootstrap.UploadCropbox} this
27368          * @param {Object} formData
27369          */
27370         "arrange" : true
27371     });
27372     
27373     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27374 };
27375
27376 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27377     
27378     emptyText : 'Click to upload image',
27379     rotateNotify : 'Image is too small to rotate',
27380     errorTimeout : 3000,
27381     scale : 0,
27382     baseScale : 1,
27383     rotate : 0,
27384     dragable : false,
27385     pinching : false,
27386     mouseX : 0,
27387     mouseY : 0,
27388     cropData : false,
27389     minWidth : 300,
27390     minHeight : 300,
27391     file : false,
27392     exif : {},
27393     baseRotate : 1,
27394     cropType : 'image/jpeg',
27395     buttons : false,
27396     canvasLoaded : false,
27397     isDocument : false,
27398     method : 'POST',
27399     paramName : 'imageUpload',
27400     loadMask : true,
27401     loadingText : 'Loading...',
27402     maskEl : false,
27403     
27404     getAutoCreate : function()
27405     {
27406         var cfg = {
27407             tag : 'div',
27408             cls : 'roo-upload-cropbox',
27409             cn : [
27410                 {
27411                     tag : 'input',
27412                     cls : 'roo-upload-cropbox-selector',
27413                     type : 'file'
27414                 },
27415                 {
27416                     tag : 'div',
27417                     cls : 'roo-upload-cropbox-body',
27418                     style : 'cursor:pointer',
27419                     cn : [
27420                         {
27421                             tag : 'div',
27422                             cls : 'roo-upload-cropbox-preview'
27423                         },
27424                         {
27425                             tag : 'div',
27426                             cls : 'roo-upload-cropbox-thumb'
27427                         },
27428                         {
27429                             tag : 'div',
27430                             cls : 'roo-upload-cropbox-empty-notify',
27431                             html : this.emptyText
27432                         },
27433                         {
27434                             tag : 'div',
27435                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27436                             html : this.rotateNotify
27437                         }
27438                     ]
27439                 },
27440                 {
27441                     tag : 'div',
27442                     cls : 'roo-upload-cropbox-footer',
27443                     cn : {
27444                         tag : 'div',
27445                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27446                         cn : []
27447                     }
27448                 }
27449             ]
27450         };
27451         
27452         return cfg;
27453     },
27454     
27455     onRender : function(ct, position)
27456     {
27457         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27458         
27459         if (this.buttons.length) {
27460             
27461             Roo.each(this.buttons, function(bb) {
27462                 
27463                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27464                 
27465                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27466                 
27467             }, this);
27468         }
27469         
27470         if(this.loadMask){
27471             this.maskEl = this.el;
27472         }
27473     },
27474     
27475     initEvents : function()
27476     {
27477         this.urlAPI = (window.createObjectURL && window) || 
27478                                 (window.URL && URL.revokeObjectURL && URL) || 
27479                                 (window.webkitURL && webkitURL);
27480                         
27481         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27482         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27483         
27484         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27485         this.selectorEl.hide();
27486         
27487         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27488         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27489         
27490         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27491         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27492         this.thumbEl.hide();
27493         
27494         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27495         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27496         
27497         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27498         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27499         this.errorEl.hide();
27500         
27501         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27502         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27503         this.footerEl.hide();
27504         
27505         this.setThumbBoxSize();
27506         
27507         this.bind();
27508         
27509         this.resize();
27510         
27511         this.fireEvent('initial', this);
27512     },
27513
27514     bind : function()
27515     {
27516         var _this = this;
27517         
27518         window.addEventListener("resize", function() { _this.resize(); } );
27519         
27520         this.bodyEl.on('click', this.beforeSelectFile, this);
27521         
27522         if(Roo.isTouch){
27523             this.bodyEl.on('touchstart', this.onTouchStart, this);
27524             this.bodyEl.on('touchmove', this.onTouchMove, this);
27525             this.bodyEl.on('touchend', this.onTouchEnd, this);
27526         }
27527         
27528         if(!Roo.isTouch){
27529             this.bodyEl.on('mousedown', this.onMouseDown, this);
27530             this.bodyEl.on('mousemove', this.onMouseMove, this);
27531             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27532             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27533             Roo.get(document).on('mouseup', this.onMouseUp, this);
27534         }
27535         
27536         this.selectorEl.on('change', this.onFileSelected, this);
27537     },
27538     
27539     reset : function()
27540     {    
27541         this.scale = 0;
27542         this.baseScale = 1;
27543         this.rotate = 0;
27544         this.baseRotate = 1;
27545         this.dragable = false;
27546         this.pinching = false;
27547         this.mouseX = 0;
27548         this.mouseY = 0;
27549         this.cropData = false;
27550         this.notifyEl.dom.innerHTML = this.emptyText;
27551         
27552         this.selectorEl.dom.value = '';
27553         
27554     },
27555     
27556     resize : function()
27557     {
27558         if(this.fireEvent('resize', this) != false){
27559             this.setThumbBoxPosition();
27560             this.setCanvasPosition();
27561         }
27562     },
27563     
27564     onFooterButtonClick : function(e, el, o, type)
27565     {
27566         switch (type) {
27567             case 'rotate-left' :
27568                 this.onRotateLeft(e);
27569                 break;
27570             case 'rotate-right' :
27571                 this.onRotateRight(e);
27572                 break;
27573             case 'picture' :
27574                 this.beforeSelectFile(e);
27575                 break;
27576             case 'trash' :
27577                 this.trash(e);
27578                 break;
27579             case 'crop' :
27580                 this.crop(e);
27581                 break;
27582             case 'download' :
27583                 this.download(e);
27584                 break;
27585             default :
27586                 break;
27587         }
27588         
27589         this.fireEvent('footerbuttonclick', this, type);
27590     },
27591     
27592     beforeSelectFile : function(e)
27593     {
27594         e.preventDefault();
27595         
27596         if(this.fireEvent('beforeselectfile', this) != false){
27597             this.selectorEl.dom.click();
27598         }
27599     },
27600     
27601     onFileSelected : function(e)
27602     {
27603         e.preventDefault();
27604         
27605         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27606             return;
27607         }
27608         
27609         var file = this.selectorEl.dom.files[0];
27610         
27611         if(this.fireEvent('inspect', this, file) != false){
27612             this.prepare(file);
27613         }
27614         
27615     },
27616     
27617     trash : function(e)
27618     {
27619         this.fireEvent('trash', this);
27620     },
27621     
27622     download : function(e)
27623     {
27624         this.fireEvent('download', this);
27625     },
27626     
27627     loadCanvas : function(src)
27628     {   
27629         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27630             
27631             this.reset();
27632             
27633             this.imageEl = document.createElement('img');
27634             
27635             var _this = this;
27636             
27637             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27638             
27639             this.imageEl.src = src;
27640         }
27641     },
27642     
27643     onLoadCanvas : function()
27644     {   
27645         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27646         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27647         
27648         this.bodyEl.un('click', this.beforeSelectFile, this);
27649         
27650         this.notifyEl.hide();
27651         this.thumbEl.show();
27652         this.footerEl.show();
27653         
27654         this.baseRotateLevel();
27655         
27656         if(this.isDocument){
27657             this.setThumbBoxSize();
27658         }
27659         
27660         this.setThumbBoxPosition();
27661         
27662         this.baseScaleLevel();
27663         
27664         this.draw();
27665         
27666         this.resize();
27667         
27668         this.canvasLoaded = true;
27669         
27670         if(this.loadMask){
27671             this.maskEl.unmask();
27672         }
27673         
27674     },
27675     
27676     setCanvasPosition : function()
27677     {   
27678         if(!this.canvasEl){
27679             return;
27680         }
27681         
27682         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27683         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27684         
27685         this.previewEl.setLeft(pw);
27686         this.previewEl.setTop(ph);
27687         
27688     },
27689     
27690     onMouseDown : function(e)
27691     {   
27692         e.stopEvent();
27693         
27694         this.dragable = true;
27695         this.pinching = false;
27696         
27697         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27698             this.dragable = false;
27699             return;
27700         }
27701         
27702         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27703         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27704         
27705     },
27706     
27707     onMouseMove : function(e)
27708     {   
27709         e.stopEvent();
27710         
27711         if(!this.canvasLoaded){
27712             return;
27713         }
27714         
27715         if (!this.dragable){
27716             return;
27717         }
27718         
27719         var minX = Math.ceil(this.thumbEl.getLeft(true));
27720         var minY = Math.ceil(this.thumbEl.getTop(true));
27721         
27722         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27723         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27724         
27725         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27726         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27727         
27728         x = x - this.mouseX;
27729         y = y - this.mouseY;
27730         
27731         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27732         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27733         
27734         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27735         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27736         
27737         this.previewEl.setLeft(bgX);
27738         this.previewEl.setTop(bgY);
27739         
27740         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27741         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27742     },
27743     
27744     onMouseUp : function(e)
27745     {   
27746         e.stopEvent();
27747         
27748         this.dragable = false;
27749     },
27750     
27751     onMouseWheel : function(e)
27752     {   
27753         e.stopEvent();
27754         
27755         this.startScale = this.scale;
27756         
27757         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27758         
27759         if(!this.zoomable()){
27760             this.scale = this.startScale;
27761             return;
27762         }
27763         
27764         this.draw();
27765         
27766         return;
27767     },
27768     
27769     zoomable : function()
27770     {
27771         var minScale = this.thumbEl.getWidth() / this.minWidth;
27772         
27773         if(this.minWidth < this.minHeight){
27774             minScale = this.thumbEl.getHeight() / this.minHeight;
27775         }
27776         
27777         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27778         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27779         
27780         if(
27781                 this.isDocument &&
27782                 (this.rotate == 0 || this.rotate == 180) && 
27783                 (
27784                     width > this.imageEl.OriginWidth || 
27785                     height > this.imageEl.OriginHeight ||
27786                     (width < this.minWidth && height < this.minHeight)
27787                 )
27788         ){
27789             return false;
27790         }
27791         
27792         if(
27793                 this.isDocument &&
27794                 (this.rotate == 90 || this.rotate == 270) && 
27795                 (
27796                     width > this.imageEl.OriginWidth || 
27797                     height > this.imageEl.OriginHeight ||
27798                     (width < this.minHeight && height < this.minWidth)
27799                 )
27800         ){
27801             return false;
27802         }
27803         
27804         if(
27805                 !this.isDocument &&
27806                 (this.rotate == 0 || this.rotate == 180) && 
27807                 (
27808                     width < this.minWidth || 
27809                     width > this.imageEl.OriginWidth || 
27810                     height < this.minHeight || 
27811                     height > this.imageEl.OriginHeight
27812                 )
27813         ){
27814             return false;
27815         }
27816         
27817         if(
27818                 !this.isDocument &&
27819                 (this.rotate == 90 || this.rotate == 270) && 
27820                 (
27821                     width < this.minHeight || 
27822                     width > this.imageEl.OriginWidth || 
27823                     height < this.minWidth || 
27824                     height > this.imageEl.OriginHeight
27825                 )
27826         ){
27827             return false;
27828         }
27829         
27830         return true;
27831         
27832     },
27833     
27834     onRotateLeft : function(e)
27835     {   
27836         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27837             
27838             var minScale = this.thumbEl.getWidth() / this.minWidth;
27839             
27840             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27841             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27842             
27843             this.startScale = this.scale;
27844             
27845             while (this.getScaleLevel() < minScale){
27846             
27847                 this.scale = this.scale + 1;
27848                 
27849                 if(!this.zoomable()){
27850                     break;
27851                 }
27852                 
27853                 if(
27854                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27855                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27856                 ){
27857                     continue;
27858                 }
27859                 
27860                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27861
27862                 this.draw();
27863                 
27864                 return;
27865             }
27866             
27867             this.scale = this.startScale;
27868             
27869             this.onRotateFail();
27870             
27871             return false;
27872         }
27873         
27874         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27875
27876         if(this.isDocument){
27877             this.setThumbBoxSize();
27878             this.setThumbBoxPosition();
27879             this.setCanvasPosition();
27880         }
27881         
27882         this.draw();
27883         
27884         this.fireEvent('rotate', this, 'left');
27885         
27886     },
27887     
27888     onRotateRight : function(e)
27889     {
27890         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27891             
27892             var minScale = this.thumbEl.getWidth() / this.minWidth;
27893         
27894             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27895             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27896             
27897             this.startScale = this.scale;
27898             
27899             while (this.getScaleLevel() < minScale){
27900             
27901                 this.scale = this.scale + 1;
27902                 
27903                 if(!this.zoomable()){
27904                     break;
27905                 }
27906                 
27907                 if(
27908                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27909                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27910                 ){
27911                     continue;
27912                 }
27913                 
27914                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27915
27916                 this.draw();
27917                 
27918                 return;
27919             }
27920             
27921             this.scale = this.startScale;
27922             
27923             this.onRotateFail();
27924             
27925             return false;
27926         }
27927         
27928         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27929
27930         if(this.isDocument){
27931             this.setThumbBoxSize();
27932             this.setThumbBoxPosition();
27933             this.setCanvasPosition();
27934         }
27935         
27936         this.draw();
27937         
27938         this.fireEvent('rotate', this, 'right');
27939     },
27940     
27941     onRotateFail : function()
27942     {
27943         this.errorEl.show(true);
27944         
27945         var _this = this;
27946         
27947         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27948     },
27949     
27950     draw : function()
27951     {
27952         this.previewEl.dom.innerHTML = '';
27953         
27954         var canvasEl = document.createElement("canvas");
27955         
27956         var contextEl = canvasEl.getContext("2d");
27957         
27958         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27959         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27960         var center = this.imageEl.OriginWidth / 2;
27961         
27962         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27963             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27964             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27965             center = this.imageEl.OriginHeight / 2;
27966         }
27967         
27968         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27969         
27970         contextEl.translate(center, center);
27971         contextEl.rotate(this.rotate * Math.PI / 180);
27972
27973         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27974         
27975         this.canvasEl = document.createElement("canvas");
27976         
27977         this.contextEl = this.canvasEl.getContext("2d");
27978         
27979         switch (this.rotate) {
27980             case 0 :
27981                 
27982                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27983                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27984                 
27985                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27986                 
27987                 break;
27988             case 90 : 
27989                 
27990                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27991                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27992                 
27993                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27994                     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);
27995                     break;
27996                 }
27997                 
27998                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27999                 
28000                 break;
28001             case 180 :
28002                 
28003                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28004                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28005                 
28006                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28007                     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);
28008                     break;
28009                 }
28010                 
28011                 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);
28012                 
28013                 break;
28014             case 270 :
28015                 
28016                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28017                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28018         
28019                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28020                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28021                     break;
28022                 }
28023                 
28024                 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);
28025                 
28026                 break;
28027             default : 
28028                 break;
28029         }
28030         
28031         this.previewEl.appendChild(this.canvasEl);
28032         
28033         this.setCanvasPosition();
28034     },
28035     
28036     crop : function()
28037     {
28038         if(!this.canvasLoaded){
28039             return;
28040         }
28041         
28042         var imageCanvas = document.createElement("canvas");
28043         
28044         var imageContext = imageCanvas.getContext("2d");
28045         
28046         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28047         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28048         
28049         var center = imageCanvas.width / 2;
28050         
28051         imageContext.translate(center, center);
28052         
28053         imageContext.rotate(this.rotate * Math.PI / 180);
28054         
28055         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28056         
28057         var canvas = document.createElement("canvas");
28058         
28059         var context = canvas.getContext("2d");
28060                 
28061         canvas.width = this.minWidth;
28062         canvas.height = this.minHeight;
28063
28064         switch (this.rotate) {
28065             case 0 :
28066                 
28067                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28068                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28069                 
28070                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28071                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28072                 
28073                 var targetWidth = this.minWidth - 2 * x;
28074                 var targetHeight = this.minHeight - 2 * y;
28075                 
28076                 var scale = 1;
28077                 
28078                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28079                     scale = targetWidth / width;
28080                 }
28081                 
28082                 if(x > 0 && y == 0){
28083                     scale = targetHeight / height;
28084                 }
28085                 
28086                 if(x > 0 && y > 0){
28087                     scale = targetWidth / width;
28088                     
28089                     if(width < height){
28090                         scale = targetHeight / height;
28091                     }
28092                 }
28093                 
28094                 context.scale(scale, scale);
28095                 
28096                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28097                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28098
28099                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28100                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28101
28102                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28103                 
28104                 break;
28105             case 90 : 
28106                 
28107                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28108                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28109                 
28110                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28111                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28112                 
28113                 var targetWidth = this.minWidth - 2 * x;
28114                 var targetHeight = this.minHeight - 2 * y;
28115                 
28116                 var scale = 1;
28117                 
28118                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28119                     scale = targetWidth / width;
28120                 }
28121                 
28122                 if(x > 0 && y == 0){
28123                     scale = targetHeight / height;
28124                 }
28125                 
28126                 if(x > 0 && y > 0){
28127                     scale = targetWidth / width;
28128                     
28129                     if(width < height){
28130                         scale = targetHeight / height;
28131                     }
28132                 }
28133                 
28134                 context.scale(scale, scale);
28135                 
28136                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28137                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28138
28139                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28140                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28141                 
28142                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28143                 
28144                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28145                 
28146                 break;
28147             case 180 :
28148                 
28149                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28150                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28151                 
28152                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28153                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28154                 
28155                 var targetWidth = this.minWidth - 2 * x;
28156                 var targetHeight = this.minHeight - 2 * y;
28157                 
28158                 var scale = 1;
28159                 
28160                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28161                     scale = targetWidth / width;
28162                 }
28163                 
28164                 if(x > 0 && y == 0){
28165                     scale = targetHeight / height;
28166                 }
28167                 
28168                 if(x > 0 && y > 0){
28169                     scale = targetWidth / width;
28170                     
28171                     if(width < height){
28172                         scale = targetHeight / height;
28173                     }
28174                 }
28175                 
28176                 context.scale(scale, scale);
28177                 
28178                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28179                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28180
28181                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28182                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28183
28184                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28185                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28186                 
28187                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28188                 
28189                 break;
28190             case 270 :
28191                 
28192                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28193                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28194                 
28195                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28196                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28197                 
28198                 var targetWidth = this.minWidth - 2 * x;
28199                 var targetHeight = this.minHeight - 2 * y;
28200                 
28201                 var scale = 1;
28202                 
28203                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28204                     scale = targetWidth / width;
28205                 }
28206                 
28207                 if(x > 0 && y == 0){
28208                     scale = targetHeight / height;
28209                 }
28210                 
28211                 if(x > 0 && y > 0){
28212                     scale = targetWidth / width;
28213                     
28214                     if(width < height){
28215                         scale = targetHeight / height;
28216                     }
28217                 }
28218                 
28219                 context.scale(scale, scale);
28220                 
28221                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28222                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28223
28224                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28225                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28226                 
28227                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28228                 
28229                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28230                 
28231                 break;
28232             default : 
28233                 break;
28234         }
28235         
28236         this.cropData = canvas.toDataURL(this.cropType);
28237         
28238         if(this.fireEvent('crop', this, this.cropData) !== false){
28239             this.process(this.file, this.cropData);
28240         }
28241         
28242         return;
28243         
28244     },
28245     
28246     setThumbBoxSize : function()
28247     {
28248         var width, height;
28249         
28250         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28251             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28252             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28253             
28254             this.minWidth = width;
28255             this.minHeight = height;
28256             
28257             if(this.rotate == 90 || this.rotate == 270){
28258                 this.minWidth = height;
28259                 this.minHeight = width;
28260             }
28261         }
28262         
28263         height = 300;
28264         width = Math.ceil(this.minWidth * height / this.minHeight);
28265         
28266         if(this.minWidth > this.minHeight){
28267             width = 300;
28268             height = Math.ceil(this.minHeight * width / this.minWidth);
28269         }
28270         
28271         this.thumbEl.setStyle({
28272             width : width + 'px',
28273             height : height + 'px'
28274         });
28275
28276         return;
28277             
28278     },
28279     
28280     setThumbBoxPosition : function()
28281     {
28282         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28283         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28284         
28285         this.thumbEl.setLeft(x);
28286         this.thumbEl.setTop(y);
28287         
28288     },
28289     
28290     baseRotateLevel : function()
28291     {
28292         this.baseRotate = 1;
28293         
28294         if(
28295                 typeof(this.exif) != 'undefined' &&
28296                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28297                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28298         ){
28299             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28300         }
28301         
28302         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28303         
28304     },
28305     
28306     baseScaleLevel : function()
28307     {
28308         var width, height;
28309         
28310         if(this.isDocument){
28311             
28312             if(this.baseRotate == 6 || this.baseRotate == 8){
28313             
28314                 height = this.thumbEl.getHeight();
28315                 this.baseScale = height / this.imageEl.OriginWidth;
28316
28317                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28318                     width = this.thumbEl.getWidth();
28319                     this.baseScale = width / this.imageEl.OriginHeight;
28320                 }
28321
28322                 return;
28323             }
28324
28325             height = this.thumbEl.getHeight();
28326             this.baseScale = height / this.imageEl.OriginHeight;
28327
28328             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28329                 width = this.thumbEl.getWidth();
28330                 this.baseScale = width / this.imageEl.OriginWidth;
28331             }
28332
28333             return;
28334         }
28335         
28336         if(this.baseRotate == 6 || this.baseRotate == 8){
28337             
28338             width = this.thumbEl.getHeight();
28339             this.baseScale = width / this.imageEl.OriginHeight;
28340             
28341             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28342                 height = this.thumbEl.getWidth();
28343                 this.baseScale = height / this.imageEl.OriginHeight;
28344             }
28345             
28346             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28347                 height = this.thumbEl.getWidth();
28348                 this.baseScale = height / this.imageEl.OriginHeight;
28349                 
28350                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28351                     width = this.thumbEl.getHeight();
28352                     this.baseScale = width / this.imageEl.OriginWidth;
28353                 }
28354             }
28355             
28356             return;
28357         }
28358         
28359         width = this.thumbEl.getWidth();
28360         this.baseScale = width / this.imageEl.OriginWidth;
28361         
28362         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28363             height = this.thumbEl.getHeight();
28364             this.baseScale = height / this.imageEl.OriginHeight;
28365         }
28366         
28367         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28368             
28369             height = this.thumbEl.getHeight();
28370             this.baseScale = height / this.imageEl.OriginHeight;
28371             
28372             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28373                 width = this.thumbEl.getWidth();
28374                 this.baseScale = width / this.imageEl.OriginWidth;
28375             }
28376             
28377         }
28378         
28379         return;
28380     },
28381     
28382     getScaleLevel : function()
28383     {
28384         return this.baseScale * Math.pow(1.1, this.scale);
28385     },
28386     
28387     onTouchStart : function(e)
28388     {
28389         if(!this.canvasLoaded){
28390             this.beforeSelectFile(e);
28391             return;
28392         }
28393         
28394         var touches = e.browserEvent.touches;
28395         
28396         if(!touches){
28397             return;
28398         }
28399         
28400         if(touches.length == 1){
28401             this.onMouseDown(e);
28402             return;
28403         }
28404         
28405         if(touches.length != 2){
28406             return;
28407         }
28408         
28409         var coords = [];
28410         
28411         for(var i = 0, finger; finger = touches[i]; i++){
28412             coords.push(finger.pageX, finger.pageY);
28413         }
28414         
28415         var x = Math.pow(coords[0] - coords[2], 2);
28416         var y = Math.pow(coords[1] - coords[3], 2);
28417         
28418         this.startDistance = Math.sqrt(x + y);
28419         
28420         this.startScale = this.scale;
28421         
28422         this.pinching = true;
28423         this.dragable = false;
28424         
28425     },
28426     
28427     onTouchMove : function(e)
28428     {
28429         if(!this.pinching && !this.dragable){
28430             return;
28431         }
28432         
28433         var touches = e.browserEvent.touches;
28434         
28435         if(!touches){
28436             return;
28437         }
28438         
28439         if(this.dragable){
28440             this.onMouseMove(e);
28441             return;
28442         }
28443         
28444         var coords = [];
28445         
28446         for(var i = 0, finger; finger = touches[i]; i++){
28447             coords.push(finger.pageX, finger.pageY);
28448         }
28449         
28450         var x = Math.pow(coords[0] - coords[2], 2);
28451         var y = Math.pow(coords[1] - coords[3], 2);
28452         
28453         this.endDistance = Math.sqrt(x + y);
28454         
28455         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28456         
28457         if(!this.zoomable()){
28458             this.scale = this.startScale;
28459             return;
28460         }
28461         
28462         this.draw();
28463         
28464     },
28465     
28466     onTouchEnd : function(e)
28467     {
28468         this.pinching = false;
28469         this.dragable = false;
28470         
28471     },
28472     
28473     process : function(file, crop)
28474     {
28475         if(this.loadMask){
28476             this.maskEl.mask(this.loadingText);
28477         }
28478         
28479         this.xhr = new XMLHttpRequest();
28480         
28481         file.xhr = this.xhr;
28482
28483         this.xhr.open(this.method, this.url, true);
28484         
28485         var headers = {
28486             "Accept": "application/json",
28487             "Cache-Control": "no-cache",
28488             "X-Requested-With": "XMLHttpRequest"
28489         };
28490         
28491         for (var headerName in headers) {
28492             var headerValue = headers[headerName];
28493             if (headerValue) {
28494                 this.xhr.setRequestHeader(headerName, headerValue);
28495             }
28496         }
28497         
28498         var _this = this;
28499         
28500         this.xhr.onload = function()
28501         {
28502             _this.xhrOnLoad(_this.xhr);
28503         }
28504         
28505         this.xhr.onerror = function()
28506         {
28507             _this.xhrOnError(_this.xhr);
28508         }
28509         
28510         var formData = new FormData();
28511
28512         formData.append('returnHTML', 'NO');
28513         
28514         if(crop){
28515             formData.append('crop', crop);
28516         }
28517         
28518         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28519             formData.append(this.paramName, file, file.name);
28520         }
28521         
28522         if(typeof(file.filename) != 'undefined'){
28523             formData.append('filename', file.filename);
28524         }
28525         
28526         if(typeof(file.mimetype) != 'undefined'){
28527             formData.append('mimetype', file.mimetype);
28528         }
28529         
28530         if(this.fireEvent('arrange', this, formData) != false){
28531             this.xhr.send(formData);
28532         };
28533     },
28534     
28535     xhrOnLoad : function(xhr)
28536     {
28537         if(this.loadMask){
28538             this.maskEl.unmask();
28539         }
28540         
28541         if (xhr.readyState !== 4) {
28542             this.fireEvent('exception', this, xhr);
28543             return;
28544         }
28545
28546         var response = Roo.decode(xhr.responseText);
28547         
28548         if(!response.success){
28549             this.fireEvent('exception', this, xhr);
28550             return;
28551         }
28552         
28553         var response = Roo.decode(xhr.responseText);
28554         
28555         this.fireEvent('upload', this, response);
28556         
28557     },
28558     
28559     xhrOnError : function()
28560     {
28561         if(this.loadMask){
28562             this.maskEl.unmask();
28563         }
28564         
28565         Roo.log('xhr on error');
28566         
28567         var response = Roo.decode(xhr.responseText);
28568           
28569         Roo.log(response);
28570         
28571     },
28572     
28573     prepare : function(file)
28574     {   
28575         if(this.loadMask){
28576             this.maskEl.mask(this.loadingText);
28577         }
28578         
28579         this.file = false;
28580         this.exif = {};
28581         
28582         if(typeof(file) === 'string'){
28583             this.loadCanvas(file);
28584             return;
28585         }
28586         
28587         if(!file || !this.urlAPI){
28588             return;
28589         }
28590         
28591         this.file = file;
28592         this.cropType = file.type;
28593         
28594         var _this = this;
28595         
28596         if(this.fireEvent('prepare', this, this.file) != false){
28597             
28598             var reader = new FileReader();
28599             
28600             reader.onload = function (e) {
28601                 if (e.target.error) {
28602                     Roo.log(e.target.error);
28603                     return;
28604                 }
28605                 
28606                 var buffer = e.target.result,
28607                     dataView = new DataView(buffer),
28608                     offset = 2,
28609                     maxOffset = dataView.byteLength - 4,
28610                     markerBytes,
28611                     markerLength;
28612                 
28613                 if (dataView.getUint16(0) === 0xffd8) {
28614                     while (offset < maxOffset) {
28615                         markerBytes = dataView.getUint16(offset);
28616                         
28617                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28618                             markerLength = dataView.getUint16(offset + 2) + 2;
28619                             if (offset + markerLength > dataView.byteLength) {
28620                                 Roo.log('Invalid meta data: Invalid segment size.');
28621                                 break;
28622                             }
28623                             
28624                             if(markerBytes == 0xffe1){
28625                                 _this.parseExifData(
28626                                     dataView,
28627                                     offset,
28628                                     markerLength
28629                                 );
28630                             }
28631                             
28632                             offset += markerLength;
28633                             
28634                             continue;
28635                         }
28636                         
28637                         break;
28638                     }
28639                     
28640                 }
28641                 
28642                 var url = _this.urlAPI.createObjectURL(_this.file);
28643                 
28644                 _this.loadCanvas(url);
28645                 
28646                 return;
28647             }
28648             
28649             reader.readAsArrayBuffer(this.file);
28650             
28651         }
28652         
28653     },
28654     
28655     parseExifData : function(dataView, offset, length)
28656     {
28657         var tiffOffset = offset + 10,
28658             littleEndian,
28659             dirOffset;
28660     
28661         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28662             // No Exif data, might be XMP data instead
28663             return;
28664         }
28665         
28666         // Check for the ASCII code for "Exif" (0x45786966):
28667         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28668             // No Exif data, might be XMP data instead
28669             return;
28670         }
28671         if (tiffOffset + 8 > dataView.byteLength) {
28672             Roo.log('Invalid Exif data: Invalid segment size.');
28673             return;
28674         }
28675         // Check for the two null bytes:
28676         if (dataView.getUint16(offset + 8) !== 0x0000) {
28677             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28678             return;
28679         }
28680         // Check the byte alignment:
28681         switch (dataView.getUint16(tiffOffset)) {
28682         case 0x4949:
28683             littleEndian = true;
28684             break;
28685         case 0x4D4D:
28686             littleEndian = false;
28687             break;
28688         default:
28689             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28690             return;
28691         }
28692         // Check for the TIFF tag marker (0x002A):
28693         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28694             Roo.log('Invalid Exif data: Missing TIFF marker.');
28695             return;
28696         }
28697         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28698         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28699         
28700         this.parseExifTags(
28701             dataView,
28702             tiffOffset,
28703             tiffOffset + dirOffset,
28704             littleEndian
28705         );
28706     },
28707     
28708     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28709     {
28710         var tagsNumber,
28711             dirEndOffset,
28712             i;
28713         if (dirOffset + 6 > dataView.byteLength) {
28714             Roo.log('Invalid Exif data: Invalid directory offset.');
28715             return;
28716         }
28717         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28718         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28719         if (dirEndOffset + 4 > dataView.byteLength) {
28720             Roo.log('Invalid Exif data: Invalid directory size.');
28721             return;
28722         }
28723         for (i = 0; i < tagsNumber; i += 1) {
28724             this.parseExifTag(
28725                 dataView,
28726                 tiffOffset,
28727                 dirOffset + 2 + 12 * i, // tag offset
28728                 littleEndian
28729             );
28730         }
28731         // Return the offset to the next directory:
28732         return dataView.getUint32(dirEndOffset, littleEndian);
28733     },
28734     
28735     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28736     {
28737         var tag = dataView.getUint16(offset, littleEndian);
28738         
28739         this.exif[tag] = this.getExifValue(
28740             dataView,
28741             tiffOffset,
28742             offset,
28743             dataView.getUint16(offset + 2, littleEndian), // tag type
28744             dataView.getUint32(offset + 4, littleEndian), // tag length
28745             littleEndian
28746         );
28747     },
28748     
28749     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28750     {
28751         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28752             tagSize,
28753             dataOffset,
28754             values,
28755             i,
28756             str,
28757             c;
28758     
28759         if (!tagType) {
28760             Roo.log('Invalid Exif data: Invalid tag type.');
28761             return;
28762         }
28763         
28764         tagSize = tagType.size * length;
28765         // Determine if the value is contained in the dataOffset bytes,
28766         // or if the value at the dataOffset is a pointer to the actual data:
28767         dataOffset = tagSize > 4 ?
28768                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28769         if (dataOffset + tagSize > dataView.byteLength) {
28770             Roo.log('Invalid Exif data: Invalid data offset.');
28771             return;
28772         }
28773         if (length === 1) {
28774             return tagType.getValue(dataView, dataOffset, littleEndian);
28775         }
28776         values = [];
28777         for (i = 0; i < length; i += 1) {
28778             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28779         }
28780         
28781         if (tagType.ascii) {
28782             str = '';
28783             // Concatenate the chars:
28784             for (i = 0; i < values.length; i += 1) {
28785                 c = values[i];
28786                 // Ignore the terminating NULL byte(s):
28787                 if (c === '\u0000') {
28788                     break;
28789                 }
28790                 str += c;
28791             }
28792             return str;
28793         }
28794         return values;
28795     }
28796     
28797 });
28798
28799 Roo.apply(Roo.bootstrap.UploadCropbox, {
28800     tags : {
28801         'Orientation': 0x0112
28802     },
28803     
28804     Orientation: {
28805             1: 0, //'top-left',
28806 //            2: 'top-right',
28807             3: 180, //'bottom-right',
28808 //            4: 'bottom-left',
28809 //            5: 'left-top',
28810             6: 90, //'right-top',
28811 //            7: 'right-bottom',
28812             8: 270 //'left-bottom'
28813     },
28814     
28815     exifTagTypes : {
28816         // byte, 8-bit unsigned int:
28817         1: {
28818             getValue: function (dataView, dataOffset) {
28819                 return dataView.getUint8(dataOffset);
28820             },
28821             size: 1
28822         },
28823         // ascii, 8-bit byte:
28824         2: {
28825             getValue: function (dataView, dataOffset) {
28826                 return String.fromCharCode(dataView.getUint8(dataOffset));
28827             },
28828             size: 1,
28829             ascii: true
28830         },
28831         // short, 16 bit int:
28832         3: {
28833             getValue: function (dataView, dataOffset, littleEndian) {
28834                 return dataView.getUint16(dataOffset, littleEndian);
28835             },
28836             size: 2
28837         },
28838         // long, 32 bit int:
28839         4: {
28840             getValue: function (dataView, dataOffset, littleEndian) {
28841                 return dataView.getUint32(dataOffset, littleEndian);
28842             },
28843             size: 4
28844         },
28845         // rational = two long values, first is numerator, second is denominator:
28846         5: {
28847             getValue: function (dataView, dataOffset, littleEndian) {
28848                 return dataView.getUint32(dataOffset, littleEndian) /
28849                     dataView.getUint32(dataOffset + 4, littleEndian);
28850             },
28851             size: 8
28852         },
28853         // slong, 32 bit signed int:
28854         9: {
28855             getValue: function (dataView, dataOffset, littleEndian) {
28856                 return dataView.getInt32(dataOffset, littleEndian);
28857             },
28858             size: 4
28859         },
28860         // srational, two slongs, first is numerator, second is denominator:
28861         10: {
28862             getValue: function (dataView, dataOffset, littleEndian) {
28863                 return dataView.getInt32(dataOffset, littleEndian) /
28864                     dataView.getInt32(dataOffset + 4, littleEndian);
28865             },
28866             size: 8
28867         }
28868     },
28869     
28870     footer : {
28871         STANDARD : [
28872             {
28873                 tag : 'div',
28874                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28875                 action : 'rotate-left',
28876                 cn : [
28877                     {
28878                         tag : 'button',
28879                         cls : 'btn btn-default',
28880                         html : '<i class="fa fa-undo"></i>'
28881                     }
28882                 ]
28883             },
28884             {
28885                 tag : 'div',
28886                 cls : 'btn-group roo-upload-cropbox-picture',
28887                 action : 'picture',
28888                 cn : [
28889                     {
28890                         tag : 'button',
28891                         cls : 'btn btn-default',
28892                         html : '<i class="fa fa-picture-o"></i>'
28893                     }
28894                 ]
28895             },
28896             {
28897                 tag : 'div',
28898                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28899                 action : 'rotate-right',
28900                 cn : [
28901                     {
28902                         tag : 'button',
28903                         cls : 'btn btn-default',
28904                         html : '<i class="fa fa-repeat"></i>'
28905                     }
28906                 ]
28907             }
28908         ],
28909         DOCUMENT : [
28910             {
28911                 tag : 'div',
28912                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28913                 action : 'rotate-left',
28914                 cn : [
28915                     {
28916                         tag : 'button',
28917                         cls : 'btn btn-default',
28918                         html : '<i class="fa fa-undo"></i>'
28919                     }
28920                 ]
28921             },
28922             {
28923                 tag : 'div',
28924                 cls : 'btn-group roo-upload-cropbox-download',
28925                 action : 'download',
28926                 cn : [
28927                     {
28928                         tag : 'button',
28929                         cls : 'btn btn-default',
28930                         html : '<i class="fa fa-download"></i>'
28931                     }
28932                 ]
28933             },
28934             {
28935                 tag : 'div',
28936                 cls : 'btn-group roo-upload-cropbox-crop',
28937                 action : 'crop',
28938                 cn : [
28939                     {
28940                         tag : 'button',
28941                         cls : 'btn btn-default',
28942                         html : '<i class="fa fa-crop"></i>'
28943                     }
28944                 ]
28945             },
28946             {
28947                 tag : 'div',
28948                 cls : 'btn-group roo-upload-cropbox-trash',
28949                 action : 'trash',
28950                 cn : [
28951                     {
28952                         tag : 'button',
28953                         cls : 'btn btn-default',
28954                         html : '<i class="fa fa-trash"></i>'
28955                     }
28956                 ]
28957             },
28958             {
28959                 tag : 'div',
28960                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28961                 action : 'rotate-right',
28962                 cn : [
28963                     {
28964                         tag : 'button',
28965                         cls : 'btn btn-default',
28966                         html : '<i class="fa fa-repeat"></i>'
28967                     }
28968                 ]
28969             }
28970         ],
28971         ROTATOR : [
28972             {
28973                 tag : 'div',
28974                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28975                 action : 'rotate-left',
28976                 cn : [
28977                     {
28978                         tag : 'button',
28979                         cls : 'btn btn-default',
28980                         html : '<i class="fa fa-undo"></i>'
28981                     }
28982                 ]
28983             },
28984             {
28985                 tag : 'div',
28986                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28987                 action : 'rotate-right',
28988                 cn : [
28989                     {
28990                         tag : 'button',
28991                         cls : 'btn btn-default',
28992                         html : '<i class="fa fa-repeat"></i>'
28993                     }
28994                 ]
28995             }
28996         ]
28997     }
28998 });
28999
29000 /*
29001 * Licence: LGPL
29002 */
29003
29004 /**
29005  * @class Roo.bootstrap.DocumentManager
29006  * @extends Roo.bootstrap.Component
29007  * Bootstrap DocumentManager class
29008  * @cfg {String} paramName default 'imageUpload'
29009  * @cfg {String} toolTipName default 'filename'
29010  * @cfg {String} method default POST
29011  * @cfg {String} url action url
29012  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29013  * @cfg {Boolean} multiple multiple upload default true
29014  * @cfg {Number} thumbSize default 300
29015  * @cfg {String} fieldLabel
29016  * @cfg {Number} labelWidth default 4
29017  * @cfg {String} labelAlign (left|top) default left
29018  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29019 * @cfg {Number} labellg set the width of label (1-12)
29020  * @cfg {Number} labelmd set the width of label (1-12)
29021  * @cfg {Number} labelsm set the width of label (1-12)
29022  * @cfg {Number} labelxs set the width of label (1-12)
29023  * 
29024  * @constructor
29025  * Create a new DocumentManager
29026  * @param {Object} config The config object
29027  */
29028
29029 Roo.bootstrap.DocumentManager = function(config){
29030     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29031     
29032     this.files = [];
29033     this.delegates = [];
29034     
29035     this.addEvents({
29036         /**
29037          * @event initial
29038          * Fire when initial the DocumentManager
29039          * @param {Roo.bootstrap.DocumentManager} this
29040          */
29041         "initial" : true,
29042         /**
29043          * @event inspect
29044          * inspect selected file
29045          * @param {Roo.bootstrap.DocumentManager} this
29046          * @param {File} file
29047          */
29048         "inspect" : true,
29049         /**
29050          * @event exception
29051          * Fire when xhr load exception
29052          * @param {Roo.bootstrap.DocumentManager} this
29053          * @param {XMLHttpRequest} xhr
29054          */
29055         "exception" : true,
29056         /**
29057          * @event afterupload
29058          * Fire when xhr load exception
29059          * @param {Roo.bootstrap.DocumentManager} this
29060          * @param {XMLHttpRequest} xhr
29061          */
29062         "afterupload" : true,
29063         /**
29064          * @event prepare
29065          * prepare the form data
29066          * @param {Roo.bootstrap.DocumentManager} this
29067          * @param {Object} formData
29068          */
29069         "prepare" : true,
29070         /**
29071          * @event remove
29072          * Fire when remove the file
29073          * @param {Roo.bootstrap.DocumentManager} this
29074          * @param {Object} file
29075          */
29076         "remove" : true,
29077         /**
29078          * @event refresh
29079          * Fire after refresh the file
29080          * @param {Roo.bootstrap.DocumentManager} this
29081          */
29082         "refresh" : true,
29083         /**
29084          * @event click
29085          * Fire after click the image
29086          * @param {Roo.bootstrap.DocumentManager} this
29087          * @param {Object} file
29088          */
29089         "click" : true,
29090         /**
29091          * @event edit
29092          * Fire when upload a image and editable set to true
29093          * @param {Roo.bootstrap.DocumentManager} this
29094          * @param {Object} file
29095          */
29096         "edit" : true,
29097         /**
29098          * @event beforeselectfile
29099          * Fire before select file
29100          * @param {Roo.bootstrap.DocumentManager} this
29101          */
29102         "beforeselectfile" : true,
29103         /**
29104          * @event process
29105          * Fire before process file
29106          * @param {Roo.bootstrap.DocumentManager} this
29107          * @param {Object} file
29108          */
29109         "process" : true,
29110         /**
29111          * @event previewrendered
29112          * Fire when preview rendered
29113          * @param {Roo.bootstrap.DocumentManager} this
29114          * @param {Object} file
29115          */
29116         "previewrendered" : true,
29117         /**
29118          */
29119         "previewResize" : true
29120         
29121     });
29122 };
29123
29124 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29125     
29126     boxes : 0,
29127     inputName : '',
29128     thumbSize : 300,
29129     multiple : true,
29130     files : false,
29131     method : 'POST',
29132     url : '',
29133     paramName : 'imageUpload',
29134     toolTipName : 'filename',
29135     fieldLabel : '',
29136     labelWidth : 4,
29137     labelAlign : 'left',
29138     editable : true,
29139     delegates : false,
29140     xhr : false, 
29141     
29142     labellg : 0,
29143     labelmd : 0,
29144     labelsm : 0,
29145     labelxs : 0,
29146     
29147     getAutoCreate : function()
29148     {   
29149         var managerWidget = {
29150             tag : 'div',
29151             cls : 'roo-document-manager',
29152             cn : [
29153                 {
29154                     tag : 'input',
29155                     cls : 'roo-document-manager-selector',
29156                     type : 'file'
29157                 },
29158                 {
29159                     tag : 'div',
29160                     cls : 'roo-document-manager-uploader',
29161                     cn : [
29162                         {
29163                             tag : 'div',
29164                             cls : 'roo-document-manager-upload-btn',
29165                             html : '<i class="fa fa-plus"></i>'
29166                         }
29167                     ]
29168                     
29169                 }
29170             ]
29171         };
29172         
29173         var content = [
29174             {
29175                 tag : 'div',
29176                 cls : 'column col-md-12',
29177                 cn : managerWidget
29178             }
29179         ];
29180         
29181         if(this.fieldLabel.length){
29182             
29183             content = [
29184                 {
29185                     tag : 'div',
29186                     cls : 'column col-md-12',
29187                     html : this.fieldLabel
29188                 },
29189                 {
29190                     tag : 'div',
29191                     cls : 'column col-md-12',
29192                     cn : managerWidget
29193                 }
29194             ];
29195
29196             if(this.labelAlign == 'left'){
29197                 content = [
29198                     {
29199                         tag : 'div',
29200                         cls : 'column',
29201                         html : this.fieldLabel
29202                     },
29203                     {
29204                         tag : 'div',
29205                         cls : 'column',
29206                         cn : managerWidget
29207                     }
29208                 ];
29209                 
29210                 if(this.labelWidth > 12){
29211                     content[0].style = "width: " + this.labelWidth + 'px';
29212                 }
29213
29214                 if(this.labelWidth < 13 && this.labelmd == 0){
29215                     this.labelmd = this.labelWidth;
29216                 }
29217
29218                 if(this.labellg > 0){
29219                     content[0].cls += ' col-lg-' + this.labellg;
29220                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29221                 }
29222
29223                 if(this.labelmd > 0){
29224                     content[0].cls += ' col-md-' + this.labelmd;
29225                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29226                 }
29227
29228                 if(this.labelsm > 0){
29229                     content[0].cls += ' col-sm-' + this.labelsm;
29230                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29231                 }
29232
29233                 if(this.labelxs > 0){
29234                     content[0].cls += ' col-xs-' + this.labelxs;
29235                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29236                 }
29237                 
29238             }
29239         }
29240         
29241         var cfg = {
29242             tag : 'div',
29243             cls : 'row clearfix',
29244             cn : content
29245         };
29246         
29247         return cfg;
29248         
29249     },
29250     
29251     initEvents : function()
29252     {
29253         this.managerEl = this.el.select('.roo-document-manager', true).first();
29254         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29255         
29256         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29257         this.selectorEl.hide();
29258         
29259         if(this.multiple){
29260             this.selectorEl.attr('multiple', 'multiple');
29261         }
29262         
29263         this.selectorEl.on('change', this.onFileSelected, this);
29264         
29265         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29266         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29267         
29268         this.uploader.on('click', this.onUploaderClick, this);
29269         
29270         this.renderProgressDialog();
29271         
29272         var _this = this;
29273         
29274         window.addEventListener("resize", function() { _this.refresh(); } );
29275         
29276         this.fireEvent('initial', this);
29277     },
29278     
29279     renderProgressDialog : function()
29280     {
29281         var _this = this;
29282         
29283         this.progressDialog = new Roo.bootstrap.Modal({
29284             cls : 'roo-document-manager-progress-dialog',
29285             allow_close : false,
29286             animate : false,
29287             title : '',
29288             buttons : [
29289                 {
29290                     name  :'cancel',
29291                     weight : 'danger',
29292                     html : 'Cancel'
29293                 }
29294             ], 
29295             listeners : { 
29296                 btnclick : function() {
29297                     _this.uploadCancel();
29298                     this.hide();
29299                 }
29300             }
29301         });
29302          
29303         this.progressDialog.render(Roo.get(document.body));
29304          
29305         this.progress = new Roo.bootstrap.Progress({
29306             cls : 'roo-document-manager-progress',
29307             active : true,
29308             striped : true
29309         });
29310         
29311         this.progress.render(this.progressDialog.getChildContainer());
29312         
29313         this.progressBar = new Roo.bootstrap.ProgressBar({
29314             cls : 'roo-document-manager-progress-bar',
29315             aria_valuenow : 0,
29316             aria_valuemin : 0,
29317             aria_valuemax : 12,
29318             panel : 'success'
29319         });
29320         
29321         this.progressBar.render(this.progress.getChildContainer());
29322     },
29323     
29324     onUploaderClick : function(e)
29325     {
29326         e.preventDefault();
29327      
29328         if(this.fireEvent('beforeselectfile', this) != false){
29329             this.selectorEl.dom.click();
29330         }
29331         
29332     },
29333     
29334     onFileSelected : function(e)
29335     {
29336         e.preventDefault();
29337         
29338         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29339             return;
29340         }
29341         
29342         Roo.each(this.selectorEl.dom.files, function(file){
29343             if(this.fireEvent('inspect', this, file) != false){
29344                 this.files.push(file);
29345             }
29346         }, this);
29347         
29348         this.queue();
29349         
29350     },
29351     
29352     queue : function()
29353     {
29354         this.selectorEl.dom.value = '';
29355         
29356         if(!this.files || !this.files.length){
29357             return;
29358         }
29359         
29360         if(this.boxes > 0 && this.files.length > this.boxes){
29361             this.files = this.files.slice(0, this.boxes);
29362         }
29363         
29364         this.uploader.show();
29365         
29366         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29367             this.uploader.hide();
29368         }
29369         
29370         var _this = this;
29371         
29372         var files = [];
29373         
29374         var docs = [];
29375         
29376         Roo.each(this.files, function(file){
29377             
29378             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29379                 var f = this.renderPreview(file);
29380                 files.push(f);
29381                 return;
29382             }
29383             
29384             if(file.type.indexOf('image') != -1){
29385                 this.delegates.push(
29386                     (function(){
29387                         _this.process(file);
29388                     }).createDelegate(this)
29389                 );
29390         
29391                 return;
29392             }
29393             
29394             docs.push(
29395                 (function(){
29396                     _this.process(file);
29397                 }).createDelegate(this)
29398             );
29399             
29400         }, this);
29401         
29402         this.files = files;
29403         
29404         this.delegates = this.delegates.concat(docs);
29405         
29406         if(!this.delegates.length){
29407             this.refresh();
29408             return;
29409         }
29410         
29411         this.progressBar.aria_valuemax = this.delegates.length;
29412         
29413         this.arrange();
29414         
29415         return;
29416     },
29417     
29418     arrange : function()
29419     {
29420         if(!this.delegates.length){
29421             this.progressDialog.hide();
29422             this.refresh();
29423             return;
29424         }
29425         
29426         var delegate = this.delegates.shift();
29427         
29428         this.progressDialog.show();
29429         
29430         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29431         
29432         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29433         
29434         delegate();
29435     },
29436     
29437     refresh : function()
29438     {
29439         this.uploader.show();
29440         
29441         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29442             this.uploader.hide();
29443         }
29444         
29445         Roo.isTouch ? this.closable(false) : this.closable(true);
29446         
29447         this.fireEvent('refresh', this);
29448     },
29449     
29450     onRemove : function(e, el, o)
29451     {
29452         e.preventDefault();
29453         
29454         this.fireEvent('remove', this, o);
29455         
29456     },
29457     
29458     remove : function(o)
29459     {
29460         var files = [];
29461         
29462         Roo.each(this.files, function(file){
29463             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29464                 files.push(file);
29465                 return;
29466             }
29467
29468             o.target.remove();
29469
29470         }, this);
29471         
29472         this.files = files;
29473         
29474         this.refresh();
29475     },
29476     
29477     clear : function()
29478     {
29479         Roo.each(this.files, function(file){
29480             if(!file.target){
29481                 return;
29482             }
29483             
29484             file.target.remove();
29485
29486         }, this);
29487         
29488         this.files = [];
29489         
29490         this.refresh();
29491     },
29492     
29493     onClick : function(e, el, o)
29494     {
29495         e.preventDefault();
29496         
29497         this.fireEvent('click', this, o);
29498         
29499     },
29500     
29501     closable : function(closable)
29502     {
29503         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29504             
29505             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29506             
29507             if(closable){
29508                 el.show();
29509                 return;
29510             }
29511             
29512             el.hide();
29513             
29514         }, this);
29515     },
29516     
29517     xhrOnLoad : function(xhr)
29518     {
29519         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29520             el.remove();
29521         }, this);
29522         
29523         if (xhr.readyState !== 4) {
29524             this.arrange();
29525             this.fireEvent('exception', this, xhr);
29526             return;
29527         }
29528
29529         var response = Roo.decode(xhr.responseText);
29530         
29531         if(!response.success){
29532             this.arrange();
29533             this.fireEvent('exception', this, xhr);
29534             return;
29535         }
29536         
29537         var file = this.renderPreview(response.data);
29538         
29539         this.files.push(file);
29540         
29541         this.arrange();
29542         
29543         this.fireEvent('afterupload', this, xhr);
29544         
29545     },
29546     
29547     xhrOnError : function(xhr)
29548     {
29549         Roo.log('xhr on error');
29550         
29551         var response = Roo.decode(xhr.responseText);
29552           
29553         Roo.log(response);
29554         
29555         this.arrange();
29556     },
29557     
29558     process : function(file)
29559     {
29560         if(this.fireEvent('process', this, file) !== false){
29561             if(this.editable && file.type.indexOf('image') != -1){
29562                 this.fireEvent('edit', this, file);
29563                 return;
29564             }
29565
29566             this.uploadStart(file, false);
29567
29568             return;
29569         }
29570         
29571     },
29572     
29573     uploadStart : function(file, crop)
29574     {
29575         this.xhr = new XMLHttpRequest();
29576         
29577         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29578             this.arrange();
29579             return;
29580         }
29581         
29582         file.xhr = this.xhr;
29583             
29584         this.managerEl.createChild({
29585             tag : 'div',
29586             cls : 'roo-document-manager-loading',
29587             cn : [
29588                 {
29589                     tag : 'div',
29590                     tooltip : file.name,
29591                     cls : 'roo-document-manager-thumb',
29592                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29593                 }
29594             ]
29595
29596         });
29597
29598         this.xhr.open(this.method, this.url, true);
29599         
29600         var headers = {
29601             "Accept": "application/json",
29602             "Cache-Control": "no-cache",
29603             "X-Requested-With": "XMLHttpRequest"
29604         };
29605         
29606         for (var headerName in headers) {
29607             var headerValue = headers[headerName];
29608             if (headerValue) {
29609                 this.xhr.setRequestHeader(headerName, headerValue);
29610             }
29611         }
29612         
29613         var _this = this;
29614         
29615         this.xhr.onload = function()
29616         {
29617             _this.xhrOnLoad(_this.xhr);
29618         }
29619         
29620         this.xhr.onerror = function()
29621         {
29622             _this.xhrOnError(_this.xhr);
29623         }
29624         
29625         var formData = new FormData();
29626
29627         formData.append('returnHTML', 'NO');
29628         
29629         if(crop){
29630             formData.append('crop', crop);
29631         }
29632         
29633         formData.append(this.paramName, file, file.name);
29634         
29635         var options = {
29636             file : file, 
29637             manually : false
29638         };
29639         
29640         if(this.fireEvent('prepare', this, formData, options) != false){
29641             
29642             if(options.manually){
29643                 return;
29644             }
29645             
29646             this.xhr.send(formData);
29647             return;
29648         };
29649         
29650         this.uploadCancel();
29651     },
29652     
29653     uploadCancel : function()
29654     {
29655         if (this.xhr) {
29656             this.xhr.abort();
29657         }
29658         
29659         this.delegates = [];
29660         
29661         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29662             el.remove();
29663         }, this);
29664         
29665         this.arrange();
29666     },
29667     
29668     renderPreview : function(file)
29669     {
29670         if(typeof(file.target) != 'undefined' && file.target){
29671             return file;
29672         }
29673         
29674         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29675         
29676         var previewEl = this.managerEl.createChild({
29677             tag : 'div',
29678             cls : 'roo-document-manager-preview',
29679             cn : [
29680                 {
29681                     tag : 'div',
29682                     tooltip : file[this.toolTipName],
29683                     cls : 'roo-document-manager-thumb',
29684                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29685                 },
29686                 {
29687                     tag : 'button',
29688                     cls : 'close',
29689                     html : '<i class="fa fa-times-circle"></i>'
29690                 }
29691             ]
29692         });
29693
29694         var close = previewEl.select('button.close', true).first();
29695
29696         close.on('click', this.onRemove, this, file);
29697
29698         file.target = previewEl;
29699
29700         var image = previewEl.select('img', true).first();
29701         
29702         var _this = this;
29703         
29704         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29705         
29706         image.on('click', this.onClick, this, file);
29707         
29708         this.fireEvent('previewrendered', this, file);
29709         
29710         return file;
29711         
29712     },
29713     
29714     onPreviewLoad : function(file, image)
29715     {
29716         if(typeof(file.target) == 'undefined' || !file.target){
29717             return;
29718         }
29719         
29720         var width = image.dom.naturalWidth || image.dom.width;
29721         var height = image.dom.naturalHeight || image.dom.height;
29722         
29723         if(!this.previewResize) {
29724             return;
29725         }
29726         
29727         if(width > height){
29728             file.target.addClass('wide');
29729             return;
29730         }
29731         
29732         file.target.addClass('tall');
29733         return;
29734         
29735     },
29736     
29737     uploadFromSource : function(file, crop)
29738     {
29739         this.xhr = new XMLHttpRequest();
29740         
29741         this.managerEl.createChild({
29742             tag : 'div',
29743             cls : 'roo-document-manager-loading',
29744             cn : [
29745                 {
29746                     tag : 'div',
29747                     tooltip : file.name,
29748                     cls : 'roo-document-manager-thumb',
29749                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29750                 }
29751             ]
29752
29753         });
29754
29755         this.xhr.open(this.method, this.url, true);
29756         
29757         var headers = {
29758             "Accept": "application/json",
29759             "Cache-Control": "no-cache",
29760             "X-Requested-With": "XMLHttpRequest"
29761         };
29762         
29763         for (var headerName in headers) {
29764             var headerValue = headers[headerName];
29765             if (headerValue) {
29766                 this.xhr.setRequestHeader(headerName, headerValue);
29767             }
29768         }
29769         
29770         var _this = this;
29771         
29772         this.xhr.onload = function()
29773         {
29774             _this.xhrOnLoad(_this.xhr);
29775         }
29776         
29777         this.xhr.onerror = function()
29778         {
29779             _this.xhrOnError(_this.xhr);
29780         }
29781         
29782         var formData = new FormData();
29783
29784         formData.append('returnHTML', 'NO');
29785         
29786         formData.append('crop', crop);
29787         
29788         if(typeof(file.filename) != 'undefined'){
29789             formData.append('filename', file.filename);
29790         }
29791         
29792         if(typeof(file.mimetype) != 'undefined'){
29793             formData.append('mimetype', file.mimetype);
29794         }
29795         
29796         Roo.log(formData);
29797         
29798         if(this.fireEvent('prepare', this, formData) != false){
29799             this.xhr.send(formData);
29800         };
29801     }
29802 });
29803
29804 /*
29805 * Licence: LGPL
29806 */
29807
29808 /**
29809  * @class Roo.bootstrap.DocumentViewer
29810  * @extends Roo.bootstrap.Component
29811  * Bootstrap DocumentViewer class
29812  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29813  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29814  * 
29815  * @constructor
29816  * Create a new DocumentViewer
29817  * @param {Object} config The config object
29818  */
29819
29820 Roo.bootstrap.DocumentViewer = function(config){
29821     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29822     
29823     this.addEvents({
29824         /**
29825          * @event initial
29826          * Fire after initEvent
29827          * @param {Roo.bootstrap.DocumentViewer} this
29828          */
29829         "initial" : true,
29830         /**
29831          * @event click
29832          * Fire after click
29833          * @param {Roo.bootstrap.DocumentViewer} this
29834          */
29835         "click" : true,
29836         /**
29837          * @event download
29838          * Fire after download button
29839          * @param {Roo.bootstrap.DocumentViewer} this
29840          */
29841         "download" : true,
29842         /**
29843          * @event trash
29844          * Fire after trash button
29845          * @param {Roo.bootstrap.DocumentViewer} this
29846          */
29847         "trash" : true
29848         
29849     });
29850 };
29851
29852 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29853     
29854     showDownload : true,
29855     
29856     showTrash : true,
29857     
29858     getAutoCreate : function()
29859     {
29860         var cfg = {
29861             tag : 'div',
29862             cls : 'roo-document-viewer',
29863             cn : [
29864                 {
29865                     tag : 'div',
29866                     cls : 'roo-document-viewer-body',
29867                     cn : [
29868                         {
29869                             tag : 'div',
29870                             cls : 'roo-document-viewer-thumb',
29871                             cn : [
29872                                 {
29873                                     tag : 'img',
29874                                     cls : 'roo-document-viewer-image'
29875                                 }
29876                             ]
29877                         }
29878                     ]
29879                 },
29880                 {
29881                     tag : 'div',
29882                     cls : 'roo-document-viewer-footer',
29883                     cn : {
29884                         tag : 'div',
29885                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29886                         cn : [
29887                             {
29888                                 tag : 'div',
29889                                 cls : 'btn-group roo-document-viewer-download',
29890                                 cn : [
29891                                     {
29892                                         tag : 'button',
29893                                         cls : 'btn btn-default',
29894                                         html : '<i class="fa fa-download"></i>'
29895                                     }
29896                                 ]
29897                             },
29898                             {
29899                                 tag : 'div',
29900                                 cls : 'btn-group roo-document-viewer-trash',
29901                                 cn : [
29902                                     {
29903                                         tag : 'button',
29904                                         cls : 'btn btn-default',
29905                                         html : '<i class="fa fa-trash"></i>'
29906                                     }
29907                                 ]
29908                             }
29909                         ]
29910                     }
29911                 }
29912             ]
29913         };
29914         
29915         return cfg;
29916     },
29917     
29918     initEvents : function()
29919     {
29920         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29921         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29922         
29923         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29924         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29925         
29926         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29927         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29928         
29929         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29930         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29931         
29932         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29933         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29934         
29935         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29936         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29937         
29938         this.bodyEl.on('click', this.onClick, this);
29939         this.downloadBtn.on('click', this.onDownload, this);
29940         this.trashBtn.on('click', this.onTrash, this);
29941         
29942         this.downloadBtn.hide();
29943         this.trashBtn.hide();
29944         
29945         if(this.showDownload){
29946             this.downloadBtn.show();
29947         }
29948         
29949         if(this.showTrash){
29950             this.trashBtn.show();
29951         }
29952         
29953         if(!this.showDownload && !this.showTrash) {
29954             this.footerEl.hide();
29955         }
29956         
29957     },
29958     
29959     initial : function()
29960     {
29961         this.fireEvent('initial', this);
29962         
29963     },
29964     
29965     onClick : function(e)
29966     {
29967         e.preventDefault();
29968         
29969         this.fireEvent('click', this);
29970     },
29971     
29972     onDownload : function(e)
29973     {
29974         e.preventDefault();
29975         
29976         this.fireEvent('download', this);
29977     },
29978     
29979     onTrash : function(e)
29980     {
29981         e.preventDefault();
29982         
29983         this.fireEvent('trash', this);
29984     }
29985     
29986 });
29987 /*
29988  * - LGPL
29989  *
29990  * nav progress bar
29991  * 
29992  */
29993
29994 /**
29995  * @class Roo.bootstrap.NavProgressBar
29996  * @extends Roo.bootstrap.Component
29997  * Bootstrap NavProgressBar class
29998  * 
29999  * @constructor
30000  * Create a new nav progress bar
30001  * @param {Object} config The config object
30002  */
30003
30004 Roo.bootstrap.NavProgressBar = function(config){
30005     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30006
30007     this.bullets = this.bullets || [];
30008    
30009 //    Roo.bootstrap.NavProgressBar.register(this);
30010      this.addEvents({
30011         /**
30012              * @event changed
30013              * Fires when the active item changes
30014              * @param {Roo.bootstrap.NavProgressBar} this
30015              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30016              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30017          */
30018         'changed': true
30019      });
30020     
30021 };
30022
30023 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30024     
30025     bullets : [],
30026     barItems : [],
30027     
30028     getAutoCreate : function()
30029     {
30030         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30031         
30032         cfg = {
30033             tag : 'div',
30034             cls : 'roo-navigation-bar-group',
30035             cn : [
30036                 {
30037                     tag : 'div',
30038                     cls : 'roo-navigation-top-bar'
30039                 },
30040                 {
30041                     tag : 'div',
30042                     cls : 'roo-navigation-bullets-bar',
30043                     cn : [
30044                         {
30045                             tag : 'ul',
30046                             cls : 'roo-navigation-bar'
30047                         }
30048                     ]
30049                 },
30050                 
30051                 {
30052                     tag : 'div',
30053                     cls : 'roo-navigation-bottom-bar'
30054                 }
30055             ]
30056             
30057         };
30058         
30059         return cfg;
30060         
30061     },
30062     
30063     initEvents: function() 
30064     {
30065         
30066     },
30067     
30068     onRender : function(ct, position) 
30069     {
30070         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30071         
30072         if(this.bullets.length){
30073             Roo.each(this.bullets, function(b){
30074                this.addItem(b);
30075             }, this);
30076         }
30077         
30078         this.format();
30079         
30080     },
30081     
30082     addItem : function(cfg)
30083     {
30084         var item = new Roo.bootstrap.NavProgressItem(cfg);
30085         
30086         item.parentId = this.id;
30087         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30088         
30089         if(cfg.html){
30090             var top = new Roo.bootstrap.Element({
30091                 tag : 'div',
30092                 cls : 'roo-navigation-bar-text'
30093             });
30094             
30095             var bottom = new Roo.bootstrap.Element({
30096                 tag : 'div',
30097                 cls : 'roo-navigation-bar-text'
30098             });
30099             
30100             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30101             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30102             
30103             var topText = new Roo.bootstrap.Element({
30104                 tag : 'span',
30105                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30106             });
30107             
30108             var bottomText = new Roo.bootstrap.Element({
30109                 tag : 'span',
30110                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30111             });
30112             
30113             topText.onRender(top.el, null);
30114             bottomText.onRender(bottom.el, null);
30115             
30116             item.topEl = top;
30117             item.bottomEl = bottom;
30118         }
30119         
30120         this.barItems.push(item);
30121         
30122         return item;
30123     },
30124     
30125     getActive : function()
30126     {
30127         var active = false;
30128         
30129         Roo.each(this.barItems, function(v){
30130             
30131             if (!v.isActive()) {
30132                 return;
30133             }
30134             
30135             active = v;
30136             return false;
30137             
30138         });
30139         
30140         return active;
30141     },
30142     
30143     setActiveItem : function(item)
30144     {
30145         var prev = false;
30146         
30147         Roo.each(this.barItems, function(v){
30148             if (v.rid == item.rid) {
30149                 return ;
30150             }
30151             
30152             if (v.isActive()) {
30153                 v.setActive(false);
30154                 prev = v;
30155             }
30156         });
30157
30158         item.setActive(true);
30159         
30160         this.fireEvent('changed', this, item, prev);
30161     },
30162     
30163     getBarItem: function(rid)
30164     {
30165         var ret = false;
30166         
30167         Roo.each(this.barItems, function(e) {
30168             if (e.rid != rid) {
30169                 return;
30170             }
30171             
30172             ret =  e;
30173             return false;
30174         });
30175         
30176         return ret;
30177     },
30178     
30179     indexOfItem : function(item)
30180     {
30181         var index = false;
30182         
30183         Roo.each(this.barItems, function(v, i){
30184             
30185             if (v.rid != item.rid) {
30186                 return;
30187             }
30188             
30189             index = i;
30190             return false
30191         });
30192         
30193         return index;
30194     },
30195     
30196     setActiveNext : function()
30197     {
30198         var i = this.indexOfItem(this.getActive());
30199         
30200         if (i > this.barItems.length) {
30201             return;
30202         }
30203         
30204         this.setActiveItem(this.barItems[i+1]);
30205     },
30206     
30207     setActivePrev : function()
30208     {
30209         var i = this.indexOfItem(this.getActive());
30210         
30211         if (i  < 1) {
30212             return;
30213         }
30214         
30215         this.setActiveItem(this.barItems[i-1]);
30216     },
30217     
30218     format : function()
30219     {
30220         if(!this.barItems.length){
30221             return;
30222         }
30223      
30224         var width = 100 / this.barItems.length;
30225         
30226         Roo.each(this.barItems, function(i){
30227             i.el.setStyle('width', width + '%');
30228             i.topEl.el.setStyle('width', width + '%');
30229             i.bottomEl.el.setStyle('width', width + '%');
30230         }, this);
30231         
30232     }
30233     
30234 });
30235 /*
30236  * - LGPL
30237  *
30238  * Nav Progress Item
30239  * 
30240  */
30241
30242 /**
30243  * @class Roo.bootstrap.NavProgressItem
30244  * @extends Roo.bootstrap.Component
30245  * Bootstrap NavProgressItem class
30246  * @cfg {String} rid the reference id
30247  * @cfg {Boolean} active (true|false) Is item active default false
30248  * @cfg {Boolean} disabled (true|false) Is item active default false
30249  * @cfg {String} html
30250  * @cfg {String} position (top|bottom) text position default bottom
30251  * @cfg {String} icon show icon instead of number
30252  * 
30253  * @constructor
30254  * Create a new NavProgressItem
30255  * @param {Object} config The config object
30256  */
30257 Roo.bootstrap.NavProgressItem = function(config){
30258     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30259     this.addEvents({
30260         // raw events
30261         /**
30262          * @event click
30263          * The raw click event for the entire grid.
30264          * @param {Roo.bootstrap.NavProgressItem} this
30265          * @param {Roo.EventObject} e
30266          */
30267         "click" : true
30268     });
30269    
30270 };
30271
30272 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30273     
30274     rid : '',
30275     active : false,
30276     disabled : false,
30277     html : '',
30278     position : 'bottom',
30279     icon : false,
30280     
30281     getAutoCreate : function()
30282     {
30283         var iconCls = 'roo-navigation-bar-item-icon';
30284         
30285         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30286         
30287         var cfg = {
30288             tag: 'li',
30289             cls: 'roo-navigation-bar-item',
30290             cn : [
30291                 {
30292                     tag : 'i',
30293                     cls : iconCls
30294                 }
30295             ]
30296         };
30297         
30298         if(this.active){
30299             cfg.cls += ' active';
30300         }
30301         if(this.disabled){
30302             cfg.cls += ' disabled';
30303         }
30304         
30305         return cfg;
30306     },
30307     
30308     disable : function()
30309     {
30310         this.setDisabled(true);
30311     },
30312     
30313     enable : function()
30314     {
30315         this.setDisabled(false);
30316     },
30317     
30318     initEvents: function() 
30319     {
30320         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30321         
30322         this.iconEl.on('click', this.onClick, this);
30323     },
30324     
30325     onClick : function(e)
30326     {
30327         e.preventDefault();
30328         
30329         if(this.disabled){
30330             return;
30331         }
30332         
30333         if(this.fireEvent('click', this, e) === false){
30334             return;
30335         };
30336         
30337         this.parent().setActiveItem(this);
30338     },
30339     
30340     isActive: function () 
30341     {
30342         return this.active;
30343     },
30344     
30345     setActive : function(state)
30346     {
30347         if(this.active == state){
30348             return;
30349         }
30350         
30351         this.active = state;
30352         
30353         if (state) {
30354             this.el.addClass('active');
30355             return;
30356         }
30357         
30358         this.el.removeClass('active');
30359         
30360         return;
30361     },
30362     
30363     setDisabled : function(state)
30364     {
30365         if(this.disabled == state){
30366             return;
30367         }
30368         
30369         this.disabled = state;
30370         
30371         if (state) {
30372             this.el.addClass('disabled');
30373             return;
30374         }
30375         
30376         this.el.removeClass('disabled');
30377     },
30378     
30379     tooltipEl : function()
30380     {
30381         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30382     }
30383 });
30384  
30385
30386  /*
30387  * - LGPL
30388  *
30389  * FieldLabel
30390  * 
30391  */
30392
30393 /**
30394  * @class Roo.bootstrap.FieldLabel
30395  * @extends Roo.bootstrap.Component
30396  * Bootstrap FieldLabel class
30397  * @cfg {String} html contents of the element
30398  * @cfg {String} tag tag of the element default label
30399  * @cfg {String} cls class of the element
30400  * @cfg {String} target label target 
30401  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30402  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30403  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30404  * @cfg {String} iconTooltip default "This field is required"
30405  * @cfg {String} indicatorpos (left|right) default left
30406  * 
30407  * @constructor
30408  * Create a new FieldLabel
30409  * @param {Object} config The config object
30410  */
30411
30412 Roo.bootstrap.FieldLabel = function(config){
30413     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30414     
30415     this.addEvents({
30416             /**
30417              * @event invalid
30418              * Fires after the field has been marked as invalid.
30419              * @param {Roo.form.FieldLabel} this
30420              * @param {String} msg The validation message
30421              */
30422             invalid : true,
30423             /**
30424              * @event valid
30425              * Fires after the field has been validated with no errors.
30426              * @param {Roo.form.FieldLabel} this
30427              */
30428             valid : true
30429         });
30430 };
30431
30432 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30433     
30434     tag: 'label',
30435     cls: '',
30436     html: '',
30437     target: '',
30438     allowBlank : true,
30439     invalidClass : 'has-warning',
30440     validClass : 'has-success',
30441     iconTooltip : 'This field is required',
30442     indicatorpos : 'left',
30443     
30444     getAutoCreate : function(){
30445         
30446         var cls = "";
30447         if (!this.allowBlank) {
30448             cls  = "visible";
30449         }
30450         
30451         var cfg = {
30452             tag : this.tag,
30453             cls : 'roo-bootstrap-field-label ' + this.cls,
30454             for : this.target,
30455             cn : [
30456                 {
30457                     tag : 'i',
30458                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30459                     tooltip : this.iconTooltip
30460                 },
30461                 {
30462                     tag : 'span',
30463                     html : this.html
30464                 }
30465             ] 
30466         };
30467         
30468         if(this.indicatorpos == 'right'){
30469             var cfg = {
30470                 tag : this.tag,
30471                 cls : 'roo-bootstrap-field-label ' + this.cls,
30472                 for : this.target,
30473                 cn : [
30474                     {
30475                         tag : 'span',
30476                         html : this.html
30477                     },
30478                     {
30479                         tag : 'i',
30480                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30481                         tooltip : this.iconTooltip
30482                     }
30483                 ] 
30484             };
30485         }
30486         
30487         return cfg;
30488     },
30489     
30490     initEvents: function() 
30491     {
30492         Roo.bootstrap.Element.superclass.initEvents.call(this);
30493         
30494         this.indicator = this.indicatorEl();
30495         
30496         if(this.indicator){
30497             this.indicator.removeClass('visible');
30498             this.indicator.addClass('invisible');
30499         }
30500         
30501         Roo.bootstrap.FieldLabel.register(this);
30502     },
30503     
30504     indicatorEl : function()
30505     {
30506         var indicator = this.el.select('i.roo-required-indicator',true).first();
30507         
30508         if(!indicator){
30509             return false;
30510         }
30511         
30512         return indicator;
30513         
30514     },
30515     
30516     /**
30517      * Mark this field as valid
30518      */
30519     markValid : function()
30520     {
30521         if(this.indicator){
30522             this.indicator.removeClass('visible');
30523             this.indicator.addClass('invisible');
30524         }
30525         if (Roo.bootstrap.version == 3) {
30526             this.el.removeClass(this.invalidClass);
30527             this.el.addClass(this.validClass);
30528         } else {
30529             this.el.removeClass('is-invalid');
30530             this.el.addClass('is-valid');
30531         }
30532         
30533         
30534         this.fireEvent('valid', this);
30535     },
30536     
30537     /**
30538      * Mark this field as invalid
30539      * @param {String} msg The validation message
30540      */
30541     markInvalid : function(msg)
30542     {
30543         if(this.indicator){
30544             this.indicator.removeClass('invisible');
30545             this.indicator.addClass('visible');
30546         }
30547           if (Roo.bootstrap.version == 3) {
30548             this.el.removeClass(this.validClass);
30549             this.el.addClass(this.invalidClass);
30550         } else {
30551             this.el.removeClass('is-valid');
30552             this.el.addClass('is-invalid');
30553         }
30554         
30555         
30556         this.fireEvent('invalid', this, msg);
30557     }
30558     
30559    
30560 });
30561
30562 Roo.apply(Roo.bootstrap.FieldLabel, {
30563     
30564     groups: {},
30565     
30566      /**
30567     * register a FieldLabel Group
30568     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30569     */
30570     register : function(label)
30571     {
30572         if(this.groups.hasOwnProperty(label.target)){
30573             return;
30574         }
30575      
30576         this.groups[label.target] = label;
30577         
30578     },
30579     /**
30580     * fetch a FieldLabel Group based on the target
30581     * @param {string} target
30582     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30583     */
30584     get: function(target) {
30585         if (typeof(this.groups[target]) == 'undefined') {
30586             return false;
30587         }
30588         
30589         return this.groups[target] ;
30590     }
30591 });
30592
30593  
30594
30595  /*
30596  * - LGPL
30597  *
30598  * page DateSplitField.
30599  * 
30600  */
30601
30602
30603 /**
30604  * @class Roo.bootstrap.DateSplitField
30605  * @extends Roo.bootstrap.Component
30606  * Bootstrap DateSplitField class
30607  * @cfg {string} fieldLabel - the label associated
30608  * @cfg {Number} labelWidth set the width of label (0-12)
30609  * @cfg {String} labelAlign (top|left)
30610  * @cfg {Boolean} dayAllowBlank (true|false) default false
30611  * @cfg {Boolean} monthAllowBlank (true|false) default false
30612  * @cfg {Boolean} yearAllowBlank (true|false) default false
30613  * @cfg {string} dayPlaceholder 
30614  * @cfg {string} monthPlaceholder
30615  * @cfg {string} yearPlaceholder
30616  * @cfg {string} dayFormat default 'd'
30617  * @cfg {string} monthFormat default 'm'
30618  * @cfg {string} yearFormat default 'Y'
30619  * @cfg {Number} labellg set the width of label (1-12)
30620  * @cfg {Number} labelmd set the width of label (1-12)
30621  * @cfg {Number} labelsm set the width of label (1-12)
30622  * @cfg {Number} labelxs set the width of label (1-12)
30623
30624  *     
30625  * @constructor
30626  * Create a new DateSplitField
30627  * @param {Object} config The config object
30628  */
30629
30630 Roo.bootstrap.DateSplitField = function(config){
30631     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30632     
30633     this.addEvents({
30634         // raw events
30635          /**
30636          * @event years
30637          * getting the data of years
30638          * @param {Roo.bootstrap.DateSplitField} this
30639          * @param {Object} years
30640          */
30641         "years" : true,
30642         /**
30643          * @event days
30644          * getting the data of days
30645          * @param {Roo.bootstrap.DateSplitField} this
30646          * @param {Object} days
30647          */
30648         "days" : true,
30649         /**
30650          * @event invalid
30651          * Fires after the field has been marked as invalid.
30652          * @param {Roo.form.Field} this
30653          * @param {String} msg The validation message
30654          */
30655         invalid : true,
30656        /**
30657          * @event valid
30658          * Fires after the field has been validated with no errors.
30659          * @param {Roo.form.Field} this
30660          */
30661         valid : true
30662     });
30663 };
30664
30665 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30666     
30667     fieldLabel : '',
30668     labelAlign : 'top',
30669     labelWidth : 3,
30670     dayAllowBlank : false,
30671     monthAllowBlank : false,
30672     yearAllowBlank : false,
30673     dayPlaceholder : '',
30674     monthPlaceholder : '',
30675     yearPlaceholder : '',
30676     dayFormat : 'd',
30677     monthFormat : 'm',
30678     yearFormat : 'Y',
30679     isFormField : true,
30680     labellg : 0,
30681     labelmd : 0,
30682     labelsm : 0,
30683     labelxs : 0,
30684     
30685     getAutoCreate : function()
30686     {
30687         var cfg = {
30688             tag : 'div',
30689             cls : 'row roo-date-split-field-group',
30690             cn : [
30691                 {
30692                     tag : 'input',
30693                     type : 'hidden',
30694                     cls : 'form-hidden-field roo-date-split-field-group-value',
30695                     name : this.name
30696                 }
30697             ]
30698         };
30699         
30700         var labelCls = 'col-md-12';
30701         var contentCls = 'col-md-4';
30702         
30703         if(this.fieldLabel){
30704             
30705             var label = {
30706                 tag : 'div',
30707                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30708                 cn : [
30709                     {
30710                         tag : 'label',
30711                         html : this.fieldLabel
30712                     }
30713                 ]
30714             };
30715             
30716             if(this.labelAlign == 'left'){
30717             
30718                 if(this.labelWidth > 12){
30719                     label.style = "width: " + this.labelWidth + 'px';
30720                 }
30721
30722                 if(this.labelWidth < 13 && this.labelmd == 0){
30723                     this.labelmd = this.labelWidth;
30724                 }
30725
30726                 if(this.labellg > 0){
30727                     labelCls = ' col-lg-' + this.labellg;
30728                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30729                 }
30730
30731                 if(this.labelmd > 0){
30732                     labelCls = ' col-md-' + this.labelmd;
30733                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30734                 }
30735
30736                 if(this.labelsm > 0){
30737                     labelCls = ' col-sm-' + this.labelsm;
30738                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30739                 }
30740
30741                 if(this.labelxs > 0){
30742                     labelCls = ' col-xs-' + this.labelxs;
30743                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30744                 }
30745             }
30746             
30747             label.cls += ' ' + labelCls;
30748             
30749             cfg.cn.push(label);
30750         }
30751         
30752         Roo.each(['day', 'month', 'year'], function(t){
30753             cfg.cn.push({
30754                 tag : 'div',
30755                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30756             });
30757         }, this);
30758         
30759         return cfg;
30760     },
30761     
30762     inputEl: function ()
30763     {
30764         return this.el.select('.roo-date-split-field-group-value', true).first();
30765     },
30766     
30767     onRender : function(ct, position) 
30768     {
30769         var _this = this;
30770         
30771         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30772         
30773         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30774         
30775         this.dayField = new Roo.bootstrap.ComboBox({
30776             allowBlank : this.dayAllowBlank,
30777             alwaysQuery : true,
30778             displayField : 'value',
30779             editable : false,
30780             fieldLabel : '',
30781             forceSelection : true,
30782             mode : 'local',
30783             placeholder : this.dayPlaceholder,
30784             selectOnFocus : true,
30785             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30786             triggerAction : 'all',
30787             typeAhead : true,
30788             valueField : 'value',
30789             store : new Roo.data.SimpleStore({
30790                 data : (function() {    
30791                     var days = [];
30792                     _this.fireEvent('days', _this, days);
30793                     return days;
30794                 })(),
30795                 fields : [ 'value' ]
30796             }),
30797             listeners : {
30798                 select : function (_self, record, index)
30799                 {
30800                     _this.setValue(_this.getValue());
30801                 }
30802             }
30803         });
30804
30805         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30806         
30807         this.monthField = new Roo.bootstrap.MonthField({
30808             after : '<i class=\"fa fa-calendar\"></i>',
30809             allowBlank : this.monthAllowBlank,
30810             placeholder : this.monthPlaceholder,
30811             readOnly : true,
30812             listeners : {
30813                 render : function (_self)
30814                 {
30815                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30816                         e.preventDefault();
30817                         _self.focus();
30818                     });
30819                 },
30820                 select : function (_self, oldvalue, newvalue)
30821                 {
30822                     _this.setValue(_this.getValue());
30823                 }
30824             }
30825         });
30826         
30827         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30828         
30829         this.yearField = new Roo.bootstrap.ComboBox({
30830             allowBlank : this.yearAllowBlank,
30831             alwaysQuery : true,
30832             displayField : 'value',
30833             editable : false,
30834             fieldLabel : '',
30835             forceSelection : true,
30836             mode : 'local',
30837             placeholder : this.yearPlaceholder,
30838             selectOnFocus : true,
30839             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30840             triggerAction : 'all',
30841             typeAhead : true,
30842             valueField : 'value',
30843             store : new Roo.data.SimpleStore({
30844                 data : (function() {
30845                     var years = [];
30846                     _this.fireEvent('years', _this, years);
30847                     return years;
30848                 })(),
30849                 fields : [ 'value' ]
30850             }),
30851             listeners : {
30852                 select : function (_self, record, index)
30853                 {
30854                     _this.setValue(_this.getValue());
30855                 }
30856             }
30857         });
30858
30859         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30860     },
30861     
30862     setValue : function(v, format)
30863     {
30864         this.inputEl.dom.value = v;
30865         
30866         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30867         
30868         var d = Date.parseDate(v, f);
30869         
30870         if(!d){
30871             this.validate();
30872             return;
30873         }
30874         
30875         this.setDay(d.format(this.dayFormat));
30876         this.setMonth(d.format(this.monthFormat));
30877         this.setYear(d.format(this.yearFormat));
30878         
30879         this.validate();
30880         
30881         return;
30882     },
30883     
30884     setDay : function(v)
30885     {
30886         this.dayField.setValue(v);
30887         this.inputEl.dom.value = this.getValue();
30888         this.validate();
30889         return;
30890     },
30891     
30892     setMonth : function(v)
30893     {
30894         this.monthField.setValue(v, true);
30895         this.inputEl.dom.value = this.getValue();
30896         this.validate();
30897         return;
30898     },
30899     
30900     setYear : function(v)
30901     {
30902         this.yearField.setValue(v);
30903         this.inputEl.dom.value = this.getValue();
30904         this.validate();
30905         return;
30906     },
30907     
30908     getDay : function()
30909     {
30910         return this.dayField.getValue();
30911     },
30912     
30913     getMonth : function()
30914     {
30915         return this.monthField.getValue();
30916     },
30917     
30918     getYear : function()
30919     {
30920         return this.yearField.getValue();
30921     },
30922     
30923     getValue : function()
30924     {
30925         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30926         
30927         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30928         
30929         return date;
30930     },
30931     
30932     reset : function()
30933     {
30934         this.setDay('');
30935         this.setMonth('');
30936         this.setYear('');
30937         this.inputEl.dom.value = '';
30938         this.validate();
30939         return;
30940     },
30941     
30942     validate : function()
30943     {
30944         var d = this.dayField.validate();
30945         var m = this.monthField.validate();
30946         var y = this.yearField.validate();
30947         
30948         var valid = true;
30949         
30950         if(
30951                 (!this.dayAllowBlank && !d) ||
30952                 (!this.monthAllowBlank && !m) ||
30953                 (!this.yearAllowBlank && !y)
30954         ){
30955             valid = false;
30956         }
30957         
30958         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30959             return valid;
30960         }
30961         
30962         if(valid){
30963             this.markValid();
30964             return valid;
30965         }
30966         
30967         this.markInvalid();
30968         
30969         return valid;
30970     },
30971     
30972     markValid : function()
30973     {
30974         
30975         var label = this.el.select('label', true).first();
30976         var icon = this.el.select('i.fa-star', true).first();
30977
30978         if(label && icon){
30979             icon.remove();
30980         }
30981         
30982         this.fireEvent('valid', this);
30983     },
30984     
30985      /**
30986      * Mark this field as invalid
30987      * @param {String} msg The validation message
30988      */
30989     markInvalid : function(msg)
30990     {
30991         
30992         var label = this.el.select('label', true).first();
30993         var icon = this.el.select('i.fa-star', true).first();
30994
30995         if(label && !icon){
30996             this.el.select('.roo-date-split-field-label', true).createChild({
30997                 tag : 'i',
30998                 cls : 'text-danger fa fa-lg fa-star',
30999                 tooltip : 'This field is required',
31000                 style : 'margin-right:5px;'
31001             }, label, true);
31002         }
31003         
31004         this.fireEvent('invalid', this, msg);
31005     },
31006     
31007     clearInvalid : function()
31008     {
31009         var label = this.el.select('label', true).first();
31010         var icon = this.el.select('i.fa-star', true).first();
31011
31012         if(label && icon){
31013             icon.remove();
31014         }
31015         
31016         this.fireEvent('valid', this);
31017     },
31018     
31019     getName: function()
31020     {
31021         return this.name;
31022     }
31023     
31024 });
31025
31026  /**
31027  *
31028  * This is based on 
31029  * http://masonry.desandro.com
31030  *
31031  * The idea is to render all the bricks based on vertical width...
31032  *
31033  * The original code extends 'outlayer' - we might need to use that....
31034  * 
31035  */
31036
31037
31038 /**
31039  * @class Roo.bootstrap.LayoutMasonry
31040  * @extends Roo.bootstrap.Component
31041  * Bootstrap Layout Masonry class
31042  * 
31043  * @constructor
31044  * Create a new Element
31045  * @param {Object} config The config object
31046  */
31047
31048 Roo.bootstrap.LayoutMasonry = function(config){
31049     
31050     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31051     
31052     this.bricks = [];
31053     
31054     Roo.bootstrap.LayoutMasonry.register(this);
31055     
31056     this.addEvents({
31057         // raw events
31058         /**
31059          * @event layout
31060          * Fire after layout the items
31061          * @param {Roo.bootstrap.LayoutMasonry} this
31062          * @param {Roo.EventObject} e
31063          */
31064         "layout" : true
31065     });
31066     
31067 };
31068
31069 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31070     
31071     /**
31072      * @cfg {Boolean} isLayoutInstant = no animation?
31073      */   
31074     isLayoutInstant : false, // needed?
31075    
31076     /**
31077      * @cfg {Number} boxWidth  width of the columns
31078      */   
31079     boxWidth : 450,
31080     
31081       /**
31082      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31083      */   
31084     boxHeight : 0,
31085     
31086     /**
31087      * @cfg {Number} padWidth padding below box..
31088      */   
31089     padWidth : 10, 
31090     
31091     /**
31092      * @cfg {Number} gutter gutter width..
31093      */   
31094     gutter : 10,
31095     
31096      /**
31097      * @cfg {Number} maxCols maximum number of columns
31098      */   
31099     
31100     maxCols: 0,
31101     
31102     /**
31103      * @cfg {Boolean} isAutoInitial defalut true
31104      */   
31105     isAutoInitial : true, 
31106     
31107     containerWidth: 0,
31108     
31109     /**
31110      * @cfg {Boolean} isHorizontal defalut false
31111      */   
31112     isHorizontal : false, 
31113
31114     currentSize : null,
31115     
31116     tag: 'div',
31117     
31118     cls: '',
31119     
31120     bricks: null, //CompositeElement
31121     
31122     cols : 1,
31123     
31124     _isLayoutInited : false,
31125     
31126 //    isAlternative : false, // only use for vertical layout...
31127     
31128     /**
31129      * @cfg {Number} alternativePadWidth padding below box..
31130      */   
31131     alternativePadWidth : 50,
31132     
31133     selectedBrick : [],
31134     
31135     getAutoCreate : function(){
31136         
31137         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31138         
31139         var cfg = {
31140             tag: this.tag,
31141             cls: 'blog-masonary-wrapper ' + this.cls,
31142             cn : {
31143                 cls : 'mas-boxes masonary'
31144             }
31145         };
31146         
31147         return cfg;
31148     },
31149     
31150     getChildContainer: function( )
31151     {
31152         if (this.boxesEl) {
31153             return this.boxesEl;
31154         }
31155         
31156         this.boxesEl = this.el.select('.mas-boxes').first();
31157         
31158         return this.boxesEl;
31159     },
31160     
31161     
31162     initEvents : function()
31163     {
31164         var _this = this;
31165         
31166         if(this.isAutoInitial){
31167             Roo.log('hook children rendered');
31168             this.on('childrenrendered', function() {
31169                 Roo.log('children rendered');
31170                 _this.initial();
31171             } ,this);
31172         }
31173     },
31174     
31175     initial : function()
31176     {
31177         this.selectedBrick = [];
31178         
31179         this.currentSize = this.el.getBox(true);
31180         
31181         Roo.EventManager.onWindowResize(this.resize, this); 
31182
31183         if(!this.isAutoInitial){
31184             this.layout();
31185             return;
31186         }
31187         
31188         this.layout();
31189         
31190         return;
31191         //this.layout.defer(500,this);
31192         
31193     },
31194     
31195     resize : function()
31196     {
31197         var cs = this.el.getBox(true);
31198         
31199         if (
31200                 this.currentSize.width == cs.width && 
31201                 this.currentSize.x == cs.x && 
31202                 this.currentSize.height == cs.height && 
31203                 this.currentSize.y == cs.y 
31204         ) {
31205             Roo.log("no change in with or X or Y");
31206             return;
31207         }
31208         
31209         this.currentSize = cs;
31210         
31211         this.layout();
31212         
31213     },
31214     
31215     layout : function()
31216     {   
31217         this._resetLayout();
31218         
31219         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31220         
31221         this.layoutItems( isInstant );
31222       
31223         this._isLayoutInited = true;
31224         
31225         this.fireEvent('layout', this);
31226         
31227     },
31228     
31229     _resetLayout : function()
31230     {
31231         if(this.isHorizontal){
31232             this.horizontalMeasureColumns();
31233             return;
31234         }
31235         
31236         this.verticalMeasureColumns();
31237         
31238     },
31239     
31240     verticalMeasureColumns : function()
31241     {
31242         this.getContainerWidth();
31243         
31244 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31245 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31246 //            return;
31247 //        }
31248         
31249         var boxWidth = this.boxWidth + this.padWidth;
31250         
31251         if(this.containerWidth < this.boxWidth){
31252             boxWidth = this.containerWidth
31253         }
31254         
31255         var containerWidth = this.containerWidth;
31256         
31257         var cols = Math.floor(containerWidth / boxWidth);
31258         
31259         this.cols = Math.max( cols, 1 );
31260         
31261         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31262         
31263         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31264         
31265         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31266         
31267         this.colWidth = boxWidth + avail - this.padWidth;
31268         
31269         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31270         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31271     },
31272     
31273     horizontalMeasureColumns : function()
31274     {
31275         this.getContainerWidth();
31276         
31277         var boxWidth = this.boxWidth;
31278         
31279         if(this.containerWidth < boxWidth){
31280             boxWidth = this.containerWidth;
31281         }
31282         
31283         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31284         
31285         this.el.setHeight(boxWidth);
31286         
31287     },
31288     
31289     getContainerWidth : function()
31290     {
31291         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31292     },
31293     
31294     layoutItems : function( isInstant )
31295     {
31296         Roo.log(this.bricks);
31297         
31298         var items = Roo.apply([], this.bricks);
31299         
31300         if(this.isHorizontal){
31301             this._horizontalLayoutItems( items , isInstant );
31302             return;
31303         }
31304         
31305 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31306 //            this._verticalAlternativeLayoutItems( items , isInstant );
31307 //            return;
31308 //        }
31309         
31310         this._verticalLayoutItems( items , isInstant );
31311         
31312     },
31313     
31314     _verticalLayoutItems : function ( items , isInstant)
31315     {
31316         if ( !items || !items.length ) {
31317             return;
31318         }
31319         
31320         var standard = [
31321             ['xs', 'xs', 'xs', 'tall'],
31322             ['xs', 'xs', 'tall'],
31323             ['xs', 'xs', 'sm'],
31324             ['xs', 'xs', 'xs'],
31325             ['xs', 'tall'],
31326             ['xs', 'sm'],
31327             ['xs', 'xs'],
31328             ['xs'],
31329             
31330             ['sm', 'xs', 'xs'],
31331             ['sm', 'xs'],
31332             ['sm'],
31333             
31334             ['tall', 'xs', 'xs', 'xs'],
31335             ['tall', 'xs', 'xs'],
31336             ['tall', 'xs'],
31337             ['tall']
31338             
31339         ];
31340         
31341         var queue = [];
31342         
31343         var boxes = [];
31344         
31345         var box = [];
31346         
31347         Roo.each(items, function(item, k){
31348             
31349             switch (item.size) {
31350                 // these layouts take up a full box,
31351                 case 'md' :
31352                 case 'md-left' :
31353                 case 'md-right' :
31354                 case 'wide' :
31355                     
31356                     if(box.length){
31357                         boxes.push(box);
31358                         box = [];
31359                     }
31360                     
31361                     boxes.push([item]);
31362                     
31363                     break;
31364                     
31365                 case 'xs' :
31366                 case 'sm' :
31367                 case 'tall' :
31368                     
31369                     box.push(item);
31370                     
31371                     break;
31372                 default :
31373                     break;
31374                     
31375             }
31376             
31377         }, this);
31378         
31379         if(box.length){
31380             boxes.push(box);
31381             box = [];
31382         }
31383         
31384         var filterPattern = function(box, length)
31385         {
31386             if(!box.length){
31387                 return;
31388             }
31389             
31390             var match = false;
31391             
31392             var pattern = box.slice(0, length);
31393             
31394             var format = [];
31395             
31396             Roo.each(pattern, function(i){
31397                 format.push(i.size);
31398             }, this);
31399             
31400             Roo.each(standard, function(s){
31401                 
31402                 if(String(s) != String(format)){
31403                     return;
31404                 }
31405                 
31406                 match = true;
31407                 return false;
31408                 
31409             }, this);
31410             
31411             if(!match && length == 1){
31412                 return;
31413             }
31414             
31415             if(!match){
31416                 filterPattern(box, length - 1);
31417                 return;
31418             }
31419                 
31420             queue.push(pattern);
31421
31422             box = box.slice(length, box.length);
31423
31424             filterPattern(box, 4);
31425
31426             return;
31427             
31428         }
31429         
31430         Roo.each(boxes, function(box, k){
31431             
31432             if(!box.length){
31433                 return;
31434             }
31435             
31436             if(box.length == 1){
31437                 queue.push(box);
31438                 return;
31439             }
31440             
31441             filterPattern(box, 4);
31442             
31443         }, this);
31444         
31445         this._processVerticalLayoutQueue( queue, isInstant );
31446         
31447     },
31448     
31449 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31450 //    {
31451 //        if ( !items || !items.length ) {
31452 //            return;
31453 //        }
31454 //
31455 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31456 //        
31457 //    },
31458     
31459     _horizontalLayoutItems : function ( items , isInstant)
31460     {
31461         if ( !items || !items.length || items.length < 3) {
31462             return;
31463         }
31464         
31465         items.reverse();
31466         
31467         var eItems = items.slice(0, 3);
31468         
31469         items = items.slice(3, items.length);
31470         
31471         var standard = [
31472             ['xs', 'xs', 'xs', 'wide'],
31473             ['xs', 'xs', 'wide'],
31474             ['xs', 'xs', 'sm'],
31475             ['xs', 'xs', 'xs'],
31476             ['xs', 'wide'],
31477             ['xs', 'sm'],
31478             ['xs', 'xs'],
31479             ['xs'],
31480             
31481             ['sm', 'xs', 'xs'],
31482             ['sm', 'xs'],
31483             ['sm'],
31484             
31485             ['wide', 'xs', 'xs', 'xs'],
31486             ['wide', 'xs', 'xs'],
31487             ['wide', 'xs'],
31488             ['wide'],
31489             
31490             ['wide-thin']
31491         ];
31492         
31493         var queue = [];
31494         
31495         var boxes = [];
31496         
31497         var box = [];
31498         
31499         Roo.each(items, function(item, k){
31500             
31501             switch (item.size) {
31502                 case 'md' :
31503                 case 'md-left' :
31504                 case 'md-right' :
31505                 case 'tall' :
31506                     
31507                     if(box.length){
31508                         boxes.push(box);
31509                         box = [];
31510                     }
31511                     
31512                     boxes.push([item]);
31513                     
31514                     break;
31515                     
31516                 case 'xs' :
31517                 case 'sm' :
31518                 case 'wide' :
31519                 case 'wide-thin' :
31520                     
31521                     box.push(item);
31522                     
31523                     break;
31524                 default :
31525                     break;
31526                     
31527             }
31528             
31529         }, this);
31530         
31531         if(box.length){
31532             boxes.push(box);
31533             box = [];
31534         }
31535         
31536         var filterPattern = function(box, length)
31537         {
31538             if(!box.length){
31539                 return;
31540             }
31541             
31542             var match = false;
31543             
31544             var pattern = box.slice(0, length);
31545             
31546             var format = [];
31547             
31548             Roo.each(pattern, function(i){
31549                 format.push(i.size);
31550             }, this);
31551             
31552             Roo.each(standard, function(s){
31553                 
31554                 if(String(s) != String(format)){
31555                     return;
31556                 }
31557                 
31558                 match = true;
31559                 return false;
31560                 
31561             }, this);
31562             
31563             if(!match && length == 1){
31564                 return;
31565             }
31566             
31567             if(!match){
31568                 filterPattern(box, length - 1);
31569                 return;
31570             }
31571                 
31572             queue.push(pattern);
31573
31574             box = box.slice(length, box.length);
31575
31576             filterPattern(box, 4);
31577
31578             return;
31579             
31580         }
31581         
31582         Roo.each(boxes, function(box, k){
31583             
31584             if(!box.length){
31585                 return;
31586             }
31587             
31588             if(box.length == 1){
31589                 queue.push(box);
31590                 return;
31591             }
31592             
31593             filterPattern(box, 4);
31594             
31595         }, this);
31596         
31597         
31598         var prune = [];
31599         
31600         var pos = this.el.getBox(true);
31601         
31602         var minX = pos.x;
31603         
31604         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31605         
31606         var hit_end = false;
31607         
31608         Roo.each(queue, function(box){
31609             
31610             if(hit_end){
31611                 
31612                 Roo.each(box, function(b){
31613                 
31614                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31615                     b.el.hide();
31616
31617                 }, this);
31618
31619                 return;
31620             }
31621             
31622             var mx = 0;
31623             
31624             Roo.each(box, function(b){
31625                 
31626                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31627                 b.el.show();
31628
31629                 mx = Math.max(mx, b.x);
31630                 
31631             }, this);
31632             
31633             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31634             
31635             if(maxX < minX){
31636                 
31637                 Roo.each(box, function(b){
31638                 
31639                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31640                     b.el.hide();
31641                     
31642                 }, this);
31643                 
31644                 hit_end = true;
31645                 
31646                 return;
31647             }
31648             
31649             prune.push(box);
31650             
31651         }, this);
31652         
31653         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31654     },
31655     
31656     /** Sets position of item in DOM
31657     * @param {Element} item
31658     * @param {Number} x - horizontal position
31659     * @param {Number} y - vertical position
31660     * @param {Boolean} isInstant - disables transitions
31661     */
31662     _processVerticalLayoutQueue : function( queue, isInstant )
31663     {
31664         var pos = this.el.getBox(true);
31665         var x = pos.x;
31666         var y = pos.y;
31667         var maxY = [];
31668         
31669         for (var i = 0; i < this.cols; i++){
31670             maxY[i] = pos.y;
31671         }
31672         
31673         Roo.each(queue, function(box, k){
31674             
31675             var col = k % this.cols;
31676             
31677             Roo.each(box, function(b,kk){
31678                 
31679                 b.el.position('absolute');
31680                 
31681                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31682                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31683                 
31684                 if(b.size == 'md-left' || b.size == 'md-right'){
31685                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31686                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31687                 }
31688                 
31689                 b.el.setWidth(width);
31690                 b.el.setHeight(height);
31691                 // iframe?
31692                 b.el.select('iframe',true).setSize(width,height);
31693                 
31694             }, this);
31695             
31696             for (var i = 0; i < this.cols; i++){
31697                 
31698                 if(maxY[i] < maxY[col]){
31699                     col = i;
31700                     continue;
31701                 }
31702                 
31703                 col = Math.min(col, i);
31704                 
31705             }
31706             
31707             x = pos.x + col * (this.colWidth + this.padWidth);
31708             
31709             y = maxY[col];
31710             
31711             var positions = [];
31712             
31713             switch (box.length){
31714                 case 1 :
31715                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31716                     break;
31717                 case 2 :
31718                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31719                     break;
31720                 case 3 :
31721                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31722                     break;
31723                 case 4 :
31724                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31725                     break;
31726                 default :
31727                     break;
31728             }
31729             
31730             Roo.each(box, function(b,kk){
31731                 
31732                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31733                 
31734                 var sz = b.el.getSize();
31735                 
31736                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31737                 
31738             }, this);
31739             
31740         }, this);
31741         
31742         var mY = 0;
31743         
31744         for (var i = 0; i < this.cols; i++){
31745             mY = Math.max(mY, maxY[i]);
31746         }
31747         
31748         this.el.setHeight(mY - pos.y);
31749         
31750     },
31751     
31752 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31753 //    {
31754 //        var pos = this.el.getBox(true);
31755 //        var x = pos.x;
31756 //        var y = pos.y;
31757 //        var maxX = pos.right;
31758 //        
31759 //        var maxHeight = 0;
31760 //        
31761 //        Roo.each(items, function(item, k){
31762 //            
31763 //            var c = k % 2;
31764 //            
31765 //            item.el.position('absolute');
31766 //                
31767 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31768 //
31769 //            item.el.setWidth(width);
31770 //
31771 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31772 //
31773 //            item.el.setHeight(height);
31774 //            
31775 //            if(c == 0){
31776 //                item.el.setXY([x, y], isInstant ? false : true);
31777 //            } else {
31778 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31779 //            }
31780 //            
31781 //            y = y + height + this.alternativePadWidth;
31782 //            
31783 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31784 //            
31785 //        }, this);
31786 //        
31787 //        this.el.setHeight(maxHeight);
31788 //        
31789 //    },
31790     
31791     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31792     {
31793         var pos = this.el.getBox(true);
31794         
31795         var minX = pos.x;
31796         var minY = pos.y;
31797         
31798         var maxX = pos.right;
31799         
31800         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31801         
31802         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31803         
31804         Roo.each(queue, function(box, k){
31805             
31806             Roo.each(box, function(b, kk){
31807                 
31808                 b.el.position('absolute');
31809                 
31810                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31811                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31812                 
31813                 if(b.size == 'md-left' || b.size == 'md-right'){
31814                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31815                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31816                 }
31817                 
31818                 b.el.setWidth(width);
31819                 b.el.setHeight(height);
31820                 
31821             }, this);
31822             
31823             if(!box.length){
31824                 return;
31825             }
31826             
31827             var positions = [];
31828             
31829             switch (box.length){
31830                 case 1 :
31831                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31832                     break;
31833                 case 2 :
31834                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31835                     break;
31836                 case 3 :
31837                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31838                     break;
31839                 case 4 :
31840                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31841                     break;
31842                 default :
31843                     break;
31844             }
31845             
31846             Roo.each(box, function(b,kk){
31847                 
31848                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31849                 
31850                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31851                 
31852             }, this);
31853             
31854         }, this);
31855         
31856     },
31857     
31858     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31859     {
31860         Roo.each(eItems, function(b,k){
31861             
31862             b.size = (k == 0) ? 'sm' : 'xs';
31863             b.x = (k == 0) ? 2 : 1;
31864             b.y = (k == 0) ? 2 : 1;
31865             
31866             b.el.position('absolute');
31867             
31868             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31869                 
31870             b.el.setWidth(width);
31871             
31872             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31873             
31874             b.el.setHeight(height);
31875             
31876         }, this);
31877
31878         var positions = [];
31879         
31880         positions.push({
31881             x : maxX - this.unitWidth * 2 - this.gutter,
31882             y : minY
31883         });
31884         
31885         positions.push({
31886             x : maxX - this.unitWidth,
31887             y : minY + (this.unitWidth + this.gutter) * 2
31888         });
31889         
31890         positions.push({
31891             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31892             y : minY
31893         });
31894         
31895         Roo.each(eItems, function(b,k){
31896             
31897             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31898
31899         }, this);
31900         
31901     },
31902     
31903     getVerticalOneBoxColPositions : function(x, y, box)
31904     {
31905         var pos = [];
31906         
31907         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31908         
31909         if(box[0].size == 'md-left'){
31910             rand = 0;
31911         }
31912         
31913         if(box[0].size == 'md-right'){
31914             rand = 1;
31915         }
31916         
31917         pos.push({
31918             x : x + (this.unitWidth + this.gutter) * rand,
31919             y : y
31920         });
31921         
31922         return pos;
31923     },
31924     
31925     getVerticalTwoBoxColPositions : function(x, y, box)
31926     {
31927         var pos = [];
31928         
31929         if(box[0].size == 'xs'){
31930             
31931             pos.push({
31932                 x : x,
31933                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31934             });
31935
31936             pos.push({
31937                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31938                 y : y
31939             });
31940             
31941             return pos;
31942             
31943         }
31944         
31945         pos.push({
31946             x : x,
31947             y : y
31948         });
31949
31950         pos.push({
31951             x : x + (this.unitWidth + this.gutter) * 2,
31952             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31953         });
31954         
31955         return pos;
31956         
31957     },
31958     
31959     getVerticalThreeBoxColPositions : function(x, y, box)
31960     {
31961         var pos = [];
31962         
31963         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31964             
31965             pos.push({
31966                 x : x,
31967                 y : y
31968             });
31969
31970             pos.push({
31971                 x : x + (this.unitWidth + this.gutter) * 1,
31972                 y : y
31973             });
31974             
31975             pos.push({
31976                 x : x + (this.unitWidth + this.gutter) * 2,
31977                 y : y
31978             });
31979             
31980             return pos;
31981             
31982         }
31983         
31984         if(box[0].size == 'xs' && box[1].size == 'xs'){
31985             
31986             pos.push({
31987                 x : x,
31988                 y : y
31989             });
31990
31991             pos.push({
31992                 x : x,
31993                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31994             });
31995             
31996             pos.push({
31997                 x : x + (this.unitWidth + this.gutter) * 1,
31998                 y : y
31999             });
32000             
32001             return pos;
32002             
32003         }
32004         
32005         pos.push({
32006             x : x,
32007             y : y
32008         });
32009
32010         pos.push({
32011             x : x + (this.unitWidth + this.gutter) * 2,
32012             y : y
32013         });
32014
32015         pos.push({
32016             x : x + (this.unitWidth + this.gutter) * 2,
32017             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32018         });
32019             
32020         return pos;
32021         
32022     },
32023     
32024     getVerticalFourBoxColPositions : function(x, y, box)
32025     {
32026         var pos = [];
32027         
32028         if(box[0].size == 'xs'){
32029             
32030             pos.push({
32031                 x : x,
32032                 y : y
32033             });
32034
32035             pos.push({
32036                 x : x,
32037                 y : y + (this.unitHeight + this.gutter) * 1
32038             });
32039             
32040             pos.push({
32041                 x : x,
32042                 y : y + (this.unitHeight + this.gutter) * 2
32043             });
32044             
32045             pos.push({
32046                 x : x + (this.unitWidth + this.gutter) * 1,
32047                 y : y
32048             });
32049             
32050             return pos;
32051             
32052         }
32053         
32054         pos.push({
32055             x : x,
32056             y : y
32057         });
32058
32059         pos.push({
32060             x : x + (this.unitWidth + this.gutter) * 2,
32061             y : y
32062         });
32063
32064         pos.push({
32065             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32066             y : y + (this.unitHeight + this.gutter) * 1
32067         });
32068
32069         pos.push({
32070             x : x + (this.unitWidth + this.gutter) * 2,
32071             y : y + (this.unitWidth + this.gutter) * 2
32072         });
32073
32074         return pos;
32075         
32076     },
32077     
32078     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32079     {
32080         var pos = [];
32081         
32082         if(box[0].size == 'md-left'){
32083             pos.push({
32084                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32085                 y : minY
32086             });
32087             
32088             return pos;
32089         }
32090         
32091         if(box[0].size == 'md-right'){
32092             pos.push({
32093                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32094                 y : minY + (this.unitWidth + this.gutter) * 1
32095             });
32096             
32097             return pos;
32098         }
32099         
32100         var rand = Math.floor(Math.random() * (4 - box[0].y));
32101         
32102         pos.push({
32103             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32104             y : minY + (this.unitWidth + this.gutter) * rand
32105         });
32106         
32107         return pos;
32108         
32109     },
32110     
32111     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32112     {
32113         var pos = [];
32114         
32115         if(box[0].size == 'xs'){
32116             
32117             pos.push({
32118                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32119                 y : minY
32120             });
32121
32122             pos.push({
32123                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32124                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32125             });
32126             
32127             return pos;
32128             
32129         }
32130         
32131         pos.push({
32132             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32133             y : minY
32134         });
32135
32136         pos.push({
32137             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32138             y : minY + (this.unitWidth + this.gutter) * 2
32139         });
32140         
32141         return pos;
32142         
32143     },
32144     
32145     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32146     {
32147         var pos = [];
32148         
32149         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32150             
32151             pos.push({
32152                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32153                 y : minY
32154             });
32155
32156             pos.push({
32157                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32158                 y : minY + (this.unitWidth + this.gutter) * 1
32159             });
32160             
32161             pos.push({
32162                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32163                 y : minY + (this.unitWidth + this.gutter) * 2
32164             });
32165             
32166             return pos;
32167             
32168         }
32169         
32170         if(box[0].size == 'xs' && box[1].size == 'xs'){
32171             
32172             pos.push({
32173                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32174                 y : minY
32175             });
32176
32177             pos.push({
32178                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32179                 y : minY
32180             });
32181             
32182             pos.push({
32183                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32184                 y : minY + (this.unitWidth + this.gutter) * 1
32185             });
32186             
32187             return pos;
32188             
32189         }
32190         
32191         pos.push({
32192             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32193             y : minY
32194         });
32195
32196         pos.push({
32197             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32198             y : minY + (this.unitWidth + this.gutter) * 2
32199         });
32200
32201         pos.push({
32202             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32203             y : minY + (this.unitWidth + this.gutter) * 2
32204         });
32205             
32206         return pos;
32207         
32208     },
32209     
32210     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32211     {
32212         var pos = [];
32213         
32214         if(box[0].size == 'xs'){
32215             
32216             pos.push({
32217                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32218                 y : minY
32219             });
32220
32221             pos.push({
32222                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32223                 y : minY
32224             });
32225             
32226             pos.push({
32227                 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),
32228                 y : minY
32229             });
32230             
32231             pos.push({
32232                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32233                 y : minY + (this.unitWidth + this.gutter) * 1
32234             });
32235             
32236             return pos;
32237             
32238         }
32239         
32240         pos.push({
32241             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32242             y : minY
32243         });
32244         
32245         pos.push({
32246             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32247             y : minY + (this.unitWidth + this.gutter) * 2
32248         });
32249         
32250         pos.push({
32251             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32252             y : minY + (this.unitWidth + this.gutter) * 2
32253         });
32254         
32255         pos.push({
32256             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),
32257             y : minY + (this.unitWidth + this.gutter) * 2
32258         });
32259
32260         return pos;
32261         
32262     },
32263     
32264     /**
32265     * remove a Masonry Brick
32266     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32267     */
32268     removeBrick : function(brick_id)
32269     {
32270         if (!brick_id) {
32271             return;
32272         }
32273         
32274         for (var i = 0; i<this.bricks.length; i++) {
32275             if (this.bricks[i].id == brick_id) {
32276                 this.bricks.splice(i,1);
32277                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32278                 this.initial();
32279             }
32280         }
32281     },
32282     
32283     /**
32284     * adds a Masonry Brick
32285     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32286     */
32287     addBrick : function(cfg)
32288     {
32289         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32290         //this.register(cn);
32291         cn.parentId = this.id;
32292         cn.render(this.el);
32293         return cn;
32294     },
32295     
32296     /**
32297     * register a Masonry Brick
32298     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32299     */
32300     
32301     register : function(brick)
32302     {
32303         this.bricks.push(brick);
32304         brick.masonryId = this.id;
32305     },
32306     
32307     /**
32308     * clear all the Masonry Brick
32309     */
32310     clearAll : function()
32311     {
32312         this.bricks = [];
32313         //this.getChildContainer().dom.innerHTML = "";
32314         this.el.dom.innerHTML = '';
32315     },
32316     
32317     getSelected : function()
32318     {
32319         if (!this.selectedBrick) {
32320             return false;
32321         }
32322         
32323         return this.selectedBrick;
32324     }
32325 });
32326
32327 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32328     
32329     groups: {},
32330      /**
32331     * register a Masonry Layout
32332     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32333     */
32334     
32335     register : function(layout)
32336     {
32337         this.groups[layout.id] = layout;
32338     },
32339     /**
32340     * fetch a  Masonry Layout based on the masonry layout ID
32341     * @param {string} the masonry layout to add
32342     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32343     */
32344     
32345     get: function(layout_id) {
32346         if (typeof(this.groups[layout_id]) == 'undefined') {
32347             return false;
32348         }
32349         return this.groups[layout_id] ;
32350     }
32351     
32352     
32353     
32354 });
32355
32356  
32357
32358  /**
32359  *
32360  * This is based on 
32361  * http://masonry.desandro.com
32362  *
32363  * The idea is to render all the bricks based on vertical width...
32364  *
32365  * The original code extends 'outlayer' - we might need to use that....
32366  * 
32367  */
32368
32369
32370 /**
32371  * @class Roo.bootstrap.LayoutMasonryAuto
32372  * @extends Roo.bootstrap.Component
32373  * Bootstrap Layout Masonry class
32374  * 
32375  * @constructor
32376  * Create a new Element
32377  * @param {Object} config The config object
32378  */
32379
32380 Roo.bootstrap.LayoutMasonryAuto = function(config){
32381     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32382 };
32383
32384 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32385     
32386       /**
32387      * @cfg {Boolean} isFitWidth  - resize the width..
32388      */   
32389     isFitWidth : false,  // options..
32390     /**
32391      * @cfg {Boolean} isOriginLeft = left align?
32392      */   
32393     isOriginLeft : true,
32394     /**
32395      * @cfg {Boolean} isOriginTop = top align?
32396      */   
32397     isOriginTop : false,
32398     /**
32399      * @cfg {Boolean} isLayoutInstant = no animation?
32400      */   
32401     isLayoutInstant : false, // needed?
32402     /**
32403      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32404      */   
32405     isResizingContainer : true,
32406     /**
32407      * @cfg {Number} columnWidth  width of the columns 
32408      */   
32409     
32410     columnWidth : 0,
32411     
32412     /**
32413      * @cfg {Number} maxCols maximum number of columns
32414      */   
32415     
32416     maxCols: 0,
32417     /**
32418      * @cfg {Number} padHeight padding below box..
32419      */   
32420     
32421     padHeight : 10, 
32422     
32423     /**
32424      * @cfg {Boolean} isAutoInitial defalut true
32425      */   
32426     
32427     isAutoInitial : true, 
32428     
32429     // private?
32430     gutter : 0,
32431     
32432     containerWidth: 0,
32433     initialColumnWidth : 0,
32434     currentSize : null,
32435     
32436     colYs : null, // array.
32437     maxY : 0,
32438     padWidth: 10,
32439     
32440     
32441     tag: 'div',
32442     cls: '',
32443     bricks: null, //CompositeElement
32444     cols : 0, // array?
32445     // element : null, // wrapped now this.el
32446     _isLayoutInited : null, 
32447     
32448     
32449     getAutoCreate : function(){
32450         
32451         var cfg = {
32452             tag: this.tag,
32453             cls: 'blog-masonary-wrapper ' + this.cls,
32454             cn : {
32455                 cls : 'mas-boxes masonary'
32456             }
32457         };
32458         
32459         return cfg;
32460     },
32461     
32462     getChildContainer: function( )
32463     {
32464         if (this.boxesEl) {
32465             return this.boxesEl;
32466         }
32467         
32468         this.boxesEl = this.el.select('.mas-boxes').first();
32469         
32470         return this.boxesEl;
32471     },
32472     
32473     
32474     initEvents : function()
32475     {
32476         var _this = this;
32477         
32478         if(this.isAutoInitial){
32479             Roo.log('hook children rendered');
32480             this.on('childrenrendered', function() {
32481                 Roo.log('children rendered');
32482                 _this.initial();
32483             } ,this);
32484         }
32485         
32486     },
32487     
32488     initial : function()
32489     {
32490         this.reloadItems();
32491
32492         this.currentSize = this.el.getBox(true);
32493
32494         /// was window resize... - let's see if this works..
32495         Roo.EventManager.onWindowResize(this.resize, this); 
32496
32497         if(!this.isAutoInitial){
32498             this.layout();
32499             return;
32500         }
32501         
32502         this.layout.defer(500,this);
32503     },
32504     
32505     reloadItems: function()
32506     {
32507         this.bricks = this.el.select('.masonry-brick', true);
32508         
32509         this.bricks.each(function(b) {
32510             //Roo.log(b.getSize());
32511             if (!b.attr('originalwidth')) {
32512                 b.attr('originalwidth',  b.getSize().width);
32513             }
32514             
32515         });
32516         
32517         Roo.log(this.bricks.elements.length);
32518     },
32519     
32520     resize : function()
32521     {
32522         Roo.log('resize');
32523         var cs = this.el.getBox(true);
32524         
32525         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32526             Roo.log("no change in with or X");
32527             return;
32528         }
32529         this.currentSize = cs;
32530         this.layout();
32531     },
32532     
32533     layout : function()
32534     {
32535          Roo.log('layout');
32536         this._resetLayout();
32537         //this._manageStamps();
32538       
32539         // don't animate first layout
32540         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32541         this.layoutItems( isInstant );
32542       
32543         // flag for initalized
32544         this._isLayoutInited = true;
32545     },
32546     
32547     layoutItems : function( isInstant )
32548     {
32549         //var items = this._getItemsForLayout( this.items );
32550         // original code supports filtering layout items.. we just ignore it..
32551         
32552         this._layoutItems( this.bricks , isInstant );
32553       
32554         this._postLayout();
32555     },
32556     _layoutItems : function ( items , isInstant)
32557     {
32558        //this.fireEvent( 'layout', this, items );
32559     
32560
32561         if ( !items || !items.elements.length ) {
32562           // no items, emit event with empty array
32563             return;
32564         }
32565
32566         var queue = [];
32567         items.each(function(item) {
32568             Roo.log("layout item");
32569             Roo.log(item);
32570             // get x/y object from method
32571             var position = this._getItemLayoutPosition( item );
32572             // enqueue
32573             position.item = item;
32574             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32575             queue.push( position );
32576         }, this);
32577       
32578         this._processLayoutQueue( queue );
32579     },
32580     /** Sets position of item in DOM
32581     * @param {Element} item
32582     * @param {Number} x - horizontal position
32583     * @param {Number} y - vertical position
32584     * @param {Boolean} isInstant - disables transitions
32585     */
32586     _processLayoutQueue : function( queue )
32587     {
32588         for ( var i=0, len = queue.length; i < len; i++ ) {
32589             var obj = queue[i];
32590             obj.item.position('absolute');
32591             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32592         }
32593     },
32594       
32595     
32596     /**
32597     * Any logic you want to do after each layout,
32598     * i.e. size the container
32599     */
32600     _postLayout : function()
32601     {
32602         this.resizeContainer();
32603     },
32604     
32605     resizeContainer : function()
32606     {
32607         if ( !this.isResizingContainer ) {
32608             return;
32609         }
32610         var size = this._getContainerSize();
32611         if ( size ) {
32612             this.el.setSize(size.width,size.height);
32613             this.boxesEl.setSize(size.width,size.height);
32614         }
32615     },
32616     
32617     
32618     
32619     _resetLayout : function()
32620     {
32621         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32622         this.colWidth = this.el.getWidth();
32623         //this.gutter = this.el.getWidth(); 
32624         
32625         this.measureColumns();
32626
32627         // reset column Y
32628         var i = this.cols;
32629         this.colYs = [];
32630         while (i--) {
32631             this.colYs.push( 0 );
32632         }
32633     
32634         this.maxY = 0;
32635     },
32636
32637     measureColumns : function()
32638     {
32639         this.getContainerWidth();
32640       // if columnWidth is 0, default to outerWidth of first item
32641         if ( !this.columnWidth ) {
32642             var firstItem = this.bricks.first();
32643             Roo.log(firstItem);
32644             this.columnWidth  = this.containerWidth;
32645             if (firstItem && firstItem.attr('originalwidth') ) {
32646                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32647             }
32648             // columnWidth fall back to item of first element
32649             Roo.log("set column width?");
32650                         this.initialColumnWidth = this.columnWidth  ;
32651
32652             // if first elem has no width, default to size of container
32653             
32654         }
32655         
32656         
32657         if (this.initialColumnWidth) {
32658             this.columnWidth = this.initialColumnWidth;
32659         }
32660         
32661         
32662             
32663         // column width is fixed at the top - however if container width get's smaller we should
32664         // reduce it...
32665         
32666         // this bit calcs how man columns..
32667             
32668         var columnWidth = this.columnWidth += this.gutter;
32669       
32670         // calculate columns
32671         var containerWidth = this.containerWidth + this.gutter;
32672         
32673         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32674         // fix rounding errors, typically with gutters
32675         var excess = columnWidth - containerWidth % columnWidth;
32676         
32677         
32678         // if overshoot is less than a pixel, round up, otherwise floor it
32679         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32680         cols = Math[ mathMethod ]( cols );
32681         this.cols = Math.max( cols, 1 );
32682         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32683         
32684          // padding positioning..
32685         var totalColWidth = this.cols * this.columnWidth;
32686         var padavail = this.containerWidth - totalColWidth;
32687         // so for 2 columns - we need 3 'pads'
32688         
32689         var padNeeded = (1+this.cols) * this.padWidth;
32690         
32691         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32692         
32693         this.columnWidth += padExtra
32694         //this.padWidth = Math.floor(padavail /  ( this.cols));
32695         
32696         // adjust colum width so that padding is fixed??
32697         
32698         // we have 3 columns ... total = width * 3
32699         // we have X left over... that should be used by 
32700         
32701         //if (this.expandC) {
32702             
32703         //}
32704         
32705         
32706         
32707     },
32708     
32709     getContainerWidth : function()
32710     {
32711        /* // container is parent if fit width
32712         var container = this.isFitWidth ? this.element.parentNode : this.element;
32713         // check that this.size and size are there
32714         // IE8 triggers resize on body size change, so they might not be
32715         
32716         var size = getSize( container );  //FIXME
32717         this.containerWidth = size && size.innerWidth; //FIXME
32718         */
32719          
32720         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32721         
32722     },
32723     
32724     _getItemLayoutPosition : function( item )  // what is item?
32725     {
32726         // we resize the item to our columnWidth..
32727       
32728         item.setWidth(this.columnWidth);
32729         item.autoBoxAdjust  = false;
32730         
32731         var sz = item.getSize();
32732  
32733         // how many columns does this brick span
32734         var remainder = this.containerWidth % this.columnWidth;
32735         
32736         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32737         // round if off by 1 pixel, otherwise use ceil
32738         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32739         colSpan = Math.min( colSpan, this.cols );
32740         
32741         // normally this should be '1' as we dont' currently allow multi width columns..
32742         
32743         var colGroup = this._getColGroup( colSpan );
32744         // get the minimum Y value from the columns
32745         var minimumY = Math.min.apply( Math, colGroup );
32746         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32747         
32748         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32749          
32750         // position the brick
32751         var position = {
32752             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32753             y: this.currentSize.y + minimumY + this.padHeight
32754         };
32755         
32756         Roo.log(position);
32757         // apply setHeight to necessary columns
32758         var setHeight = minimumY + sz.height + this.padHeight;
32759         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32760         
32761         var setSpan = this.cols + 1 - colGroup.length;
32762         for ( var i = 0; i < setSpan; i++ ) {
32763           this.colYs[ shortColIndex + i ] = setHeight ;
32764         }
32765       
32766         return position;
32767     },
32768     
32769     /**
32770      * @param {Number} colSpan - number of columns the element spans
32771      * @returns {Array} colGroup
32772      */
32773     _getColGroup : function( colSpan )
32774     {
32775         if ( colSpan < 2 ) {
32776           // if brick spans only one column, use all the column Ys
32777           return this.colYs;
32778         }
32779       
32780         var colGroup = [];
32781         // how many different places could this brick fit horizontally
32782         var groupCount = this.cols + 1 - colSpan;
32783         // for each group potential horizontal position
32784         for ( var i = 0; i < groupCount; i++ ) {
32785           // make an array of colY values for that one group
32786           var groupColYs = this.colYs.slice( i, i + colSpan );
32787           // and get the max value of the array
32788           colGroup[i] = Math.max.apply( Math, groupColYs );
32789         }
32790         return colGroup;
32791     },
32792     /*
32793     _manageStamp : function( stamp )
32794     {
32795         var stampSize =  stamp.getSize();
32796         var offset = stamp.getBox();
32797         // get the columns that this stamp affects
32798         var firstX = this.isOriginLeft ? offset.x : offset.right;
32799         var lastX = firstX + stampSize.width;
32800         var firstCol = Math.floor( firstX / this.columnWidth );
32801         firstCol = Math.max( 0, firstCol );
32802         
32803         var lastCol = Math.floor( lastX / this.columnWidth );
32804         // lastCol should not go over if multiple of columnWidth #425
32805         lastCol -= lastX % this.columnWidth ? 0 : 1;
32806         lastCol = Math.min( this.cols - 1, lastCol );
32807         
32808         // set colYs to bottom of the stamp
32809         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32810             stampSize.height;
32811             
32812         for ( var i = firstCol; i <= lastCol; i++ ) {
32813           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32814         }
32815     },
32816     */
32817     
32818     _getContainerSize : function()
32819     {
32820         this.maxY = Math.max.apply( Math, this.colYs );
32821         var size = {
32822             height: this.maxY
32823         };
32824       
32825         if ( this.isFitWidth ) {
32826             size.width = this._getContainerFitWidth();
32827         }
32828       
32829         return size;
32830     },
32831     
32832     _getContainerFitWidth : function()
32833     {
32834         var unusedCols = 0;
32835         // count unused columns
32836         var i = this.cols;
32837         while ( --i ) {
32838           if ( this.colYs[i] !== 0 ) {
32839             break;
32840           }
32841           unusedCols++;
32842         }
32843         // fit container to columns that have been used
32844         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32845     },
32846     
32847     needsResizeLayout : function()
32848     {
32849         var previousWidth = this.containerWidth;
32850         this.getContainerWidth();
32851         return previousWidth !== this.containerWidth;
32852     }
32853  
32854 });
32855
32856  
32857
32858  /*
32859  * - LGPL
32860  *
32861  * element
32862  * 
32863  */
32864
32865 /**
32866  * @class Roo.bootstrap.MasonryBrick
32867  * @extends Roo.bootstrap.Component
32868  * Bootstrap MasonryBrick class
32869  * 
32870  * @constructor
32871  * Create a new MasonryBrick
32872  * @param {Object} config The config object
32873  */
32874
32875 Roo.bootstrap.MasonryBrick = function(config){
32876     
32877     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32878     
32879     Roo.bootstrap.MasonryBrick.register(this);
32880     
32881     this.addEvents({
32882         // raw events
32883         /**
32884          * @event click
32885          * When a MasonryBrick is clcik
32886          * @param {Roo.bootstrap.MasonryBrick} this
32887          * @param {Roo.EventObject} e
32888          */
32889         "click" : true
32890     });
32891 };
32892
32893 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32894     
32895     /**
32896      * @cfg {String} title
32897      */   
32898     title : '',
32899     /**
32900      * @cfg {String} html
32901      */   
32902     html : '',
32903     /**
32904      * @cfg {String} bgimage
32905      */   
32906     bgimage : '',
32907     /**
32908      * @cfg {String} videourl
32909      */   
32910     videourl : '',
32911     /**
32912      * @cfg {String} cls
32913      */   
32914     cls : '',
32915     /**
32916      * @cfg {String} href
32917      */   
32918     href : '',
32919     /**
32920      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32921      */   
32922     size : 'xs',
32923     
32924     /**
32925      * @cfg {String} placetitle (center|bottom)
32926      */   
32927     placetitle : '',
32928     
32929     /**
32930      * @cfg {Boolean} isFitContainer defalut true
32931      */   
32932     isFitContainer : true, 
32933     
32934     /**
32935      * @cfg {Boolean} preventDefault defalut false
32936      */   
32937     preventDefault : false, 
32938     
32939     /**
32940      * @cfg {Boolean} inverse defalut false
32941      */   
32942     maskInverse : false, 
32943     
32944     getAutoCreate : function()
32945     {
32946         if(!this.isFitContainer){
32947             return this.getSplitAutoCreate();
32948         }
32949         
32950         var cls = 'masonry-brick masonry-brick-full';
32951         
32952         if(this.href.length){
32953             cls += ' masonry-brick-link';
32954         }
32955         
32956         if(this.bgimage.length){
32957             cls += ' masonry-brick-image';
32958         }
32959         
32960         if(this.maskInverse){
32961             cls += ' mask-inverse';
32962         }
32963         
32964         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32965             cls += ' enable-mask';
32966         }
32967         
32968         if(this.size){
32969             cls += ' masonry-' + this.size + '-brick';
32970         }
32971         
32972         if(this.placetitle.length){
32973             
32974             switch (this.placetitle) {
32975                 case 'center' :
32976                     cls += ' masonry-center-title';
32977                     break;
32978                 case 'bottom' :
32979                     cls += ' masonry-bottom-title';
32980                     break;
32981                 default:
32982                     break;
32983             }
32984             
32985         } else {
32986             if(!this.html.length && !this.bgimage.length){
32987                 cls += ' masonry-center-title';
32988             }
32989
32990             if(!this.html.length && this.bgimage.length){
32991                 cls += ' masonry-bottom-title';
32992             }
32993         }
32994         
32995         if(this.cls){
32996             cls += ' ' + this.cls;
32997         }
32998         
32999         var cfg = {
33000             tag: (this.href.length) ? 'a' : 'div',
33001             cls: cls,
33002             cn: [
33003                 {
33004                     tag: 'div',
33005                     cls: 'masonry-brick-mask'
33006                 },
33007                 {
33008                     tag: 'div',
33009                     cls: 'masonry-brick-paragraph',
33010                     cn: []
33011                 }
33012             ]
33013         };
33014         
33015         if(this.href.length){
33016             cfg.href = this.href;
33017         }
33018         
33019         var cn = cfg.cn[1].cn;
33020         
33021         if(this.title.length){
33022             cn.push({
33023                 tag: 'h4',
33024                 cls: 'masonry-brick-title',
33025                 html: this.title
33026             });
33027         }
33028         
33029         if(this.html.length){
33030             cn.push({
33031                 tag: 'p',
33032                 cls: 'masonry-brick-text',
33033                 html: this.html
33034             });
33035         }
33036         
33037         if (!this.title.length && !this.html.length) {
33038             cfg.cn[1].cls += ' hide';
33039         }
33040         
33041         if(this.bgimage.length){
33042             cfg.cn.push({
33043                 tag: 'img',
33044                 cls: 'masonry-brick-image-view',
33045                 src: this.bgimage
33046             });
33047         }
33048         
33049         if(this.videourl.length){
33050             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33051             // youtube support only?
33052             cfg.cn.push({
33053                 tag: 'iframe',
33054                 cls: 'masonry-brick-image-view',
33055                 src: vurl,
33056                 frameborder : 0,
33057                 allowfullscreen : true
33058             });
33059         }
33060         
33061         return cfg;
33062         
33063     },
33064     
33065     getSplitAutoCreate : function()
33066     {
33067         var cls = 'masonry-brick masonry-brick-split';
33068         
33069         if(this.href.length){
33070             cls += ' masonry-brick-link';
33071         }
33072         
33073         if(this.bgimage.length){
33074             cls += ' masonry-brick-image';
33075         }
33076         
33077         if(this.size){
33078             cls += ' masonry-' + this.size + '-brick';
33079         }
33080         
33081         switch (this.placetitle) {
33082             case 'center' :
33083                 cls += ' masonry-center-title';
33084                 break;
33085             case 'bottom' :
33086                 cls += ' masonry-bottom-title';
33087                 break;
33088             default:
33089                 if(!this.bgimage.length){
33090                     cls += ' masonry-center-title';
33091                 }
33092
33093                 if(this.bgimage.length){
33094                     cls += ' masonry-bottom-title';
33095                 }
33096                 break;
33097         }
33098         
33099         if(this.cls){
33100             cls += ' ' + this.cls;
33101         }
33102         
33103         var cfg = {
33104             tag: (this.href.length) ? 'a' : 'div',
33105             cls: cls,
33106             cn: [
33107                 {
33108                     tag: 'div',
33109                     cls: 'masonry-brick-split-head',
33110                     cn: [
33111                         {
33112                             tag: 'div',
33113                             cls: 'masonry-brick-paragraph',
33114                             cn: []
33115                         }
33116                     ]
33117                 },
33118                 {
33119                     tag: 'div',
33120                     cls: 'masonry-brick-split-body',
33121                     cn: []
33122                 }
33123             ]
33124         };
33125         
33126         if(this.href.length){
33127             cfg.href = this.href;
33128         }
33129         
33130         if(this.title.length){
33131             cfg.cn[0].cn[0].cn.push({
33132                 tag: 'h4',
33133                 cls: 'masonry-brick-title',
33134                 html: this.title
33135             });
33136         }
33137         
33138         if(this.html.length){
33139             cfg.cn[1].cn.push({
33140                 tag: 'p',
33141                 cls: 'masonry-brick-text',
33142                 html: this.html
33143             });
33144         }
33145
33146         if(this.bgimage.length){
33147             cfg.cn[0].cn.push({
33148                 tag: 'img',
33149                 cls: 'masonry-brick-image-view',
33150                 src: this.bgimage
33151             });
33152         }
33153         
33154         if(this.videourl.length){
33155             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33156             // youtube support only?
33157             cfg.cn[0].cn.cn.push({
33158                 tag: 'iframe',
33159                 cls: 'masonry-brick-image-view',
33160                 src: vurl,
33161                 frameborder : 0,
33162                 allowfullscreen : true
33163             });
33164         }
33165         
33166         return cfg;
33167     },
33168     
33169     initEvents: function() 
33170     {
33171         switch (this.size) {
33172             case 'xs' :
33173                 this.x = 1;
33174                 this.y = 1;
33175                 break;
33176             case 'sm' :
33177                 this.x = 2;
33178                 this.y = 2;
33179                 break;
33180             case 'md' :
33181             case 'md-left' :
33182             case 'md-right' :
33183                 this.x = 3;
33184                 this.y = 3;
33185                 break;
33186             case 'tall' :
33187                 this.x = 2;
33188                 this.y = 3;
33189                 break;
33190             case 'wide' :
33191                 this.x = 3;
33192                 this.y = 2;
33193                 break;
33194             case 'wide-thin' :
33195                 this.x = 3;
33196                 this.y = 1;
33197                 break;
33198                         
33199             default :
33200                 break;
33201         }
33202         
33203         if(Roo.isTouch){
33204             this.el.on('touchstart', this.onTouchStart, this);
33205             this.el.on('touchmove', this.onTouchMove, this);
33206             this.el.on('touchend', this.onTouchEnd, this);
33207             this.el.on('contextmenu', this.onContextMenu, this);
33208         } else {
33209             this.el.on('mouseenter'  ,this.enter, this);
33210             this.el.on('mouseleave', this.leave, this);
33211             this.el.on('click', this.onClick, this);
33212         }
33213         
33214         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33215             this.parent().bricks.push(this);   
33216         }
33217         
33218     },
33219     
33220     onClick: function(e, el)
33221     {
33222         var time = this.endTimer - this.startTimer;
33223         // Roo.log(e.preventDefault());
33224         if(Roo.isTouch){
33225             if(time > 1000){
33226                 e.preventDefault();
33227                 return;
33228             }
33229         }
33230         
33231         if(!this.preventDefault){
33232             return;
33233         }
33234         
33235         e.preventDefault();
33236         
33237         if (this.activeClass != '') {
33238             this.selectBrick();
33239         }
33240         
33241         this.fireEvent('click', this, e);
33242     },
33243     
33244     enter: function(e, el)
33245     {
33246         e.preventDefault();
33247         
33248         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33249             return;
33250         }
33251         
33252         if(this.bgimage.length && this.html.length){
33253             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33254         }
33255     },
33256     
33257     leave: function(e, el)
33258     {
33259         e.preventDefault();
33260         
33261         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33262             return;
33263         }
33264         
33265         if(this.bgimage.length && this.html.length){
33266             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33267         }
33268     },
33269     
33270     onTouchStart: function(e, el)
33271     {
33272 //        e.preventDefault();
33273         
33274         this.touchmoved = false;
33275         
33276         if(!this.isFitContainer){
33277             return;
33278         }
33279         
33280         if(!this.bgimage.length || !this.html.length){
33281             return;
33282         }
33283         
33284         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33285         
33286         this.timer = new Date().getTime();
33287         
33288     },
33289     
33290     onTouchMove: function(e, el)
33291     {
33292         this.touchmoved = true;
33293     },
33294     
33295     onContextMenu : function(e,el)
33296     {
33297         e.preventDefault();
33298         e.stopPropagation();
33299         return false;
33300     },
33301     
33302     onTouchEnd: function(e, el)
33303     {
33304 //        e.preventDefault();
33305         
33306         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33307         
33308             this.leave(e,el);
33309             
33310             return;
33311         }
33312         
33313         if(!this.bgimage.length || !this.html.length){
33314             
33315             if(this.href.length){
33316                 window.location.href = this.href;
33317             }
33318             
33319             return;
33320         }
33321         
33322         if(!this.isFitContainer){
33323             return;
33324         }
33325         
33326         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33327         
33328         window.location.href = this.href;
33329     },
33330     
33331     //selection on single brick only
33332     selectBrick : function() {
33333         
33334         if (!this.parentId) {
33335             return;
33336         }
33337         
33338         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33339         var index = m.selectedBrick.indexOf(this.id);
33340         
33341         if ( index > -1) {
33342             m.selectedBrick.splice(index,1);
33343             this.el.removeClass(this.activeClass);
33344             return;
33345         }
33346         
33347         for(var i = 0; i < m.selectedBrick.length; i++) {
33348             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33349             b.el.removeClass(b.activeClass);
33350         }
33351         
33352         m.selectedBrick = [];
33353         
33354         m.selectedBrick.push(this.id);
33355         this.el.addClass(this.activeClass);
33356         return;
33357     },
33358     
33359     isSelected : function(){
33360         return this.el.hasClass(this.activeClass);
33361         
33362     }
33363 });
33364
33365 Roo.apply(Roo.bootstrap.MasonryBrick, {
33366     
33367     //groups: {},
33368     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33369      /**
33370     * register a Masonry Brick
33371     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33372     */
33373     
33374     register : function(brick)
33375     {
33376         //this.groups[brick.id] = brick;
33377         this.groups.add(brick.id, brick);
33378     },
33379     /**
33380     * fetch a  masonry brick based on the masonry brick ID
33381     * @param {string} the masonry brick to add
33382     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33383     */
33384     
33385     get: function(brick_id) 
33386     {
33387         // if (typeof(this.groups[brick_id]) == 'undefined') {
33388         //     return false;
33389         // }
33390         // return this.groups[brick_id] ;
33391         
33392         if(this.groups.key(brick_id)) {
33393             return this.groups.key(brick_id);
33394         }
33395         
33396         return false;
33397     }
33398     
33399     
33400     
33401 });
33402
33403  /*
33404  * - LGPL
33405  *
33406  * element
33407  * 
33408  */
33409
33410 /**
33411  * @class Roo.bootstrap.Brick
33412  * @extends Roo.bootstrap.Component
33413  * Bootstrap Brick class
33414  * 
33415  * @constructor
33416  * Create a new Brick
33417  * @param {Object} config The config object
33418  */
33419
33420 Roo.bootstrap.Brick = function(config){
33421     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33422     
33423     this.addEvents({
33424         // raw events
33425         /**
33426          * @event click
33427          * When a Brick is click
33428          * @param {Roo.bootstrap.Brick} this
33429          * @param {Roo.EventObject} e
33430          */
33431         "click" : true
33432     });
33433 };
33434
33435 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33436     
33437     /**
33438      * @cfg {String} title
33439      */   
33440     title : '',
33441     /**
33442      * @cfg {String} html
33443      */   
33444     html : '',
33445     /**
33446      * @cfg {String} bgimage
33447      */   
33448     bgimage : '',
33449     /**
33450      * @cfg {String} cls
33451      */   
33452     cls : '',
33453     /**
33454      * @cfg {String} href
33455      */   
33456     href : '',
33457     /**
33458      * @cfg {String} video
33459      */   
33460     video : '',
33461     /**
33462      * @cfg {Boolean} square
33463      */   
33464     square : true,
33465     
33466     getAutoCreate : function()
33467     {
33468         var cls = 'roo-brick';
33469         
33470         if(this.href.length){
33471             cls += ' roo-brick-link';
33472         }
33473         
33474         if(this.bgimage.length){
33475             cls += ' roo-brick-image';
33476         }
33477         
33478         if(!this.html.length && !this.bgimage.length){
33479             cls += ' roo-brick-center-title';
33480         }
33481         
33482         if(!this.html.length && this.bgimage.length){
33483             cls += ' roo-brick-bottom-title';
33484         }
33485         
33486         if(this.cls){
33487             cls += ' ' + this.cls;
33488         }
33489         
33490         var cfg = {
33491             tag: (this.href.length) ? 'a' : 'div',
33492             cls: cls,
33493             cn: [
33494                 {
33495                     tag: 'div',
33496                     cls: 'roo-brick-paragraph',
33497                     cn: []
33498                 }
33499             ]
33500         };
33501         
33502         if(this.href.length){
33503             cfg.href = this.href;
33504         }
33505         
33506         var cn = cfg.cn[0].cn;
33507         
33508         if(this.title.length){
33509             cn.push({
33510                 tag: 'h4',
33511                 cls: 'roo-brick-title',
33512                 html: this.title
33513             });
33514         }
33515         
33516         if(this.html.length){
33517             cn.push({
33518                 tag: 'p',
33519                 cls: 'roo-brick-text',
33520                 html: this.html
33521             });
33522         } else {
33523             cn.cls += ' hide';
33524         }
33525         
33526         if(this.bgimage.length){
33527             cfg.cn.push({
33528                 tag: 'img',
33529                 cls: 'roo-brick-image-view',
33530                 src: this.bgimage
33531             });
33532         }
33533         
33534         return cfg;
33535     },
33536     
33537     initEvents: function() 
33538     {
33539         if(this.title.length || this.html.length){
33540             this.el.on('mouseenter'  ,this.enter, this);
33541             this.el.on('mouseleave', this.leave, this);
33542         }
33543         
33544         Roo.EventManager.onWindowResize(this.resize, this); 
33545         
33546         if(this.bgimage.length){
33547             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33548             this.imageEl.on('load', this.onImageLoad, this);
33549             return;
33550         }
33551         
33552         this.resize();
33553     },
33554     
33555     onImageLoad : function()
33556     {
33557         this.resize();
33558     },
33559     
33560     resize : function()
33561     {
33562         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33563         
33564         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33565         
33566         if(this.bgimage.length){
33567             var image = this.el.select('.roo-brick-image-view', true).first();
33568             
33569             image.setWidth(paragraph.getWidth());
33570             
33571             if(this.square){
33572                 image.setHeight(paragraph.getWidth());
33573             }
33574             
33575             this.el.setHeight(image.getHeight());
33576             paragraph.setHeight(image.getHeight());
33577             
33578         }
33579         
33580     },
33581     
33582     enter: function(e, el)
33583     {
33584         e.preventDefault();
33585         
33586         if(this.bgimage.length){
33587             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33588             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33589         }
33590     },
33591     
33592     leave: function(e, el)
33593     {
33594         e.preventDefault();
33595         
33596         if(this.bgimage.length){
33597             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33598             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33599         }
33600     }
33601     
33602 });
33603
33604  
33605
33606  /*
33607  * - LGPL
33608  *
33609  * Number field 
33610  */
33611
33612 /**
33613  * @class Roo.bootstrap.NumberField
33614  * @extends Roo.bootstrap.Input
33615  * Bootstrap NumberField class
33616  * 
33617  * 
33618  * 
33619  * 
33620  * @constructor
33621  * Create a new NumberField
33622  * @param {Object} config The config object
33623  */
33624
33625 Roo.bootstrap.NumberField = function(config){
33626     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33627 };
33628
33629 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33630     
33631     /**
33632      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33633      */
33634     allowDecimals : true,
33635     /**
33636      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33637      */
33638     decimalSeparator : ".",
33639     /**
33640      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33641      */
33642     decimalPrecision : 2,
33643     /**
33644      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33645      */
33646     allowNegative : true,
33647     
33648     /**
33649      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33650      */
33651     allowZero: true,
33652     /**
33653      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33654      */
33655     minValue : Number.NEGATIVE_INFINITY,
33656     /**
33657      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33658      */
33659     maxValue : Number.MAX_VALUE,
33660     /**
33661      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33662      */
33663     minText : "The minimum value for this field is {0}",
33664     /**
33665      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33666      */
33667     maxText : "The maximum value for this field is {0}",
33668     /**
33669      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33670      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33671      */
33672     nanText : "{0} is not a valid number",
33673     /**
33674      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33675      */
33676     thousandsDelimiter : false,
33677     /**
33678      * @cfg {String} valueAlign alignment of value
33679      */
33680     valueAlign : "left",
33681
33682     getAutoCreate : function()
33683     {
33684         var hiddenInput = {
33685             tag: 'input',
33686             type: 'hidden',
33687             id: Roo.id(),
33688             cls: 'hidden-number-input'
33689         };
33690         
33691         if (this.name) {
33692             hiddenInput.name = this.name;
33693         }
33694         
33695         this.name = '';
33696         
33697         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33698         
33699         this.name = hiddenInput.name;
33700         
33701         if(cfg.cn.length > 0) {
33702             cfg.cn.push(hiddenInput);
33703         }
33704         
33705         return cfg;
33706     },
33707
33708     // private
33709     initEvents : function()
33710     {   
33711         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33712         
33713         var allowed = "0123456789";
33714         
33715         if(this.allowDecimals){
33716             allowed += this.decimalSeparator;
33717         }
33718         
33719         if(this.allowNegative){
33720             allowed += "-";
33721         }
33722         
33723         if(this.thousandsDelimiter) {
33724             allowed += ",";
33725         }
33726         
33727         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33728         
33729         var keyPress = function(e){
33730             
33731             var k = e.getKey();
33732             
33733             var c = e.getCharCode();
33734             
33735             if(
33736                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33737                     allowed.indexOf(String.fromCharCode(c)) === -1
33738             ){
33739                 e.stopEvent();
33740                 return;
33741             }
33742             
33743             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33744                 return;
33745             }
33746             
33747             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33748                 e.stopEvent();
33749             }
33750         };
33751         
33752         this.el.on("keypress", keyPress, this);
33753     },
33754     
33755     validateValue : function(value)
33756     {
33757         
33758         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33759             return false;
33760         }
33761         
33762         var num = this.parseValue(value);
33763         
33764         if(isNaN(num)){
33765             this.markInvalid(String.format(this.nanText, value));
33766             return false;
33767         }
33768         
33769         if(num < this.minValue){
33770             this.markInvalid(String.format(this.minText, this.minValue));
33771             return false;
33772         }
33773         
33774         if(num > this.maxValue){
33775             this.markInvalid(String.format(this.maxText, this.maxValue));
33776             return false;
33777         }
33778         
33779         return true;
33780     },
33781
33782     getValue : function()
33783     {
33784         var v = this.hiddenEl().getValue();
33785         
33786         return this.fixPrecision(this.parseValue(v));
33787     },
33788
33789     parseValue : function(value)
33790     {
33791         if(this.thousandsDelimiter) {
33792             value += "";
33793             r = new RegExp(",", "g");
33794             value = value.replace(r, "");
33795         }
33796         
33797         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33798         return isNaN(value) ? '' : value;
33799     },
33800
33801     fixPrecision : function(value)
33802     {
33803         if(this.thousandsDelimiter) {
33804             value += "";
33805             r = new RegExp(",", "g");
33806             value = value.replace(r, "");
33807         }
33808         
33809         var nan = isNaN(value);
33810         
33811         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33812             return nan ? '' : value;
33813         }
33814         return parseFloat(value).toFixed(this.decimalPrecision);
33815     },
33816
33817     setValue : function(v)
33818     {
33819         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33820         
33821         this.value = v;
33822         
33823         if(this.rendered){
33824             
33825             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33826             
33827             this.inputEl().dom.value = (v == '') ? '' :
33828                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33829             
33830             if(!this.allowZero && v === '0') {
33831                 this.hiddenEl().dom.value = '';
33832                 this.inputEl().dom.value = '';
33833             }
33834             
33835             this.validate();
33836         }
33837     },
33838
33839     decimalPrecisionFcn : function(v)
33840     {
33841         return Math.floor(v);
33842     },
33843
33844     beforeBlur : function()
33845     {
33846         var v = this.parseValue(this.getRawValue());
33847         
33848         if(v || v === 0 || v === ''){
33849             this.setValue(v);
33850         }
33851     },
33852     
33853     hiddenEl : function()
33854     {
33855         return this.el.select('input.hidden-number-input',true).first();
33856     }
33857     
33858 });
33859
33860  
33861
33862 /*
33863 * Licence: LGPL
33864 */
33865
33866 /**
33867  * @class Roo.bootstrap.DocumentSlider
33868  * @extends Roo.bootstrap.Component
33869  * Bootstrap DocumentSlider class
33870  * 
33871  * @constructor
33872  * Create a new DocumentViewer
33873  * @param {Object} config The config object
33874  */
33875
33876 Roo.bootstrap.DocumentSlider = function(config){
33877     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33878     
33879     this.files = [];
33880     
33881     this.addEvents({
33882         /**
33883          * @event initial
33884          * Fire after initEvent
33885          * @param {Roo.bootstrap.DocumentSlider} this
33886          */
33887         "initial" : true,
33888         /**
33889          * @event update
33890          * Fire after update
33891          * @param {Roo.bootstrap.DocumentSlider} this
33892          */
33893         "update" : true,
33894         /**
33895          * @event click
33896          * Fire after click
33897          * @param {Roo.bootstrap.DocumentSlider} this
33898          */
33899         "click" : true
33900     });
33901 };
33902
33903 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33904     
33905     files : false,
33906     
33907     indicator : 0,
33908     
33909     getAutoCreate : function()
33910     {
33911         var cfg = {
33912             tag : 'div',
33913             cls : 'roo-document-slider',
33914             cn : [
33915                 {
33916                     tag : 'div',
33917                     cls : 'roo-document-slider-header',
33918                     cn : [
33919                         {
33920                             tag : 'div',
33921                             cls : 'roo-document-slider-header-title'
33922                         }
33923                     ]
33924                 },
33925                 {
33926                     tag : 'div',
33927                     cls : 'roo-document-slider-body',
33928                     cn : [
33929                         {
33930                             tag : 'div',
33931                             cls : 'roo-document-slider-prev',
33932                             cn : [
33933                                 {
33934                                     tag : 'i',
33935                                     cls : 'fa fa-chevron-left'
33936                                 }
33937                             ]
33938                         },
33939                         {
33940                             tag : 'div',
33941                             cls : 'roo-document-slider-thumb',
33942                             cn : [
33943                                 {
33944                                     tag : 'img',
33945                                     cls : 'roo-document-slider-image'
33946                                 }
33947                             ]
33948                         },
33949                         {
33950                             tag : 'div',
33951                             cls : 'roo-document-slider-next',
33952                             cn : [
33953                                 {
33954                                     tag : 'i',
33955                                     cls : 'fa fa-chevron-right'
33956                                 }
33957                             ]
33958                         }
33959                     ]
33960                 }
33961             ]
33962         };
33963         
33964         return cfg;
33965     },
33966     
33967     initEvents : function()
33968     {
33969         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33970         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33971         
33972         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33973         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33974         
33975         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33976         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33977         
33978         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33979         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33980         
33981         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33982         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33983         
33984         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33985         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33986         
33987         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33988         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33989         
33990         this.thumbEl.on('click', this.onClick, this);
33991         
33992         this.prevIndicator.on('click', this.prev, this);
33993         
33994         this.nextIndicator.on('click', this.next, this);
33995         
33996     },
33997     
33998     initial : function()
33999     {
34000         if(this.files.length){
34001             this.indicator = 1;
34002             this.update()
34003         }
34004         
34005         this.fireEvent('initial', this);
34006     },
34007     
34008     update : function()
34009     {
34010         this.imageEl.attr('src', this.files[this.indicator - 1]);
34011         
34012         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34013         
34014         this.prevIndicator.show();
34015         
34016         if(this.indicator == 1){
34017             this.prevIndicator.hide();
34018         }
34019         
34020         this.nextIndicator.show();
34021         
34022         if(this.indicator == this.files.length){
34023             this.nextIndicator.hide();
34024         }
34025         
34026         this.thumbEl.scrollTo('top');
34027         
34028         this.fireEvent('update', this);
34029     },
34030     
34031     onClick : function(e)
34032     {
34033         e.preventDefault();
34034         
34035         this.fireEvent('click', this);
34036     },
34037     
34038     prev : function(e)
34039     {
34040         e.preventDefault();
34041         
34042         this.indicator = Math.max(1, this.indicator - 1);
34043         
34044         this.update();
34045     },
34046     
34047     next : function(e)
34048     {
34049         e.preventDefault();
34050         
34051         this.indicator = Math.min(this.files.length, this.indicator + 1);
34052         
34053         this.update();
34054     }
34055 });
34056 /*
34057  * - LGPL
34058  *
34059  * RadioSet
34060  *
34061  *
34062  */
34063
34064 /**
34065  * @class Roo.bootstrap.RadioSet
34066  * @extends Roo.bootstrap.Input
34067  * Bootstrap RadioSet class
34068  * @cfg {String} indicatorpos (left|right) default left
34069  * @cfg {Boolean} inline (true|false) inline the element (default true)
34070  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34071  * @constructor
34072  * Create a new RadioSet
34073  * @param {Object} config The config object
34074  */
34075
34076 Roo.bootstrap.RadioSet = function(config){
34077     
34078     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34079     
34080     this.radioes = [];
34081     
34082     Roo.bootstrap.RadioSet.register(this);
34083     
34084     this.addEvents({
34085         /**
34086         * @event check
34087         * Fires when the element is checked or unchecked.
34088         * @param {Roo.bootstrap.RadioSet} this This radio
34089         * @param {Roo.bootstrap.Radio} item The checked item
34090         */
34091        check : true,
34092        /**
34093         * @event click
34094         * Fires when the element is click.
34095         * @param {Roo.bootstrap.RadioSet} this This radio set
34096         * @param {Roo.bootstrap.Radio} item The checked item
34097         * @param {Roo.EventObject} e The event object
34098         */
34099        click : true
34100     });
34101     
34102 };
34103
34104 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34105
34106     radioes : false,
34107     
34108     inline : true,
34109     
34110     weight : '',
34111     
34112     indicatorpos : 'left',
34113     
34114     getAutoCreate : function()
34115     {
34116         var label = {
34117             tag : 'label',
34118             cls : 'roo-radio-set-label',
34119             cn : [
34120                 {
34121                     tag : 'span',
34122                     html : this.fieldLabel
34123                 }
34124             ]
34125         };
34126         if (Roo.bootstrap.version == 3) {
34127             
34128             
34129             if(this.indicatorpos == 'left'){
34130                 label.cn.unshift({
34131                     tag : 'i',
34132                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34133                     tooltip : 'This field is required'
34134                 });
34135             } else {
34136                 label.cn.push({
34137                     tag : 'i',
34138                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34139                     tooltip : 'This field is required'
34140                 });
34141             }
34142         }
34143         var items = {
34144             tag : 'div',
34145             cls : 'roo-radio-set-items'
34146         };
34147         
34148         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34149         
34150         if (align === 'left' && this.fieldLabel.length) {
34151             
34152             items = {
34153                 cls : "roo-radio-set-right", 
34154                 cn: [
34155                     items
34156                 ]
34157             };
34158             
34159             if(this.labelWidth > 12){
34160                 label.style = "width: " + this.labelWidth + 'px';
34161             }
34162             
34163             if(this.labelWidth < 13 && this.labelmd == 0){
34164                 this.labelmd = this.labelWidth;
34165             }
34166             
34167             if(this.labellg > 0){
34168                 label.cls += ' col-lg-' + this.labellg;
34169                 items.cls += ' col-lg-' + (12 - this.labellg);
34170             }
34171             
34172             if(this.labelmd > 0){
34173                 label.cls += ' col-md-' + this.labelmd;
34174                 items.cls += ' col-md-' + (12 - this.labelmd);
34175             }
34176             
34177             if(this.labelsm > 0){
34178                 label.cls += ' col-sm-' + this.labelsm;
34179                 items.cls += ' col-sm-' + (12 - this.labelsm);
34180             }
34181             
34182             if(this.labelxs > 0){
34183                 label.cls += ' col-xs-' + this.labelxs;
34184                 items.cls += ' col-xs-' + (12 - this.labelxs);
34185             }
34186         }
34187         
34188         var cfg = {
34189             tag : 'div',
34190             cls : 'roo-radio-set',
34191             cn : [
34192                 {
34193                     tag : 'input',
34194                     cls : 'roo-radio-set-input',
34195                     type : 'hidden',
34196                     name : this.name,
34197                     value : this.value ? this.value :  ''
34198                 },
34199                 label,
34200                 items
34201             ]
34202         };
34203         
34204         if(this.weight.length){
34205             cfg.cls += ' roo-radio-' + this.weight;
34206         }
34207         
34208         if(this.inline) {
34209             cfg.cls += ' roo-radio-set-inline';
34210         }
34211         
34212         var settings=this;
34213         ['xs','sm','md','lg'].map(function(size){
34214             if (settings[size]) {
34215                 cfg.cls += ' col-' + size + '-' + settings[size];
34216             }
34217         });
34218         
34219         return cfg;
34220         
34221     },
34222
34223     initEvents : function()
34224     {
34225         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34226         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34227         
34228         if(!this.fieldLabel.length){
34229             this.labelEl.hide();
34230         }
34231         
34232         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34233         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34234         
34235         this.indicator = this.indicatorEl();
34236         
34237         if(this.indicator){
34238             this.indicator.addClass('invisible');
34239         }
34240         
34241         this.originalValue = this.getValue();
34242         
34243     },
34244     
34245     inputEl: function ()
34246     {
34247         return this.el.select('.roo-radio-set-input', true).first();
34248     },
34249     
34250     getChildContainer : function()
34251     {
34252         return this.itemsEl;
34253     },
34254     
34255     register : function(item)
34256     {
34257         this.radioes.push(item);
34258         
34259     },
34260     
34261     validate : function()
34262     {   
34263         if(this.getVisibilityEl().hasClass('hidden')){
34264             return true;
34265         }
34266         
34267         var valid = false;
34268         
34269         Roo.each(this.radioes, function(i){
34270             if(!i.checked){
34271                 return;
34272             }
34273             
34274             valid = true;
34275             return false;
34276         });
34277         
34278         if(this.allowBlank) {
34279             return true;
34280         }
34281         
34282         if(this.disabled || valid){
34283             this.markValid();
34284             return true;
34285         }
34286         
34287         this.markInvalid();
34288         return false;
34289         
34290     },
34291     
34292     markValid : function()
34293     {
34294         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34295             this.indicatorEl().removeClass('visible');
34296             this.indicatorEl().addClass('invisible');
34297         }
34298         
34299         
34300         if (Roo.bootstrap.version == 3) {
34301             this.el.removeClass([this.invalidClass, this.validClass]);
34302             this.el.addClass(this.validClass);
34303         } else {
34304             this.el.removeClass(['is-invalid','is-valid']);
34305             this.el.addClass(['is-valid']);
34306         }
34307         this.fireEvent('valid', this);
34308     },
34309     
34310     markInvalid : function(msg)
34311     {
34312         if(this.allowBlank || this.disabled){
34313             return;
34314         }
34315         
34316         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34317             this.indicatorEl().removeClass('invisible');
34318             this.indicatorEl().addClass('visible');
34319         }
34320         if (Roo.bootstrap.version == 3) {
34321             this.el.removeClass([this.invalidClass, this.validClass]);
34322             this.el.addClass(this.invalidClass);
34323         } else {
34324             this.el.removeClass(['is-invalid','is-valid']);
34325             this.el.addClass(['is-invalid']);
34326         }
34327         
34328         this.fireEvent('invalid', this, msg);
34329         
34330     },
34331     
34332     setValue : function(v, suppressEvent)
34333     {   
34334         if(this.value === v){
34335             return;
34336         }
34337         
34338         this.value = v;
34339         
34340         if(this.rendered){
34341             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34342         }
34343         
34344         Roo.each(this.radioes, function(i){
34345             i.checked = false;
34346             i.el.removeClass('checked');
34347         });
34348         
34349         Roo.each(this.radioes, function(i){
34350             
34351             if(i.value === v || i.value.toString() === v.toString()){
34352                 i.checked = true;
34353                 i.el.addClass('checked');
34354                 
34355                 if(suppressEvent !== true){
34356                     this.fireEvent('check', this, i);
34357                 }
34358                 
34359                 return false;
34360             }
34361             
34362         }, this);
34363         
34364         this.validate();
34365     },
34366     
34367     clearInvalid : function(){
34368         
34369         if(!this.el || this.preventMark){
34370             return;
34371         }
34372         
34373         this.el.removeClass([this.invalidClass]);
34374         
34375         this.fireEvent('valid', this);
34376     }
34377     
34378 });
34379
34380 Roo.apply(Roo.bootstrap.RadioSet, {
34381     
34382     groups: {},
34383     
34384     register : function(set)
34385     {
34386         this.groups[set.name] = set;
34387     },
34388     
34389     get: function(name) 
34390     {
34391         if (typeof(this.groups[name]) == 'undefined') {
34392             return false;
34393         }
34394         
34395         return this.groups[name] ;
34396     }
34397     
34398 });
34399 /*
34400  * Based on:
34401  * Ext JS Library 1.1.1
34402  * Copyright(c) 2006-2007, Ext JS, LLC.
34403  *
34404  * Originally Released Under LGPL - original licence link has changed is not relivant.
34405  *
34406  * Fork - LGPL
34407  * <script type="text/javascript">
34408  */
34409
34410
34411 /**
34412  * @class Roo.bootstrap.SplitBar
34413  * @extends Roo.util.Observable
34414  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34415  * <br><br>
34416  * Usage:
34417  * <pre><code>
34418 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34419                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34420 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34421 split.minSize = 100;
34422 split.maxSize = 600;
34423 split.animate = true;
34424 split.on('moved', splitterMoved);
34425 </code></pre>
34426  * @constructor
34427  * Create a new SplitBar
34428  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34429  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34430  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34431  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34432                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34433                         position of the SplitBar).
34434  */
34435 Roo.bootstrap.SplitBar = function(cfg){
34436     
34437     /** @private */
34438     
34439     //{
34440     //  dragElement : elm
34441     //  resizingElement: el,
34442         // optional..
34443     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34444     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34445         // existingProxy ???
34446     //}
34447     
34448     this.el = Roo.get(cfg.dragElement, true);
34449     this.el.dom.unselectable = "on";
34450     /** @private */
34451     this.resizingEl = Roo.get(cfg.resizingElement, true);
34452
34453     /**
34454      * @private
34455      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34456      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34457      * @type Number
34458      */
34459     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34460     
34461     /**
34462      * The minimum size of the resizing element. (Defaults to 0)
34463      * @type Number
34464      */
34465     this.minSize = 0;
34466     
34467     /**
34468      * The maximum size of the resizing element. (Defaults to 2000)
34469      * @type Number
34470      */
34471     this.maxSize = 2000;
34472     
34473     /**
34474      * Whether to animate the transition to the new size
34475      * @type Boolean
34476      */
34477     this.animate = false;
34478     
34479     /**
34480      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34481      * @type Boolean
34482      */
34483     this.useShim = false;
34484     
34485     /** @private */
34486     this.shim = null;
34487     
34488     if(!cfg.existingProxy){
34489         /** @private */
34490         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34491     }else{
34492         this.proxy = Roo.get(cfg.existingProxy).dom;
34493     }
34494     /** @private */
34495     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34496     
34497     /** @private */
34498     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34499     
34500     /** @private */
34501     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34502     
34503     /** @private */
34504     this.dragSpecs = {};
34505     
34506     /**
34507      * @private The adapter to use to positon and resize elements
34508      */
34509     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34510     this.adapter.init(this);
34511     
34512     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34513         /** @private */
34514         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34515         this.el.addClass("roo-splitbar-h");
34516     }else{
34517         /** @private */
34518         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34519         this.el.addClass("roo-splitbar-v");
34520     }
34521     
34522     this.addEvents({
34523         /**
34524          * @event resize
34525          * Fires when the splitter is moved (alias for {@link #event-moved})
34526          * @param {Roo.bootstrap.SplitBar} this
34527          * @param {Number} newSize the new width or height
34528          */
34529         "resize" : true,
34530         /**
34531          * @event moved
34532          * Fires when the splitter is moved
34533          * @param {Roo.bootstrap.SplitBar} this
34534          * @param {Number} newSize the new width or height
34535          */
34536         "moved" : true,
34537         /**
34538          * @event beforeresize
34539          * Fires before the splitter is dragged
34540          * @param {Roo.bootstrap.SplitBar} this
34541          */
34542         "beforeresize" : true,
34543
34544         "beforeapply" : true
34545     });
34546
34547     Roo.util.Observable.call(this);
34548 };
34549
34550 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34551     onStartProxyDrag : function(x, y){
34552         this.fireEvent("beforeresize", this);
34553         if(!this.overlay){
34554             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34555             o.unselectable();
34556             o.enableDisplayMode("block");
34557             // all splitbars share the same overlay
34558             Roo.bootstrap.SplitBar.prototype.overlay = o;
34559         }
34560         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34561         this.overlay.show();
34562         Roo.get(this.proxy).setDisplayed("block");
34563         var size = this.adapter.getElementSize(this);
34564         this.activeMinSize = this.getMinimumSize();;
34565         this.activeMaxSize = this.getMaximumSize();;
34566         var c1 = size - this.activeMinSize;
34567         var c2 = Math.max(this.activeMaxSize - size, 0);
34568         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34569             this.dd.resetConstraints();
34570             this.dd.setXConstraint(
34571                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34572                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34573             );
34574             this.dd.setYConstraint(0, 0);
34575         }else{
34576             this.dd.resetConstraints();
34577             this.dd.setXConstraint(0, 0);
34578             this.dd.setYConstraint(
34579                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34580                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34581             );
34582          }
34583         this.dragSpecs.startSize = size;
34584         this.dragSpecs.startPoint = [x, y];
34585         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34586     },
34587     
34588     /** 
34589      * @private Called after the drag operation by the DDProxy
34590      */
34591     onEndProxyDrag : function(e){
34592         Roo.get(this.proxy).setDisplayed(false);
34593         var endPoint = Roo.lib.Event.getXY(e);
34594         if(this.overlay){
34595             this.overlay.hide();
34596         }
34597         var newSize;
34598         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34599             newSize = this.dragSpecs.startSize + 
34600                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34601                     endPoint[0] - this.dragSpecs.startPoint[0] :
34602                     this.dragSpecs.startPoint[0] - endPoint[0]
34603                 );
34604         }else{
34605             newSize = this.dragSpecs.startSize + 
34606                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34607                     endPoint[1] - this.dragSpecs.startPoint[1] :
34608                     this.dragSpecs.startPoint[1] - endPoint[1]
34609                 );
34610         }
34611         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34612         if(newSize != this.dragSpecs.startSize){
34613             if(this.fireEvent('beforeapply', this, newSize) !== false){
34614                 this.adapter.setElementSize(this, newSize);
34615                 this.fireEvent("moved", this, newSize);
34616                 this.fireEvent("resize", this, newSize);
34617             }
34618         }
34619     },
34620     
34621     /**
34622      * Get the adapter this SplitBar uses
34623      * @return The adapter object
34624      */
34625     getAdapter : function(){
34626         return this.adapter;
34627     },
34628     
34629     /**
34630      * Set the adapter this SplitBar uses
34631      * @param {Object} adapter A SplitBar adapter object
34632      */
34633     setAdapter : function(adapter){
34634         this.adapter = adapter;
34635         this.adapter.init(this);
34636     },
34637     
34638     /**
34639      * Gets the minimum size for the resizing element
34640      * @return {Number} The minimum size
34641      */
34642     getMinimumSize : function(){
34643         return this.minSize;
34644     },
34645     
34646     /**
34647      * Sets the minimum size for the resizing element
34648      * @param {Number} minSize The minimum size
34649      */
34650     setMinimumSize : function(minSize){
34651         this.minSize = minSize;
34652     },
34653     
34654     /**
34655      * Gets the maximum size for the resizing element
34656      * @return {Number} The maximum size
34657      */
34658     getMaximumSize : function(){
34659         return this.maxSize;
34660     },
34661     
34662     /**
34663      * Sets the maximum size for the resizing element
34664      * @param {Number} maxSize The maximum size
34665      */
34666     setMaximumSize : function(maxSize){
34667         this.maxSize = maxSize;
34668     },
34669     
34670     /**
34671      * Sets the initialize size for the resizing element
34672      * @param {Number} size The initial size
34673      */
34674     setCurrentSize : function(size){
34675         var oldAnimate = this.animate;
34676         this.animate = false;
34677         this.adapter.setElementSize(this, size);
34678         this.animate = oldAnimate;
34679     },
34680     
34681     /**
34682      * Destroy this splitbar. 
34683      * @param {Boolean} removeEl True to remove the element
34684      */
34685     destroy : function(removeEl){
34686         if(this.shim){
34687             this.shim.remove();
34688         }
34689         this.dd.unreg();
34690         this.proxy.parentNode.removeChild(this.proxy);
34691         if(removeEl){
34692             this.el.remove();
34693         }
34694     }
34695 });
34696
34697 /**
34698  * @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.
34699  */
34700 Roo.bootstrap.SplitBar.createProxy = function(dir){
34701     var proxy = new Roo.Element(document.createElement("div"));
34702     proxy.unselectable();
34703     var cls = 'roo-splitbar-proxy';
34704     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34705     document.body.appendChild(proxy.dom);
34706     return proxy.dom;
34707 };
34708
34709 /** 
34710  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34711  * Default Adapter. It assumes the splitter and resizing element are not positioned
34712  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34713  */
34714 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34715 };
34716
34717 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34718     // do nothing for now
34719     init : function(s){
34720     
34721     },
34722     /**
34723      * Called before drag operations to get the current size of the resizing element. 
34724      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34725      */
34726      getElementSize : function(s){
34727         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34728             return s.resizingEl.getWidth();
34729         }else{
34730             return s.resizingEl.getHeight();
34731         }
34732     },
34733     
34734     /**
34735      * Called after drag operations to set the size of the resizing element.
34736      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34737      * @param {Number} newSize The new size to set
34738      * @param {Function} onComplete A function to be invoked when resizing is complete
34739      */
34740     setElementSize : function(s, newSize, onComplete){
34741         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34742             if(!s.animate){
34743                 s.resizingEl.setWidth(newSize);
34744                 if(onComplete){
34745                     onComplete(s, newSize);
34746                 }
34747             }else{
34748                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34749             }
34750         }else{
34751             
34752             if(!s.animate){
34753                 s.resizingEl.setHeight(newSize);
34754                 if(onComplete){
34755                     onComplete(s, newSize);
34756                 }
34757             }else{
34758                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34759             }
34760         }
34761     }
34762 };
34763
34764 /** 
34765  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34766  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34767  * Adapter that  moves the splitter element to align with the resized sizing element. 
34768  * Used with an absolute positioned SplitBar.
34769  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34770  * document.body, make sure you assign an id to the body element.
34771  */
34772 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34773     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34774     this.container = Roo.get(container);
34775 };
34776
34777 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34778     init : function(s){
34779         this.basic.init(s);
34780     },
34781     
34782     getElementSize : function(s){
34783         return this.basic.getElementSize(s);
34784     },
34785     
34786     setElementSize : function(s, newSize, onComplete){
34787         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34788     },
34789     
34790     moveSplitter : function(s){
34791         var yes = Roo.bootstrap.SplitBar;
34792         switch(s.placement){
34793             case yes.LEFT:
34794                 s.el.setX(s.resizingEl.getRight());
34795                 break;
34796             case yes.RIGHT:
34797                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34798                 break;
34799             case yes.TOP:
34800                 s.el.setY(s.resizingEl.getBottom());
34801                 break;
34802             case yes.BOTTOM:
34803                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34804                 break;
34805         }
34806     }
34807 };
34808
34809 /**
34810  * Orientation constant - Create a vertical SplitBar
34811  * @static
34812  * @type Number
34813  */
34814 Roo.bootstrap.SplitBar.VERTICAL = 1;
34815
34816 /**
34817  * Orientation constant - Create a horizontal SplitBar
34818  * @static
34819  * @type Number
34820  */
34821 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34822
34823 /**
34824  * Placement constant - The resizing element is to the left of the splitter element
34825  * @static
34826  * @type Number
34827  */
34828 Roo.bootstrap.SplitBar.LEFT = 1;
34829
34830 /**
34831  * Placement constant - The resizing element is to the right of the splitter element
34832  * @static
34833  * @type Number
34834  */
34835 Roo.bootstrap.SplitBar.RIGHT = 2;
34836
34837 /**
34838  * Placement constant - The resizing element is positioned above the splitter element
34839  * @static
34840  * @type Number
34841  */
34842 Roo.bootstrap.SplitBar.TOP = 3;
34843
34844 /**
34845  * Placement constant - The resizing element is positioned under splitter element
34846  * @static
34847  * @type Number
34848  */
34849 Roo.bootstrap.SplitBar.BOTTOM = 4;
34850 Roo.namespace("Roo.bootstrap.layout");/*
34851  * Based on:
34852  * Ext JS Library 1.1.1
34853  * Copyright(c) 2006-2007, Ext JS, LLC.
34854  *
34855  * Originally Released Under LGPL - original licence link has changed is not relivant.
34856  *
34857  * Fork - LGPL
34858  * <script type="text/javascript">
34859  */
34860
34861 /**
34862  * @class Roo.bootstrap.layout.Manager
34863  * @extends Roo.bootstrap.Component
34864  * Base class for layout managers.
34865  */
34866 Roo.bootstrap.layout.Manager = function(config)
34867 {
34868     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34869
34870
34871
34872
34873
34874     /** false to disable window resize monitoring @type Boolean */
34875     this.monitorWindowResize = true;
34876     this.regions = {};
34877     this.addEvents({
34878         /**
34879          * @event layout
34880          * Fires when a layout is performed.
34881          * @param {Roo.LayoutManager} this
34882          */
34883         "layout" : true,
34884         /**
34885          * @event regionresized
34886          * Fires when the user resizes a region.
34887          * @param {Roo.LayoutRegion} region The resized region
34888          * @param {Number} newSize The new size (width for east/west, height for north/south)
34889          */
34890         "regionresized" : true,
34891         /**
34892          * @event regioncollapsed
34893          * Fires when a region is collapsed.
34894          * @param {Roo.LayoutRegion} region The collapsed region
34895          */
34896         "regioncollapsed" : true,
34897         /**
34898          * @event regionexpanded
34899          * Fires when a region is expanded.
34900          * @param {Roo.LayoutRegion} region The expanded region
34901          */
34902         "regionexpanded" : true
34903     });
34904     this.updating = false;
34905
34906     if (config.el) {
34907         this.el = Roo.get(config.el);
34908         this.initEvents();
34909     }
34910
34911 };
34912
34913 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34914
34915
34916     regions : null,
34917
34918     monitorWindowResize : true,
34919
34920
34921     updating : false,
34922
34923
34924     onRender : function(ct, position)
34925     {
34926         if(!this.el){
34927             this.el = Roo.get(ct);
34928             this.initEvents();
34929         }
34930         //this.fireEvent('render',this);
34931     },
34932
34933
34934     initEvents: function()
34935     {
34936
34937
34938         // ie scrollbar fix
34939         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34940             document.body.scroll = "no";
34941         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34942             this.el.position('relative');
34943         }
34944         this.id = this.el.id;
34945         this.el.addClass("roo-layout-container");
34946         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34947         if(this.el.dom != document.body ) {
34948             this.el.on('resize', this.layout,this);
34949             this.el.on('show', this.layout,this);
34950         }
34951
34952     },
34953
34954     /**
34955      * Returns true if this layout is currently being updated
34956      * @return {Boolean}
34957      */
34958     isUpdating : function(){
34959         return this.updating;
34960     },
34961
34962     /**
34963      * Suspend the LayoutManager from doing auto-layouts while
34964      * making multiple add or remove calls
34965      */
34966     beginUpdate : function(){
34967         this.updating = true;
34968     },
34969
34970     /**
34971      * Restore auto-layouts and optionally disable the manager from performing a layout
34972      * @param {Boolean} noLayout true to disable a layout update
34973      */
34974     endUpdate : function(noLayout){
34975         this.updating = false;
34976         if(!noLayout){
34977             this.layout();
34978         }
34979     },
34980
34981     layout: function(){
34982         // abstract...
34983     },
34984
34985     onRegionResized : function(region, newSize){
34986         this.fireEvent("regionresized", region, newSize);
34987         this.layout();
34988     },
34989
34990     onRegionCollapsed : function(region){
34991         this.fireEvent("regioncollapsed", region);
34992     },
34993
34994     onRegionExpanded : function(region){
34995         this.fireEvent("regionexpanded", region);
34996     },
34997
34998     /**
34999      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35000      * performs box-model adjustments.
35001      * @return {Object} The size as an object {width: (the width), height: (the height)}
35002      */
35003     getViewSize : function()
35004     {
35005         var size;
35006         if(this.el.dom != document.body){
35007             size = this.el.getSize();
35008         }else{
35009             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35010         }
35011         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35012         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35013         return size;
35014     },
35015
35016     /**
35017      * Returns the Element this layout is bound to.
35018      * @return {Roo.Element}
35019      */
35020     getEl : function(){
35021         return this.el;
35022     },
35023
35024     /**
35025      * Returns the specified region.
35026      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35027      * @return {Roo.LayoutRegion}
35028      */
35029     getRegion : function(target){
35030         return this.regions[target.toLowerCase()];
35031     },
35032
35033     onWindowResize : function(){
35034         if(this.monitorWindowResize){
35035             this.layout();
35036         }
35037     }
35038 });
35039 /*
35040  * Based on:
35041  * Ext JS Library 1.1.1
35042  * Copyright(c) 2006-2007, Ext JS, LLC.
35043  *
35044  * Originally Released Under LGPL - original licence link has changed is not relivant.
35045  *
35046  * Fork - LGPL
35047  * <script type="text/javascript">
35048  */
35049 /**
35050  * @class Roo.bootstrap.layout.Border
35051  * @extends Roo.bootstrap.layout.Manager
35052  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35053  * please see: examples/bootstrap/nested.html<br><br>
35054  
35055 <b>The container the layout is rendered into can be either the body element or any other element.
35056 If it is not the body element, the container needs to either be an absolute positioned element,
35057 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35058 the container size if it is not the body element.</b>
35059
35060 * @constructor
35061 * Create a new Border
35062 * @param {Object} config Configuration options
35063  */
35064 Roo.bootstrap.layout.Border = function(config){
35065     config = config || {};
35066     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35067     
35068     
35069     
35070     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35071         if(config[region]){
35072             config[region].region = region;
35073             this.addRegion(config[region]);
35074         }
35075     },this);
35076     
35077 };
35078
35079 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35080
35081 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35082     /**
35083      * Creates and adds a new region if it doesn't already exist.
35084      * @param {String} target The target region key (north, south, east, west or center).
35085      * @param {Object} config The regions config object
35086      * @return {BorderLayoutRegion} The new region
35087      */
35088     addRegion : function(config)
35089     {
35090         if(!this.regions[config.region]){
35091             var r = this.factory(config);
35092             this.bindRegion(r);
35093         }
35094         return this.regions[config.region];
35095     },
35096
35097     // private (kinda)
35098     bindRegion : function(r){
35099         this.regions[r.config.region] = r;
35100         
35101         r.on("visibilitychange",    this.layout, this);
35102         r.on("paneladded",          this.layout, this);
35103         r.on("panelremoved",        this.layout, this);
35104         r.on("invalidated",         this.layout, this);
35105         r.on("resized",             this.onRegionResized, this);
35106         r.on("collapsed",           this.onRegionCollapsed, this);
35107         r.on("expanded",            this.onRegionExpanded, this);
35108     },
35109
35110     /**
35111      * Performs a layout update.
35112      */
35113     layout : function()
35114     {
35115         if(this.updating) {
35116             return;
35117         }
35118         
35119         // render all the rebions if they have not been done alreayd?
35120         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35121             if(this.regions[region] && !this.regions[region].bodyEl){
35122                 this.regions[region].onRender(this.el)
35123             }
35124         },this);
35125         
35126         var size = this.getViewSize();
35127         var w = size.width;
35128         var h = size.height;
35129         var centerW = w;
35130         var centerH = h;
35131         var centerY = 0;
35132         var centerX = 0;
35133         //var x = 0, y = 0;
35134
35135         var rs = this.regions;
35136         var north = rs["north"];
35137         var south = rs["south"]; 
35138         var west = rs["west"];
35139         var east = rs["east"];
35140         var center = rs["center"];
35141         //if(this.hideOnLayout){ // not supported anymore
35142             //c.el.setStyle("display", "none");
35143         //}
35144         if(north && north.isVisible()){
35145             var b = north.getBox();
35146             var m = north.getMargins();
35147             b.width = w - (m.left+m.right);
35148             b.x = m.left;
35149             b.y = m.top;
35150             centerY = b.height + b.y + m.bottom;
35151             centerH -= centerY;
35152             north.updateBox(this.safeBox(b));
35153         }
35154         if(south && south.isVisible()){
35155             var b = south.getBox();
35156             var m = south.getMargins();
35157             b.width = w - (m.left+m.right);
35158             b.x = m.left;
35159             var totalHeight = (b.height + m.top + m.bottom);
35160             b.y = h - totalHeight + m.top;
35161             centerH -= totalHeight;
35162             south.updateBox(this.safeBox(b));
35163         }
35164         if(west && west.isVisible()){
35165             var b = west.getBox();
35166             var m = west.getMargins();
35167             b.height = centerH - (m.top+m.bottom);
35168             b.x = m.left;
35169             b.y = centerY + m.top;
35170             var totalWidth = (b.width + m.left + m.right);
35171             centerX += totalWidth;
35172             centerW -= totalWidth;
35173             west.updateBox(this.safeBox(b));
35174         }
35175         if(east && east.isVisible()){
35176             var b = east.getBox();
35177             var m = east.getMargins();
35178             b.height = centerH - (m.top+m.bottom);
35179             var totalWidth = (b.width + m.left + m.right);
35180             b.x = w - totalWidth + m.left;
35181             b.y = centerY + m.top;
35182             centerW -= totalWidth;
35183             east.updateBox(this.safeBox(b));
35184         }
35185         if(center){
35186             var m = center.getMargins();
35187             var centerBox = {
35188                 x: centerX + m.left,
35189                 y: centerY + m.top,
35190                 width: centerW - (m.left+m.right),
35191                 height: centerH - (m.top+m.bottom)
35192             };
35193             //if(this.hideOnLayout){
35194                 //center.el.setStyle("display", "block");
35195             //}
35196             center.updateBox(this.safeBox(centerBox));
35197         }
35198         this.el.repaint();
35199         this.fireEvent("layout", this);
35200     },
35201
35202     // private
35203     safeBox : function(box){
35204         box.width = Math.max(0, box.width);
35205         box.height = Math.max(0, box.height);
35206         return box;
35207     },
35208
35209     /**
35210      * Adds a ContentPanel (or subclass) to this layout.
35211      * @param {String} target The target region key (north, south, east, west or center).
35212      * @param {Roo.ContentPanel} panel The panel to add
35213      * @return {Roo.ContentPanel} The added panel
35214      */
35215     add : function(target, panel){
35216          
35217         target = target.toLowerCase();
35218         return this.regions[target].add(panel);
35219     },
35220
35221     /**
35222      * Remove a ContentPanel (or subclass) to this layout.
35223      * @param {String} target The target region key (north, south, east, west or center).
35224      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35225      * @return {Roo.ContentPanel} The removed panel
35226      */
35227     remove : function(target, panel){
35228         target = target.toLowerCase();
35229         return this.regions[target].remove(panel);
35230     },
35231
35232     /**
35233      * Searches all regions for a panel with the specified id
35234      * @param {String} panelId
35235      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35236      */
35237     findPanel : function(panelId){
35238         var rs = this.regions;
35239         for(var target in rs){
35240             if(typeof rs[target] != "function"){
35241                 var p = rs[target].getPanel(panelId);
35242                 if(p){
35243                     return p;
35244                 }
35245             }
35246         }
35247         return null;
35248     },
35249
35250     /**
35251      * Searches all regions for a panel with the specified id and activates (shows) it.
35252      * @param {String/ContentPanel} panelId The panels id or the panel itself
35253      * @return {Roo.ContentPanel} The shown panel or null
35254      */
35255     showPanel : function(panelId) {
35256       var rs = this.regions;
35257       for(var target in rs){
35258          var r = rs[target];
35259          if(typeof r != "function"){
35260             if(r.hasPanel(panelId)){
35261                return r.showPanel(panelId);
35262             }
35263          }
35264       }
35265       return null;
35266    },
35267
35268    /**
35269      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35270      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35271      */
35272    /*
35273     restoreState : function(provider){
35274         if(!provider){
35275             provider = Roo.state.Manager;
35276         }
35277         var sm = new Roo.LayoutStateManager();
35278         sm.init(this, provider);
35279     },
35280 */
35281  
35282  
35283     /**
35284      * Adds a xtype elements to the layout.
35285      * <pre><code>
35286
35287 layout.addxtype({
35288        xtype : 'ContentPanel',
35289        region: 'west',
35290        items: [ .... ]
35291    }
35292 );
35293
35294 layout.addxtype({
35295         xtype : 'NestedLayoutPanel',
35296         region: 'west',
35297         layout: {
35298            center: { },
35299            west: { }   
35300         },
35301         items : [ ... list of content panels or nested layout panels.. ]
35302    }
35303 );
35304 </code></pre>
35305      * @param {Object} cfg Xtype definition of item to add.
35306      */
35307     addxtype : function(cfg)
35308     {
35309         // basically accepts a pannel...
35310         // can accept a layout region..!?!?
35311         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35312         
35313         
35314         // theory?  children can only be panels??
35315         
35316         //if (!cfg.xtype.match(/Panel$/)) {
35317         //    return false;
35318         //}
35319         var ret = false;
35320         
35321         if (typeof(cfg.region) == 'undefined') {
35322             Roo.log("Failed to add Panel, region was not set");
35323             Roo.log(cfg);
35324             return false;
35325         }
35326         var region = cfg.region;
35327         delete cfg.region;
35328         
35329           
35330         var xitems = [];
35331         if (cfg.items) {
35332             xitems = cfg.items;
35333             delete cfg.items;
35334         }
35335         var nb = false;
35336         
35337         switch(cfg.xtype) 
35338         {
35339             case 'Content':  // ContentPanel (el, cfg)
35340             case 'Scroll':  // ContentPanel (el, cfg)
35341             case 'View': 
35342                 cfg.autoCreate = true;
35343                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35344                 //} else {
35345                 //    var el = this.el.createChild();
35346                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35347                 //}
35348                 
35349                 this.add(region, ret);
35350                 break;
35351             
35352             /*
35353             case 'TreePanel': // our new panel!
35354                 cfg.el = this.el.createChild();
35355                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35356                 this.add(region, ret);
35357                 break;
35358             */
35359             
35360             case 'Nest': 
35361                 // create a new Layout (which is  a Border Layout...
35362                 
35363                 var clayout = cfg.layout;
35364                 clayout.el  = this.el.createChild();
35365                 clayout.items   = clayout.items  || [];
35366                 
35367                 delete cfg.layout;
35368                 
35369                 // replace this exitems with the clayout ones..
35370                 xitems = clayout.items;
35371                  
35372                 // force background off if it's in center...
35373                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35374                     cfg.background = false;
35375                 }
35376                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35377                 
35378                 
35379                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35380                 //console.log('adding nested layout panel '  + cfg.toSource());
35381                 this.add(region, ret);
35382                 nb = {}; /// find first...
35383                 break;
35384             
35385             case 'Grid':
35386                 
35387                 // needs grid and region
35388                 
35389                 //var el = this.getRegion(region).el.createChild();
35390                 /*
35391                  *var el = this.el.createChild();
35392                 // create the grid first...
35393                 cfg.grid.container = el;
35394                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35395                 */
35396                 
35397                 if (region == 'center' && this.active ) {
35398                     cfg.background = false;
35399                 }
35400                 
35401                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35402                 
35403                 this.add(region, ret);
35404                 /*
35405                 if (cfg.background) {
35406                     // render grid on panel activation (if panel background)
35407                     ret.on('activate', function(gp) {
35408                         if (!gp.grid.rendered) {
35409                     //        gp.grid.render(el);
35410                         }
35411                     });
35412                 } else {
35413                   //  cfg.grid.render(el);
35414                 }
35415                 */
35416                 break;
35417            
35418            
35419             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35420                 // it was the old xcomponent building that caused this before.
35421                 // espeically if border is the top element in the tree.
35422                 ret = this;
35423                 break; 
35424                 
35425                     
35426                 
35427                 
35428                 
35429             default:
35430                 /*
35431                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35432                     
35433                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35434                     this.add(region, ret);
35435                 } else {
35436                 */
35437                     Roo.log(cfg);
35438                     throw "Can not add '" + cfg.xtype + "' to Border";
35439                     return null;
35440              
35441                                 
35442              
35443         }
35444         this.beginUpdate();
35445         // add children..
35446         var region = '';
35447         var abn = {};
35448         Roo.each(xitems, function(i)  {
35449             region = nb && i.region ? i.region : false;
35450             
35451             var add = ret.addxtype(i);
35452            
35453             if (region) {
35454                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35455                 if (!i.background) {
35456                     abn[region] = nb[region] ;
35457                 }
35458             }
35459             
35460         });
35461         this.endUpdate();
35462
35463         // make the last non-background panel active..
35464         //if (nb) { Roo.log(abn); }
35465         if (nb) {
35466             
35467             for(var r in abn) {
35468                 region = this.getRegion(r);
35469                 if (region) {
35470                     // tried using nb[r], but it does not work..
35471                      
35472                     region.showPanel(abn[r]);
35473                    
35474                 }
35475             }
35476         }
35477         return ret;
35478         
35479     },
35480     
35481     
35482 // private
35483     factory : function(cfg)
35484     {
35485         
35486         var validRegions = Roo.bootstrap.layout.Border.regions;
35487
35488         var target = cfg.region;
35489         cfg.mgr = this;
35490         
35491         var r = Roo.bootstrap.layout;
35492         Roo.log(target);
35493         switch(target){
35494             case "north":
35495                 return new r.North(cfg);
35496             case "south":
35497                 return new r.South(cfg);
35498             case "east":
35499                 return new r.East(cfg);
35500             case "west":
35501                 return new r.West(cfg);
35502             case "center":
35503                 return new r.Center(cfg);
35504         }
35505         throw 'Layout region "'+target+'" not supported.';
35506     }
35507     
35508     
35509 });
35510  /*
35511  * Based on:
35512  * Ext JS Library 1.1.1
35513  * Copyright(c) 2006-2007, Ext JS, LLC.
35514  *
35515  * Originally Released Under LGPL - original licence link has changed is not relivant.
35516  *
35517  * Fork - LGPL
35518  * <script type="text/javascript">
35519  */
35520  
35521 /**
35522  * @class Roo.bootstrap.layout.Basic
35523  * @extends Roo.util.Observable
35524  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35525  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35526  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35527  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35528  * @cfg {string}   region  the region that it inhabits..
35529  * @cfg {bool}   skipConfig skip config?
35530  * 
35531
35532  */
35533 Roo.bootstrap.layout.Basic = function(config){
35534     
35535     this.mgr = config.mgr;
35536     
35537     this.position = config.region;
35538     
35539     var skipConfig = config.skipConfig;
35540     
35541     this.events = {
35542         /**
35543          * @scope Roo.BasicLayoutRegion
35544          */
35545         
35546         /**
35547          * @event beforeremove
35548          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35549          * @param {Roo.LayoutRegion} this
35550          * @param {Roo.ContentPanel} panel The panel
35551          * @param {Object} e The cancel event object
35552          */
35553         "beforeremove" : true,
35554         /**
35555          * @event invalidated
35556          * Fires when the layout for this region is changed.
35557          * @param {Roo.LayoutRegion} this
35558          */
35559         "invalidated" : true,
35560         /**
35561          * @event visibilitychange
35562          * Fires when this region is shown or hidden 
35563          * @param {Roo.LayoutRegion} this
35564          * @param {Boolean} visibility true or false
35565          */
35566         "visibilitychange" : true,
35567         /**
35568          * @event paneladded
35569          * Fires when a panel is added. 
35570          * @param {Roo.LayoutRegion} this
35571          * @param {Roo.ContentPanel} panel The panel
35572          */
35573         "paneladded" : true,
35574         /**
35575          * @event panelremoved
35576          * Fires when a panel is removed. 
35577          * @param {Roo.LayoutRegion} this
35578          * @param {Roo.ContentPanel} panel The panel
35579          */
35580         "panelremoved" : true,
35581         /**
35582          * @event beforecollapse
35583          * Fires when this region before collapse.
35584          * @param {Roo.LayoutRegion} this
35585          */
35586         "beforecollapse" : true,
35587         /**
35588          * @event collapsed
35589          * Fires when this region is collapsed.
35590          * @param {Roo.LayoutRegion} this
35591          */
35592         "collapsed" : true,
35593         /**
35594          * @event expanded
35595          * Fires when this region is expanded.
35596          * @param {Roo.LayoutRegion} this
35597          */
35598         "expanded" : true,
35599         /**
35600          * @event slideshow
35601          * Fires when this region is slid into view.
35602          * @param {Roo.LayoutRegion} this
35603          */
35604         "slideshow" : true,
35605         /**
35606          * @event slidehide
35607          * Fires when this region slides out of view. 
35608          * @param {Roo.LayoutRegion} this
35609          */
35610         "slidehide" : true,
35611         /**
35612          * @event panelactivated
35613          * Fires when a panel is activated. 
35614          * @param {Roo.LayoutRegion} this
35615          * @param {Roo.ContentPanel} panel The activated panel
35616          */
35617         "panelactivated" : true,
35618         /**
35619          * @event resized
35620          * Fires when the user resizes this region. 
35621          * @param {Roo.LayoutRegion} this
35622          * @param {Number} newSize The new size (width for east/west, height for north/south)
35623          */
35624         "resized" : true
35625     };
35626     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35627     this.panels = new Roo.util.MixedCollection();
35628     this.panels.getKey = this.getPanelId.createDelegate(this);
35629     this.box = null;
35630     this.activePanel = null;
35631     // ensure listeners are added...
35632     
35633     if (config.listeners || config.events) {
35634         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35635             listeners : config.listeners || {},
35636             events : config.events || {}
35637         });
35638     }
35639     
35640     if(skipConfig !== true){
35641         this.applyConfig(config);
35642     }
35643 };
35644
35645 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35646 {
35647     getPanelId : function(p){
35648         return p.getId();
35649     },
35650     
35651     applyConfig : function(config){
35652         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35653         this.config = config;
35654         
35655     },
35656     
35657     /**
35658      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35659      * the width, for horizontal (north, south) the height.
35660      * @param {Number} newSize The new width or height
35661      */
35662     resizeTo : function(newSize){
35663         var el = this.el ? this.el :
35664                  (this.activePanel ? this.activePanel.getEl() : null);
35665         if(el){
35666             switch(this.position){
35667                 case "east":
35668                 case "west":
35669                     el.setWidth(newSize);
35670                     this.fireEvent("resized", this, newSize);
35671                 break;
35672                 case "north":
35673                 case "south":
35674                     el.setHeight(newSize);
35675                     this.fireEvent("resized", this, newSize);
35676                 break;                
35677             }
35678         }
35679     },
35680     
35681     getBox : function(){
35682         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35683     },
35684     
35685     getMargins : function(){
35686         return this.margins;
35687     },
35688     
35689     updateBox : function(box){
35690         this.box = box;
35691         var el = this.activePanel.getEl();
35692         el.dom.style.left = box.x + "px";
35693         el.dom.style.top = box.y + "px";
35694         this.activePanel.setSize(box.width, box.height);
35695     },
35696     
35697     /**
35698      * Returns the container element for this region.
35699      * @return {Roo.Element}
35700      */
35701     getEl : function(){
35702         return this.activePanel;
35703     },
35704     
35705     /**
35706      * Returns true if this region is currently visible.
35707      * @return {Boolean}
35708      */
35709     isVisible : function(){
35710         return this.activePanel ? true : false;
35711     },
35712     
35713     setActivePanel : function(panel){
35714         panel = this.getPanel(panel);
35715         if(this.activePanel && this.activePanel != panel){
35716             this.activePanel.setActiveState(false);
35717             this.activePanel.getEl().setLeftTop(-10000,-10000);
35718         }
35719         this.activePanel = panel;
35720         panel.setActiveState(true);
35721         if(this.box){
35722             panel.setSize(this.box.width, this.box.height);
35723         }
35724         this.fireEvent("panelactivated", this, panel);
35725         this.fireEvent("invalidated");
35726     },
35727     
35728     /**
35729      * Show the specified panel.
35730      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35731      * @return {Roo.ContentPanel} The shown panel or null
35732      */
35733     showPanel : function(panel){
35734         panel = this.getPanel(panel);
35735         if(panel){
35736             this.setActivePanel(panel);
35737         }
35738         return panel;
35739     },
35740     
35741     /**
35742      * Get the active panel for this region.
35743      * @return {Roo.ContentPanel} The active panel or null
35744      */
35745     getActivePanel : function(){
35746         return this.activePanel;
35747     },
35748     
35749     /**
35750      * Add the passed ContentPanel(s)
35751      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35752      * @return {Roo.ContentPanel} The panel added (if only one was added)
35753      */
35754     add : function(panel){
35755         if(arguments.length > 1){
35756             for(var i = 0, len = arguments.length; i < len; i++) {
35757                 this.add(arguments[i]);
35758             }
35759             return null;
35760         }
35761         if(this.hasPanel(panel)){
35762             this.showPanel(panel);
35763             return panel;
35764         }
35765         var el = panel.getEl();
35766         if(el.dom.parentNode != this.mgr.el.dom){
35767             this.mgr.el.dom.appendChild(el.dom);
35768         }
35769         if(panel.setRegion){
35770             panel.setRegion(this);
35771         }
35772         this.panels.add(panel);
35773         el.setStyle("position", "absolute");
35774         if(!panel.background){
35775             this.setActivePanel(panel);
35776             if(this.config.initialSize && this.panels.getCount()==1){
35777                 this.resizeTo(this.config.initialSize);
35778             }
35779         }
35780         this.fireEvent("paneladded", this, panel);
35781         return panel;
35782     },
35783     
35784     /**
35785      * Returns true if the panel is in this region.
35786      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35787      * @return {Boolean}
35788      */
35789     hasPanel : function(panel){
35790         if(typeof panel == "object"){ // must be panel obj
35791             panel = panel.getId();
35792         }
35793         return this.getPanel(panel) ? true : false;
35794     },
35795     
35796     /**
35797      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35798      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35799      * @param {Boolean} preservePanel Overrides the config preservePanel option
35800      * @return {Roo.ContentPanel} The panel that was removed
35801      */
35802     remove : function(panel, preservePanel){
35803         panel = this.getPanel(panel);
35804         if(!panel){
35805             return null;
35806         }
35807         var e = {};
35808         this.fireEvent("beforeremove", this, panel, e);
35809         if(e.cancel === true){
35810             return null;
35811         }
35812         var panelId = panel.getId();
35813         this.panels.removeKey(panelId);
35814         return panel;
35815     },
35816     
35817     /**
35818      * Returns the panel specified or null if it's not in this region.
35819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35820      * @return {Roo.ContentPanel}
35821      */
35822     getPanel : function(id){
35823         if(typeof id == "object"){ // must be panel obj
35824             return id;
35825         }
35826         return this.panels.get(id);
35827     },
35828     
35829     /**
35830      * Returns this regions position (north/south/east/west/center).
35831      * @return {String} 
35832      */
35833     getPosition: function(){
35834         return this.position;    
35835     }
35836 });/*
35837  * Based on:
35838  * Ext JS Library 1.1.1
35839  * Copyright(c) 2006-2007, Ext JS, LLC.
35840  *
35841  * Originally Released Under LGPL - original licence link has changed is not relivant.
35842  *
35843  * Fork - LGPL
35844  * <script type="text/javascript">
35845  */
35846  
35847 /**
35848  * @class Roo.bootstrap.layout.Region
35849  * @extends Roo.bootstrap.layout.Basic
35850  * This class represents a region in a layout manager.
35851  
35852  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35853  * @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})
35854  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35855  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35856  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35857  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35858  * @cfg {String}    title           The title for the region (overrides panel titles)
35859  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35860  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35861  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35862  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35863  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35864  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35865  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35866  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35867  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35868  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35869
35870  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35871  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35872  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35873  * @cfg {Number}    width           For East/West panels
35874  * @cfg {Number}    height          For North/South panels
35875  * @cfg {Boolean}   split           To show the splitter
35876  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35877  * 
35878  * @cfg {string}   cls             Extra CSS classes to add to region
35879  * 
35880  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35881  * @cfg {string}   region  the region that it inhabits..
35882  *
35883
35884  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35885  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35886
35887  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35888  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35889  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35890  */
35891 Roo.bootstrap.layout.Region = function(config)
35892 {
35893     this.applyConfig(config);
35894
35895     var mgr = config.mgr;
35896     var pos = config.region;
35897     config.skipConfig = true;
35898     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35899     
35900     if (mgr.el) {
35901         this.onRender(mgr.el);   
35902     }
35903      
35904     this.visible = true;
35905     this.collapsed = false;
35906     this.unrendered_panels = [];
35907 };
35908
35909 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35910
35911     position: '', // set by wrapper (eg. north/south etc..)
35912     unrendered_panels : null,  // unrendered panels.
35913     createBody : function(){
35914         /** This region's body element 
35915         * @type Roo.Element */
35916         this.bodyEl = this.el.createChild({
35917                 tag: "div",
35918                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35919         });
35920     },
35921
35922     onRender: function(ctr, pos)
35923     {
35924         var dh = Roo.DomHelper;
35925         /** This region's container element 
35926         * @type Roo.Element */
35927         this.el = dh.append(ctr.dom, {
35928                 tag: "div",
35929                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35930             }, true);
35931         /** This region's title element 
35932         * @type Roo.Element */
35933     
35934         this.titleEl = dh.append(this.el.dom,
35935             {
35936                     tag: "div",
35937                     unselectable: "on",
35938                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35939                     children:[
35940                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35941                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35942                     ]}, true);
35943         
35944         this.titleEl.enableDisplayMode();
35945         /** This region's title text element 
35946         * @type HTMLElement */
35947         this.titleTextEl = this.titleEl.dom.firstChild;
35948         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35949         /*
35950         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35951         this.closeBtn.enableDisplayMode();
35952         this.closeBtn.on("click", this.closeClicked, this);
35953         this.closeBtn.hide();
35954     */
35955         this.createBody(this.config);
35956         if(this.config.hideWhenEmpty){
35957             this.hide();
35958             this.on("paneladded", this.validateVisibility, this);
35959             this.on("panelremoved", this.validateVisibility, this);
35960         }
35961         if(this.autoScroll){
35962             this.bodyEl.setStyle("overflow", "auto");
35963         }else{
35964             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35965         }
35966         //if(c.titlebar !== false){
35967             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35968                 this.titleEl.hide();
35969             }else{
35970                 this.titleEl.show();
35971                 if(this.config.title){
35972                     this.titleTextEl.innerHTML = this.config.title;
35973                 }
35974             }
35975         //}
35976         if(this.config.collapsed){
35977             this.collapse(true);
35978         }
35979         if(this.config.hidden){
35980             this.hide();
35981         }
35982         
35983         if (this.unrendered_panels && this.unrendered_panels.length) {
35984             for (var i =0;i< this.unrendered_panels.length; i++) {
35985                 this.add(this.unrendered_panels[i]);
35986             }
35987             this.unrendered_panels = null;
35988             
35989         }
35990         
35991     },
35992     
35993     applyConfig : function(c)
35994     {
35995         /*
35996          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35997             var dh = Roo.DomHelper;
35998             if(c.titlebar !== false){
35999                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36000                 this.collapseBtn.on("click", this.collapse, this);
36001                 this.collapseBtn.enableDisplayMode();
36002                 /*
36003                 if(c.showPin === true || this.showPin){
36004                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36005                     this.stickBtn.enableDisplayMode();
36006                     this.stickBtn.on("click", this.expand, this);
36007                     this.stickBtn.hide();
36008                 }
36009                 
36010             }
36011             */
36012             /** This region's collapsed element
36013             * @type Roo.Element */
36014             /*
36015              *
36016             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36017                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36018             ]}, true);
36019             
36020             if(c.floatable !== false){
36021                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36022                this.collapsedEl.on("click", this.collapseClick, this);
36023             }
36024
36025             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36026                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36027                    id: "message", unselectable: "on", style:{"float":"left"}});
36028                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36029              }
36030             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36031             this.expandBtn.on("click", this.expand, this);
36032             
36033         }
36034         
36035         if(this.collapseBtn){
36036             this.collapseBtn.setVisible(c.collapsible == true);
36037         }
36038         
36039         this.cmargins = c.cmargins || this.cmargins ||
36040                          (this.position == "west" || this.position == "east" ?
36041                              {top: 0, left: 2, right:2, bottom: 0} :
36042                              {top: 2, left: 0, right:0, bottom: 2});
36043         */
36044         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36045         
36046         
36047         this.bottomTabs = c.tabPosition != "top";
36048         
36049         this.autoScroll = c.autoScroll || false;
36050         
36051         
36052        
36053         
36054         this.duration = c.duration || .30;
36055         this.slideDuration = c.slideDuration || .45;
36056         this.config = c;
36057        
36058     },
36059     /**
36060      * Returns true if this region is currently visible.
36061      * @return {Boolean}
36062      */
36063     isVisible : function(){
36064         return this.visible;
36065     },
36066
36067     /**
36068      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36069      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36070      */
36071     //setCollapsedTitle : function(title){
36072     //    title = title || "&#160;";
36073      //   if(this.collapsedTitleTextEl){
36074       //      this.collapsedTitleTextEl.innerHTML = title;
36075        // }
36076     //},
36077
36078     getBox : function(){
36079         var b;
36080       //  if(!this.collapsed){
36081             b = this.el.getBox(false, true);
36082        // }else{
36083           //  b = this.collapsedEl.getBox(false, true);
36084         //}
36085         return b;
36086     },
36087
36088     getMargins : function(){
36089         return this.margins;
36090         //return this.collapsed ? this.cmargins : this.margins;
36091     },
36092 /*
36093     highlight : function(){
36094         this.el.addClass("x-layout-panel-dragover");
36095     },
36096
36097     unhighlight : function(){
36098         this.el.removeClass("x-layout-panel-dragover");
36099     },
36100 */
36101     updateBox : function(box)
36102     {
36103         if (!this.bodyEl) {
36104             return; // not rendered yet..
36105         }
36106         
36107         this.box = box;
36108         if(!this.collapsed){
36109             this.el.dom.style.left = box.x + "px";
36110             this.el.dom.style.top = box.y + "px";
36111             this.updateBody(box.width, box.height);
36112         }else{
36113             this.collapsedEl.dom.style.left = box.x + "px";
36114             this.collapsedEl.dom.style.top = box.y + "px";
36115             this.collapsedEl.setSize(box.width, box.height);
36116         }
36117         if(this.tabs){
36118             this.tabs.autoSizeTabs();
36119         }
36120     },
36121
36122     updateBody : function(w, h)
36123     {
36124         if(w !== null){
36125             this.el.setWidth(w);
36126             w -= this.el.getBorderWidth("rl");
36127             if(this.config.adjustments){
36128                 w += this.config.adjustments[0];
36129             }
36130         }
36131         if(h !== null && h > 0){
36132             this.el.setHeight(h);
36133             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36134             h -= this.el.getBorderWidth("tb");
36135             if(this.config.adjustments){
36136                 h += this.config.adjustments[1];
36137             }
36138             this.bodyEl.setHeight(h);
36139             if(this.tabs){
36140                 h = this.tabs.syncHeight(h);
36141             }
36142         }
36143         if(this.panelSize){
36144             w = w !== null ? w : this.panelSize.width;
36145             h = h !== null ? h : this.panelSize.height;
36146         }
36147         if(this.activePanel){
36148             var el = this.activePanel.getEl();
36149             w = w !== null ? w : el.getWidth();
36150             h = h !== null ? h : el.getHeight();
36151             this.panelSize = {width: w, height: h};
36152             this.activePanel.setSize(w, h);
36153         }
36154         if(Roo.isIE && this.tabs){
36155             this.tabs.el.repaint();
36156         }
36157     },
36158
36159     /**
36160      * Returns the container element for this region.
36161      * @return {Roo.Element}
36162      */
36163     getEl : function(){
36164         return this.el;
36165     },
36166
36167     /**
36168      * Hides this region.
36169      */
36170     hide : function(){
36171         //if(!this.collapsed){
36172             this.el.dom.style.left = "-2000px";
36173             this.el.hide();
36174         //}else{
36175          //   this.collapsedEl.dom.style.left = "-2000px";
36176          //   this.collapsedEl.hide();
36177        // }
36178         this.visible = false;
36179         this.fireEvent("visibilitychange", this, false);
36180     },
36181
36182     /**
36183      * Shows this region if it was previously hidden.
36184      */
36185     show : function(){
36186         //if(!this.collapsed){
36187             this.el.show();
36188         //}else{
36189         //    this.collapsedEl.show();
36190        // }
36191         this.visible = true;
36192         this.fireEvent("visibilitychange", this, true);
36193     },
36194 /*
36195     closeClicked : function(){
36196         if(this.activePanel){
36197             this.remove(this.activePanel);
36198         }
36199     },
36200
36201     collapseClick : function(e){
36202         if(this.isSlid){
36203            e.stopPropagation();
36204            this.slideIn();
36205         }else{
36206            e.stopPropagation();
36207            this.slideOut();
36208         }
36209     },
36210 */
36211     /**
36212      * Collapses this region.
36213      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36214      */
36215     /*
36216     collapse : function(skipAnim, skipCheck = false){
36217         if(this.collapsed) {
36218             return;
36219         }
36220         
36221         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36222             
36223             this.collapsed = true;
36224             if(this.split){
36225                 this.split.el.hide();
36226             }
36227             if(this.config.animate && skipAnim !== true){
36228                 this.fireEvent("invalidated", this);
36229                 this.animateCollapse();
36230             }else{
36231                 this.el.setLocation(-20000,-20000);
36232                 this.el.hide();
36233                 this.collapsedEl.show();
36234                 this.fireEvent("collapsed", this);
36235                 this.fireEvent("invalidated", this);
36236             }
36237         }
36238         
36239     },
36240 */
36241     animateCollapse : function(){
36242         // overridden
36243     },
36244
36245     /**
36246      * Expands this region if it was previously collapsed.
36247      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36248      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36249      */
36250     /*
36251     expand : function(e, skipAnim){
36252         if(e) {
36253             e.stopPropagation();
36254         }
36255         if(!this.collapsed || this.el.hasActiveFx()) {
36256             return;
36257         }
36258         if(this.isSlid){
36259             this.afterSlideIn();
36260             skipAnim = true;
36261         }
36262         this.collapsed = false;
36263         if(this.config.animate && skipAnim !== true){
36264             this.animateExpand();
36265         }else{
36266             this.el.show();
36267             if(this.split){
36268                 this.split.el.show();
36269             }
36270             this.collapsedEl.setLocation(-2000,-2000);
36271             this.collapsedEl.hide();
36272             this.fireEvent("invalidated", this);
36273             this.fireEvent("expanded", this);
36274         }
36275     },
36276 */
36277     animateExpand : function(){
36278         // overridden
36279     },
36280
36281     initTabs : function()
36282     {
36283         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36284         
36285         var ts = new Roo.bootstrap.panel.Tabs({
36286                 el: this.bodyEl.dom,
36287                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36288                 disableTooltips: this.config.disableTabTips,
36289                 toolbar : this.config.toolbar
36290             });
36291         
36292         if(this.config.hideTabs){
36293             ts.stripWrap.setDisplayed(false);
36294         }
36295         this.tabs = ts;
36296         ts.resizeTabs = this.config.resizeTabs === true;
36297         ts.minTabWidth = this.config.minTabWidth || 40;
36298         ts.maxTabWidth = this.config.maxTabWidth || 250;
36299         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36300         ts.monitorResize = false;
36301         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36302         ts.bodyEl.addClass('roo-layout-tabs-body');
36303         this.panels.each(this.initPanelAsTab, this);
36304     },
36305
36306     initPanelAsTab : function(panel){
36307         var ti = this.tabs.addTab(
36308             panel.getEl().id,
36309             panel.getTitle(),
36310             null,
36311             this.config.closeOnTab && panel.isClosable(),
36312             panel.tpl
36313         );
36314         if(panel.tabTip !== undefined){
36315             ti.setTooltip(panel.tabTip);
36316         }
36317         ti.on("activate", function(){
36318               this.setActivePanel(panel);
36319         }, this);
36320         
36321         if(this.config.closeOnTab){
36322             ti.on("beforeclose", function(t, e){
36323                 e.cancel = true;
36324                 this.remove(panel);
36325             }, this);
36326         }
36327         
36328         panel.tabItem = ti;
36329         
36330         return ti;
36331     },
36332
36333     updatePanelTitle : function(panel, title)
36334     {
36335         if(this.activePanel == panel){
36336             this.updateTitle(title);
36337         }
36338         if(this.tabs){
36339             var ti = this.tabs.getTab(panel.getEl().id);
36340             ti.setText(title);
36341             if(panel.tabTip !== undefined){
36342                 ti.setTooltip(panel.tabTip);
36343             }
36344         }
36345     },
36346
36347     updateTitle : function(title){
36348         if(this.titleTextEl && !this.config.title){
36349             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36350         }
36351     },
36352
36353     setActivePanel : function(panel)
36354     {
36355         panel = this.getPanel(panel);
36356         if(this.activePanel && this.activePanel != panel){
36357             if(this.activePanel.setActiveState(false) === false){
36358                 return;
36359             }
36360         }
36361         this.activePanel = panel;
36362         panel.setActiveState(true);
36363         if(this.panelSize){
36364             panel.setSize(this.panelSize.width, this.panelSize.height);
36365         }
36366         if(this.closeBtn){
36367             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36368         }
36369         this.updateTitle(panel.getTitle());
36370         if(this.tabs){
36371             this.fireEvent("invalidated", this);
36372         }
36373         this.fireEvent("panelactivated", this, panel);
36374     },
36375
36376     /**
36377      * Shows the specified panel.
36378      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36379      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36380      */
36381     showPanel : function(panel)
36382     {
36383         panel = this.getPanel(panel);
36384         if(panel){
36385             if(this.tabs){
36386                 var tab = this.tabs.getTab(panel.getEl().id);
36387                 if(tab.isHidden()){
36388                     this.tabs.unhideTab(tab.id);
36389                 }
36390                 tab.activate();
36391             }else{
36392                 this.setActivePanel(panel);
36393             }
36394         }
36395         return panel;
36396     },
36397
36398     /**
36399      * Get the active panel for this region.
36400      * @return {Roo.ContentPanel} The active panel or null
36401      */
36402     getActivePanel : function(){
36403         return this.activePanel;
36404     },
36405
36406     validateVisibility : function(){
36407         if(this.panels.getCount() < 1){
36408             this.updateTitle("&#160;");
36409             this.closeBtn.hide();
36410             this.hide();
36411         }else{
36412             if(!this.isVisible()){
36413                 this.show();
36414             }
36415         }
36416     },
36417
36418     /**
36419      * Adds the passed ContentPanel(s) to this region.
36420      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36421      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36422      */
36423     add : function(panel)
36424     {
36425         if(arguments.length > 1){
36426             for(var i = 0, len = arguments.length; i < len; i++) {
36427                 this.add(arguments[i]);
36428             }
36429             return null;
36430         }
36431         
36432         // if we have not been rendered yet, then we can not really do much of this..
36433         if (!this.bodyEl) {
36434             this.unrendered_panels.push(panel);
36435             return panel;
36436         }
36437         
36438         
36439         
36440         
36441         if(this.hasPanel(panel)){
36442             this.showPanel(panel);
36443             return panel;
36444         }
36445         panel.setRegion(this);
36446         this.panels.add(panel);
36447        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36448             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36449             // and hide them... ???
36450             this.bodyEl.dom.appendChild(panel.getEl().dom);
36451             if(panel.background !== true){
36452                 this.setActivePanel(panel);
36453             }
36454             this.fireEvent("paneladded", this, panel);
36455             return panel;
36456         }
36457         */
36458         if(!this.tabs){
36459             this.initTabs();
36460         }else{
36461             this.initPanelAsTab(panel);
36462         }
36463         
36464         
36465         if(panel.background !== true){
36466             this.tabs.activate(panel.getEl().id);
36467         }
36468         this.fireEvent("paneladded", this, panel);
36469         return panel;
36470     },
36471
36472     /**
36473      * Hides the tab for the specified panel.
36474      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36475      */
36476     hidePanel : function(panel){
36477         if(this.tabs && (panel = this.getPanel(panel))){
36478             this.tabs.hideTab(panel.getEl().id);
36479         }
36480     },
36481
36482     /**
36483      * Unhides the tab for a previously hidden panel.
36484      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36485      */
36486     unhidePanel : function(panel){
36487         if(this.tabs && (panel = this.getPanel(panel))){
36488             this.tabs.unhideTab(panel.getEl().id);
36489         }
36490     },
36491
36492     clearPanels : function(){
36493         while(this.panels.getCount() > 0){
36494              this.remove(this.panels.first());
36495         }
36496     },
36497
36498     /**
36499      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36500      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36501      * @param {Boolean} preservePanel Overrides the config preservePanel option
36502      * @return {Roo.ContentPanel} The panel that was removed
36503      */
36504     remove : function(panel, preservePanel)
36505     {
36506         panel = this.getPanel(panel);
36507         if(!panel){
36508             return null;
36509         }
36510         var e = {};
36511         this.fireEvent("beforeremove", this, panel, e);
36512         if(e.cancel === true){
36513             return null;
36514         }
36515         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36516         var panelId = panel.getId();
36517         this.panels.removeKey(panelId);
36518         if(preservePanel){
36519             document.body.appendChild(panel.getEl().dom);
36520         }
36521         if(this.tabs){
36522             this.tabs.removeTab(panel.getEl().id);
36523         }else if (!preservePanel){
36524             this.bodyEl.dom.removeChild(panel.getEl().dom);
36525         }
36526         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36527             var p = this.panels.first();
36528             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36529             tempEl.appendChild(p.getEl().dom);
36530             this.bodyEl.update("");
36531             this.bodyEl.dom.appendChild(p.getEl().dom);
36532             tempEl = null;
36533             this.updateTitle(p.getTitle());
36534             this.tabs = null;
36535             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36536             this.setActivePanel(p);
36537         }
36538         panel.setRegion(null);
36539         if(this.activePanel == panel){
36540             this.activePanel = null;
36541         }
36542         if(this.config.autoDestroy !== false && preservePanel !== true){
36543             try{panel.destroy();}catch(e){}
36544         }
36545         this.fireEvent("panelremoved", this, panel);
36546         return panel;
36547     },
36548
36549     /**
36550      * Returns the TabPanel component used by this region
36551      * @return {Roo.TabPanel}
36552      */
36553     getTabs : function(){
36554         return this.tabs;
36555     },
36556
36557     createTool : function(parentEl, className){
36558         var btn = Roo.DomHelper.append(parentEl, {
36559             tag: "div",
36560             cls: "x-layout-tools-button",
36561             children: [ {
36562                 tag: "div",
36563                 cls: "roo-layout-tools-button-inner " + className,
36564                 html: "&#160;"
36565             }]
36566         }, true);
36567         btn.addClassOnOver("roo-layout-tools-button-over");
36568         return btn;
36569     }
36570 });/*
36571  * Based on:
36572  * Ext JS Library 1.1.1
36573  * Copyright(c) 2006-2007, Ext JS, LLC.
36574  *
36575  * Originally Released Under LGPL - original licence link has changed is not relivant.
36576  *
36577  * Fork - LGPL
36578  * <script type="text/javascript">
36579  */
36580  
36581
36582
36583 /**
36584  * @class Roo.SplitLayoutRegion
36585  * @extends Roo.LayoutRegion
36586  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36587  */
36588 Roo.bootstrap.layout.Split = function(config){
36589     this.cursor = config.cursor;
36590     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36591 };
36592
36593 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36594 {
36595     splitTip : "Drag to resize.",
36596     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36597     useSplitTips : false,
36598
36599     applyConfig : function(config){
36600         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36601     },
36602     
36603     onRender : function(ctr,pos) {
36604         
36605         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36606         if(!this.config.split){
36607             return;
36608         }
36609         if(!this.split){
36610             
36611             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36612                             tag: "div",
36613                             id: this.el.id + "-split",
36614                             cls: "roo-layout-split roo-layout-split-"+this.position,
36615                             html: "&#160;"
36616             });
36617             /** The SplitBar for this region 
36618             * @type Roo.SplitBar */
36619             // does not exist yet...
36620             Roo.log([this.position, this.orientation]);
36621             
36622             this.split = new Roo.bootstrap.SplitBar({
36623                 dragElement : splitEl,
36624                 resizingElement: this.el,
36625                 orientation : this.orientation
36626             });
36627             
36628             this.split.on("moved", this.onSplitMove, this);
36629             this.split.useShim = this.config.useShim === true;
36630             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36631             if(this.useSplitTips){
36632                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36633             }
36634             //if(config.collapsible){
36635             //    this.split.el.on("dblclick", this.collapse,  this);
36636             //}
36637         }
36638         if(typeof this.config.minSize != "undefined"){
36639             this.split.minSize = this.config.minSize;
36640         }
36641         if(typeof this.config.maxSize != "undefined"){
36642             this.split.maxSize = this.config.maxSize;
36643         }
36644         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36645             this.hideSplitter();
36646         }
36647         
36648     },
36649
36650     getHMaxSize : function(){
36651          var cmax = this.config.maxSize || 10000;
36652          var center = this.mgr.getRegion("center");
36653          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36654     },
36655
36656     getVMaxSize : function(){
36657          var cmax = this.config.maxSize || 10000;
36658          var center = this.mgr.getRegion("center");
36659          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36660     },
36661
36662     onSplitMove : function(split, newSize){
36663         this.fireEvent("resized", this, newSize);
36664     },
36665     
36666     /** 
36667      * Returns the {@link Roo.SplitBar} for this region.
36668      * @return {Roo.SplitBar}
36669      */
36670     getSplitBar : function(){
36671         return this.split;
36672     },
36673     
36674     hide : function(){
36675         this.hideSplitter();
36676         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36677     },
36678
36679     hideSplitter : function(){
36680         if(this.split){
36681             this.split.el.setLocation(-2000,-2000);
36682             this.split.el.hide();
36683         }
36684     },
36685
36686     show : function(){
36687         if(this.split){
36688             this.split.el.show();
36689         }
36690         Roo.bootstrap.layout.Split.superclass.show.call(this);
36691     },
36692     
36693     beforeSlide: function(){
36694         if(Roo.isGecko){// firefox overflow auto bug workaround
36695             this.bodyEl.clip();
36696             if(this.tabs) {
36697                 this.tabs.bodyEl.clip();
36698             }
36699             if(this.activePanel){
36700                 this.activePanel.getEl().clip();
36701                 
36702                 if(this.activePanel.beforeSlide){
36703                     this.activePanel.beforeSlide();
36704                 }
36705             }
36706         }
36707     },
36708     
36709     afterSlide : function(){
36710         if(Roo.isGecko){// firefox overflow auto bug workaround
36711             this.bodyEl.unclip();
36712             if(this.tabs) {
36713                 this.tabs.bodyEl.unclip();
36714             }
36715             if(this.activePanel){
36716                 this.activePanel.getEl().unclip();
36717                 if(this.activePanel.afterSlide){
36718                     this.activePanel.afterSlide();
36719                 }
36720             }
36721         }
36722     },
36723
36724     initAutoHide : function(){
36725         if(this.autoHide !== false){
36726             if(!this.autoHideHd){
36727                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36728                 this.autoHideHd = {
36729                     "mouseout": function(e){
36730                         if(!e.within(this.el, true)){
36731                             st.delay(500);
36732                         }
36733                     },
36734                     "mouseover" : function(e){
36735                         st.cancel();
36736                     },
36737                     scope : this
36738                 };
36739             }
36740             this.el.on(this.autoHideHd);
36741         }
36742     },
36743
36744     clearAutoHide : function(){
36745         if(this.autoHide !== false){
36746             this.el.un("mouseout", this.autoHideHd.mouseout);
36747             this.el.un("mouseover", this.autoHideHd.mouseover);
36748         }
36749     },
36750
36751     clearMonitor : function(){
36752         Roo.get(document).un("click", this.slideInIf, this);
36753     },
36754
36755     // these names are backwards but not changed for compat
36756     slideOut : function(){
36757         if(this.isSlid || this.el.hasActiveFx()){
36758             return;
36759         }
36760         this.isSlid = true;
36761         if(this.collapseBtn){
36762             this.collapseBtn.hide();
36763         }
36764         this.closeBtnState = this.closeBtn.getStyle('display');
36765         this.closeBtn.hide();
36766         if(this.stickBtn){
36767             this.stickBtn.show();
36768         }
36769         this.el.show();
36770         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36771         this.beforeSlide();
36772         this.el.setStyle("z-index", 10001);
36773         this.el.slideIn(this.getSlideAnchor(), {
36774             callback: function(){
36775                 this.afterSlide();
36776                 this.initAutoHide();
36777                 Roo.get(document).on("click", this.slideInIf, this);
36778                 this.fireEvent("slideshow", this);
36779             },
36780             scope: this,
36781             block: true
36782         });
36783     },
36784
36785     afterSlideIn : function(){
36786         this.clearAutoHide();
36787         this.isSlid = false;
36788         this.clearMonitor();
36789         this.el.setStyle("z-index", "");
36790         if(this.collapseBtn){
36791             this.collapseBtn.show();
36792         }
36793         this.closeBtn.setStyle('display', this.closeBtnState);
36794         if(this.stickBtn){
36795             this.stickBtn.hide();
36796         }
36797         this.fireEvent("slidehide", this);
36798     },
36799
36800     slideIn : function(cb){
36801         if(!this.isSlid || this.el.hasActiveFx()){
36802             Roo.callback(cb);
36803             return;
36804         }
36805         this.isSlid = false;
36806         this.beforeSlide();
36807         this.el.slideOut(this.getSlideAnchor(), {
36808             callback: function(){
36809                 this.el.setLeftTop(-10000, -10000);
36810                 this.afterSlide();
36811                 this.afterSlideIn();
36812                 Roo.callback(cb);
36813             },
36814             scope: this,
36815             block: true
36816         });
36817     },
36818     
36819     slideInIf : function(e){
36820         if(!e.within(this.el)){
36821             this.slideIn();
36822         }
36823     },
36824
36825     animateCollapse : function(){
36826         this.beforeSlide();
36827         this.el.setStyle("z-index", 20000);
36828         var anchor = this.getSlideAnchor();
36829         this.el.slideOut(anchor, {
36830             callback : function(){
36831                 this.el.setStyle("z-index", "");
36832                 this.collapsedEl.slideIn(anchor, {duration:.3});
36833                 this.afterSlide();
36834                 this.el.setLocation(-10000,-10000);
36835                 this.el.hide();
36836                 this.fireEvent("collapsed", this);
36837             },
36838             scope: this,
36839             block: true
36840         });
36841     },
36842
36843     animateExpand : function(){
36844         this.beforeSlide();
36845         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36846         this.el.setStyle("z-index", 20000);
36847         this.collapsedEl.hide({
36848             duration:.1
36849         });
36850         this.el.slideIn(this.getSlideAnchor(), {
36851             callback : function(){
36852                 this.el.setStyle("z-index", "");
36853                 this.afterSlide();
36854                 if(this.split){
36855                     this.split.el.show();
36856                 }
36857                 this.fireEvent("invalidated", this);
36858                 this.fireEvent("expanded", this);
36859             },
36860             scope: this,
36861             block: true
36862         });
36863     },
36864
36865     anchors : {
36866         "west" : "left",
36867         "east" : "right",
36868         "north" : "top",
36869         "south" : "bottom"
36870     },
36871
36872     sanchors : {
36873         "west" : "l",
36874         "east" : "r",
36875         "north" : "t",
36876         "south" : "b"
36877     },
36878
36879     canchors : {
36880         "west" : "tl-tr",
36881         "east" : "tr-tl",
36882         "north" : "tl-bl",
36883         "south" : "bl-tl"
36884     },
36885
36886     getAnchor : function(){
36887         return this.anchors[this.position];
36888     },
36889
36890     getCollapseAnchor : function(){
36891         return this.canchors[this.position];
36892     },
36893
36894     getSlideAnchor : function(){
36895         return this.sanchors[this.position];
36896     },
36897
36898     getAlignAdj : function(){
36899         var cm = this.cmargins;
36900         switch(this.position){
36901             case "west":
36902                 return [0, 0];
36903             break;
36904             case "east":
36905                 return [0, 0];
36906             break;
36907             case "north":
36908                 return [0, 0];
36909             break;
36910             case "south":
36911                 return [0, 0];
36912             break;
36913         }
36914     },
36915
36916     getExpandAdj : function(){
36917         var c = this.collapsedEl, cm = this.cmargins;
36918         switch(this.position){
36919             case "west":
36920                 return [-(cm.right+c.getWidth()+cm.left), 0];
36921             break;
36922             case "east":
36923                 return [cm.right+c.getWidth()+cm.left, 0];
36924             break;
36925             case "north":
36926                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36927             break;
36928             case "south":
36929                 return [0, cm.top+cm.bottom+c.getHeight()];
36930             break;
36931         }
36932     }
36933 });/*
36934  * Based on:
36935  * Ext JS Library 1.1.1
36936  * Copyright(c) 2006-2007, Ext JS, LLC.
36937  *
36938  * Originally Released Under LGPL - original licence link has changed is not relivant.
36939  *
36940  * Fork - LGPL
36941  * <script type="text/javascript">
36942  */
36943 /*
36944  * These classes are private internal classes
36945  */
36946 Roo.bootstrap.layout.Center = function(config){
36947     config.region = "center";
36948     Roo.bootstrap.layout.Region.call(this, config);
36949     this.visible = true;
36950     this.minWidth = config.minWidth || 20;
36951     this.minHeight = config.minHeight || 20;
36952 };
36953
36954 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36955     hide : function(){
36956         // center panel can't be hidden
36957     },
36958     
36959     show : function(){
36960         // center panel can't be hidden
36961     },
36962     
36963     getMinWidth: function(){
36964         return this.minWidth;
36965     },
36966     
36967     getMinHeight: function(){
36968         return this.minHeight;
36969     }
36970 });
36971
36972
36973
36974
36975  
36976
36977
36978
36979
36980
36981 Roo.bootstrap.layout.North = function(config)
36982 {
36983     config.region = 'north';
36984     config.cursor = 'n-resize';
36985     
36986     Roo.bootstrap.layout.Split.call(this, config);
36987     
36988     
36989     if(this.split){
36990         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36991         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36992         this.split.el.addClass("roo-layout-split-v");
36993     }
36994     var size = config.initialSize || config.height;
36995     if(typeof size != "undefined"){
36996         this.el.setHeight(size);
36997     }
36998 };
36999 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37000 {
37001     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37002     
37003     
37004     
37005     getBox : function(){
37006         if(this.collapsed){
37007             return this.collapsedEl.getBox();
37008         }
37009         var box = this.el.getBox();
37010         if(this.split){
37011             box.height += this.split.el.getHeight();
37012         }
37013         return box;
37014     },
37015     
37016     updateBox : function(box){
37017         if(this.split && !this.collapsed){
37018             box.height -= this.split.el.getHeight();
37019             this.split.el.setLeft(box.x);
37020             this.split.el.setTop(box.y+box.height);
37021             this.split.el.setWidth(box.width);
37022         }
37023         if(this.collapsed){
37024             this.updateBody(box.width, null);
37025         }
37026         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37027     }
37028 });
37029
37030
37031
37032
37033
37034 Roo.bootstrap.layout.South = function(config){
37035     config.region = 'south';
37036     config.cursor = 's-resize';
37037     Roo.bootstrap.layout.Split.call(this, config);
37038     if(this.split){
37039         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37040         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37041         this.split.el.addClass("roo-layout-split-v");
37042     }
37043     var size = config.initialSize || config.height;
37044     if(typeof size != "undefined"){
37045         this.el.setHeight(size);
37046     }
37047 };
37048
37049 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37050     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37051     getBox : function(){
37052         if(this.collapsed){
37053             return this.collapsedEl.getBox();
37054         }
37055         var box = this.el.getBox();
37056         if(this.split){
37057             var sh = this.split.el.getHeight();
37058             box.height += sh;
37059             box.y -= sh;
37060         }
37061         return box;
37062     },
37063     
37064     updateBox : function(box){
37065         if(this.split && !this.collapsed){
37066             var sh = this.split.el.getHeight();
37067             box.height -= sh;
37068             box.y += sh;
37069             this.split.el.setLeft(box.x);
37070             this.split.el.setTop(box.y-sh);
37071             this.split.el.setWidth(box.width);
37072         }
37073         if(this.collapsed){
37074             this.updateBody(box.width, null);
37075         }
37076         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37077     }
37078 });
37079
37080 Roo.bootstrap.layout.East = function(config){
37081     config.region = "east";
37082     config.cursor = "e-resize";
37083     Roo.bootstrap.layout.Split.call(this, config);
37084     if(this.split){
37085         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37086         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37087         this.split.el.addClass("roo-layout-split-h");
37088     }
37089     var size = config.initialSize || config.width;
37090     if(typeof size != "undefined"){
37091         this.el.setWidth(size);
37092     }
37093 };
37094 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37095     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37096     getBox : function(){
37097         if(this.collapsed){
37098             return this.collapsedEl.getBox();
37099         }
37100         var box = this.el.getBox();
37101         if(this.split){
37102             var sw = this.split.el.getWidth();
37103             box.width += sw;
37104             box.x -= sw;
37105         }
37106         return box;
37107     },
37108
37109     updateBox : function(box){
37110         if(this.split && !this.collapsed){
37111             var sw = this.split.el.getWidth();
37112             box.width -= sw;
37113             this.split.el.setLeft(box.x);
37114             this.split.el.setTop(box.y);
37115             this.split.el.setHeight(box.height);
37116             box.x += sw;
37117         }
37118         if(this.collapsed){
37119             this.updateBody(null, box.height);
37120         }
37121         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37122     }
37123 });
37124
37125 Roo.bootstrap.layout.West = function(config){
37126     config.region = "west";
37127     config.cursor = "w-resize";
37128     
37129     Roo.bootstrap.layout.Split.call(this, config);
37130     if(this.split){
37131         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37132         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37133         this.split.el.addClass("roo-layout-split-h");
37134     }
37135     
37136 };
37137 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37138     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37139     
37140     onRender: function(ctr, pos)
37141     {
37142         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37143         var size = this.config.initialSize || this.config.width;
37144         if(typeof size != "undefined"){
37145             this.el.setWidth(size);
37146         }
37147     },
37148     
37149     getBox : function(){
37150         if(this.collapsed){
37151             return this.collapsedEl.getBox();
37152         }
37153         var box = this.el.getBox();
37154         if(this.split){
37155             box.width += this.split.el.getWidth();
37156         }
37157         return box;
37158     },
37159     
37160     updateBox : function(box){
37161         if(this.split && !this.collapsed){
37162             var sw = this.split.el.getWidth();
37163             box.width -= sw;
37164             this.split.el.setLeft(box.x+box.width);
37165             this.split.el.setTop(box.y);
37166             this.split.el.setHeight(box.height);
37167         }
37168         if(this.collapsed){
37169             this.updateBody(null, box.height);
37170         }
37171         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37172     }
37173 });
37174 Roo.namespace("Roo.bootstrap.panel");/*
37175  * Based on:
37176  * Ext JS Library 1.1.1
37177  * Copyright(c) 2006-2007, Ext JS, LLC.
37178  *
37179  * Originally Released Under LGPL - original licence link has changed is not relivant.
37180  *
37181  * Fork - LGPL
37182  * <script type="text/javascript">
37183  */
37184 /**
37185  * @class Roo.ContentPanel
37186  * @extends Roo.util.Observable
37187  * A basic ContentPanel element.
37188  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37189  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37190  * @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
37191  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37192  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37193  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37194  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37195  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37196  * @cfg {String} title          The title for this panel
37197  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37198  * @cfg {String} url            Calls {@link #setUrl} with this value
37199  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37200  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37201  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37202  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37203  * @cfg {Boolean} badges render the badges
37204
37205  * @constructor
37206  * Create a new ContentPanel.
37207  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37208  * @param {String/Object} config A string to set only the title or a config object
37209  * @param {String} content (optional) Set the HTML content for this panel
37210  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37211  */
37212 Roo.bootstrap.panel.Content = function( config){
37213     
37214     this.tpl = config.tpl || false;
37215     
37216     var el = config.el;
37217     var content = config.content;
37218
37219     if(config.autoCreate){ // xtype is available if this is called from factory
37220         el = Roo.id();
37221     }
37222     this.el = Roo.get(el);
37223     if(!this.el && config && config.autoCreate){
37224         if(typeof config.autoCreate == "object"){
37225             if(!config.autoCreate.id){
37226                 config.autoCreate.id = config.id||el;
37227             }
37228             this.el = Roo.DomHelper.append(document.body,
37229                         config.autoCreate, true);
37230         }else{
37231             var elcfg =  {   tag: "div",
37232                             cls: "roo-layout-inactive-content",
37233                             id: config.id||el
37234                             };
37235             if (config.html) {
37236                 elcfg.html = config.html;
37237                 
37238             }
37239                         
37240             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37241         }
37242     } 
37243     this.closable = false;
37244     this.loaded = false;
37245     this.active = false;
37246    
37247       
37248     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37249         
37250         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37251         
37252         this.wrapEl = this.el; //this.el.wrap();
37253         var ti = [];
37254         if (config.toolbar.items) {
37255             ti = config.toolbar.items ;
37256             delete config.toolbar.items ;
37257         }
37258         
37259         var nitems = [];
37260         this.toolbar.render(this.wrapEl, 'before');
37261         for(var i =0;i < ti.length;i++) {
37262           //  Roo.log(['add child', items[i]]);
37263             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37264         }
37265         this.toolbar.items = nitems;
37266         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37267         delete config.toolbar;
37268         
37269     }
37270     /*
37271     // xtype created footer. - not sure if will work as we normally have to render first..
37272     if (this.footer && !this.footer.el && this.footer.xtype) {
37273         if (!this.wrapEl) {
37274             this.wrapEl = this.el.wrap();
37275         }
37276     
37277         this.footer.container = this.wrapEl.createChild();
37278          
37279         this.footer = Roo.factory(this.footer, Roo);
37280         
37281     }
37282     */
37283     
37284      if(typeof config == "string"){
37285         this.title = config;
37286     }else{
37287         Roo.apply(this, config);
37288     }
37289     
37290     if(this.resizeEl){
37291         this.resizeEl = Roo.get(this.resizeEl, true);
37292     }else{
37293         this.resizeEl = this.el;
37294     }
37295     // handle view.xtype
37296     
37297  
37298     
37299     
37300     this.addEvents({
37301         /**
37302          * @event activate
37303          * Fires when this panel is activated. 
37304          * @param {Roo.ContentPanel} this
37305          */
37306         "activate" : true,
37307         /**
37308          * @event deactivate
37309          * Fires when this panel is activated. 
37310          * @param {Roo.ContentPanel} this
37311          */
37312         "deactivate" : true,
37313
37314         /**
37315          * @event resize
37316          * Fires when this panel is resized if fitToFrame is true.
37317          * @param {Roo.ContentPanel} this
37318          * @param {Number} width The width after any component adjustments
37319          * @param {Number} height The height after any component adjustments
37320          */
37321         "resize" : true,
37322         
37323          /**
37324          * @event render
37325          * Fires when this tab is created
37326          * @param {Roo.ContentPanel} this
37327          */
37328         "render" : true
37329         
37330         
37331         
37332     });
37333     
37334
37335     
37336     
37337     if(this.autoScroll){
37338         this.resizeEl.setStyle("overflow", "auto");
37339     } else {
37340         // fix randome scrolling
37341         //this.el.on('scroll', function() {
37342         //    Roo.log('fix random scolling');
37343         //    this.scrollTo('top',0); 
37344         //});
37345     }
37346     content = content || this.content;
37347     if(content){
37348         this.setContent(content);
37349     }
37350     if(config && config.url){
37351         this.setUrl(this.url, this.params, this.loadOnce);
37352     }
37353     
37354     
37355     
37356     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37357     
37358     if (this.view && typeof(this.view.xtype) != 'undefined') {
37359         this.view.el = this.el.appendChild(document.createElement("div"));
37360         this.view = Roo.factory(this.view); 
37361         this.view.render  &&  this.view.render(false, '');  
37362     }
37363     
37364     
37365     this.fireEvent('render', this);
37366 };
37367
37368 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37369     
37370     tabTip : '',
37371     
37372     setRegion : function(region){
37373         this.region = region;
37374         this.setActiveClass(region && !this.background);
37375     },
37376     
37377     
37378     setActiveClass: function(state)
37379     {
37380         if(state){
37381            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37382            this.el.setStyle('position','relative');
37383         }else{
37384            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37385            this.el.setStyle('position', 'absolute');
37386         } 
37387     },
37388     
37389     /**
37390      * Returns the toolbar for this Panel if one was configured. 
37391      * @return {Roo.Toolbar} 
37392      */
37393     getToolbar : function(){
37394         return this.toolbar;
37395     },
37396     
37397     setActiveState : function(active)
37398     {
37399         this.active = active;
37400         this.setActiveClass(active);
37401         if(!active){
37402             if(this.fireEvent("deactivate", this) === false){
37403                 return false;
37404             }
37405             return true;
37406         }
37407         this.fireEvent("activate", this);
37408         return true;
37409     },
37410     /**
37411      * Updates this panel's element
37412      * @param {String} content The new content
37413      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37414     */
37415     setContent : function(content, loadScripts){
37416         this.el.update(content, loadScripts);
37417     },
37418
37419     ignoreResize : function(w, h){
37420         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37421             return true;
37422         }else{
37423             this.lastSize = {width: w, height: h};
37424             return false;
37425         }
37426     },
37427     /**
37428      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37429      * @return {Roo.UpdateManager} The UpdateManager
37430      */
37431     getUpdateManager : function(){
37432         return this.el.getUpdateManager();
37433     },
37434      /**
37435      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37436      * @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:
37437 <pre><code>
37438 panel.load({
37439     url: "your-url.php",
37440     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37441     callback: yourFunction,
37442     scope: yourObject, //(optional scope)
37443     discardUrl: false,
37444     nocache: false,
37445     text: "Loading...",
37446     timeout: 30,
37447     scripts: false
37448 });
37449 </code></pre>
37450      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37451      * 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.
37452      * @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}
37453      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37454      * @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.
37455      * @return {Roo.ContentPanel} this
37456      */
37457     load : function(){
37458         var um = this.el.getUpdateManager();
37459         um.update.apply(um, arguments);
37460         return this;
37461     },
37462
37463
37464     /**
37465      * 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.
37466      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37467      * @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)
37468      * @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)
37469      * @return {Roo.UpdateManager} The UpdateManager
37470      */
37471     setUrl : function(url, params, loadOnce){
37472         if(this.refreshDelegate){
37473             this.removeListener("activate", this.refreshDelegate);
37474         }
37475         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37476         this.on("activate", this.refreshDelegate);
37477         return this.el.getUpdateManager();
37478     },
37479     
37480     _handleRefresh : function(url, params, loadOnce){
37481         if(!loadOnce || !this.loaded){
37482             var updater = this.el.getUpdateManager();
37483             updater.update(url, params, this._setLoaded.createDelegate(this));
37484         }
37485     },
37486     
37487     _setLoaded : function(){
37488         this.loaded = true;
37489     }, 
37490     
37491     /**
37492      * Returns this panel's id
37493      * @return {String} 
37494      */
37495     getId : function(){
37496         return this.el.id;
37497     },
37498     
37499     /** 
37500      * Returns this panel's element - used by regiosn to add.
37501      * @return {Roo.Element} 
37502      */
37503     getEl : function(){
37504         return this.wrapEl || this.el;
37505     },
37506     
37507    
37508     
37509     adjustForComponents : function(width, height)
37510     {
37511         //Roo.log('adjustForComponents ');
37512         if(this.resizeEl != this.el){
37513             width -= this.el.getFrameWidth('lr');
37514             height -= this.el.getFrameWidth('tb');
37515         }
37516         if(this.toolbar){
37517             var te = this.toolbar.getEl();
37518             te.setWidth(width);
37519             height -= te.getHeight();
37520         }
37521         if(this.footer){
37522             var te = this.footer.getEl();
37523             te.setWidth(width);
37524             height -= te.getHeight();
37525         }
37526         
37527         
37528         if(this.adjustments){
37529             width += this.adjustments[0];
37530             height += this.adjustments[1];
37531         }
37532         return {"width": width, "height": height};
37533     },
37534     
37535     setSize : function(width, height){
37536         if(this.fitToFrame && !this.ignoreResize(width, height)){
37537             if(this.fitContainer && this.resizeEl != this.el){
37538                 this.el.setSize(width, height);
37539             }
37540             var size = this.adjustForComponents(width, height);
37541             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37542             this.fireEvent('resize', this, size.width, size.height);
37543         }
37544     },
37545     
37546     /**
37547      * Returns this panel's title
37548      * @return {String} 
37549      */
37550     getTitle : function(){
37551         
37552         if (typeof(this.title) != 'object') {
37553             return this.title;
37554         }
37555         
37556         var t = '';
37557         for (var k in this.title) {
37558             if (!this.title.hasOwnProperty(k)) {
37559                 continue;
37560             }
37561             
37562             if (k.indexOf('-') >= 0) {
37563                 var s = k.split('-');
37564                 for (var i = 0; i<s.length; i++) {
37565                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37566                 }
37567             } else {
37568                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37569             }
37570         }
37571         return t;
37572     },
37573     
37574     /**
37575      * Set this panel's title
37576      * @param {String} title
37577      */
37578     setTitle : function(title){
37579         this.title = title;
37580         if(this.region){
37581             this.region.updatePanelTitle(this, title);
37582         }
37583     },
37584     
37585     /**
37586      * Returns true is this panel was configured to be closable
37587      * @return {Boolean} 
37588      */
37589     isClosable : function(){
37590         return this.closable;
37591     },
37592     
37593     beforeSlide : function(){
37594         this.el.clip();
37595         this.resizeEl.clip();
37596     },
37597     
37598     afterSlide : function(){
37599         this.el.unclip();
37600         this.resizeEl.unclip();
37601     },
37602     
37603     /**
37604      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37605      *   Will fail silently if the {@link #setUrl} method has not been called.
37606      *   This does not activate the panel, just updates its content.
37607      */
37608     refresh : function(){
37609         if(this.refreshDelegate){
37610            this.loaded = false;
37611            this.refreshDelegate();
37612         }
37613     },
37614     
37615     /**
37616      * Destroys this panel
37617      */
37618     destroy : function(){
37619         this.el.removeAllListeners();
37620         var tempEl = document.createElement("span");
37621         tempEl.appendChild(this.el.dom);
37622         tempEl.innerHTML = "";
37623         this.el.remove();
37624         this.el = null;
37625     },
37626     
37627     /**
37628      * form - if the content panel contains a form - this is a reference to it.
37629      * @type {Roo.form.Form}
37630      */
37631     form : false,
37632     /**
37633      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37634      *    This contains a reference to it.
37635      * @type {Roo.View}
37636      */
37637     view : false,
37638     
37639       /**
37640      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37641      * <pre><code>
37642
37643 layout.addxtype({
37644        xtype : 'Form',
37645        items: [ .... ]
37646    }
37647 );
37648
37649 </code></pre>
37650      * @param {Object} cfg Xtype definition of item to add.
37651      */
37652     
37653     
37654     getChildContainer: function () {
37655         return this.getEl();
37656     }
37657     
37658     
37659     /*
37660         var  ret = new Roo.factory(cfg);
37661         return ret;
37662         
37663         
37664         // add form..
37665         if (cfg.xtype.match(/^Form$/)) {
37666             
37667             var el;
37668             //if (this.footer) {
37669             //    el = this.footer.container.insertSibling(false, 'before');
37670             //} else {
37671                 el = this.el.createChild();
37672             //}
37673
37674             this.form = new  Roo.form.Form(cfg);
37675             
37676             
37677             if ( this.form.allItems.length) {
37678                 this.form.render(el.dom);
37679             }
37680             return this.form;
37681         }
37682         // should only have one of theses..
37683         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37684             // views.. should not be just added - used named prop 'view''
37685             
37686             cfg.el = this.el.appendChild(document.createElement("div"));
37687             // factory?
37688             
37689             var ret = new Roo.factory(cfg);
37690              
37691              ret.render && ret.render(false, ''); // render blank..
37692             this.view = ret;
37693             return ret;
37694         }
37695         return false;
37696     }
37697     \*/
37698 });
37699  
37700 /**
37701  * @class Roo.bootstrap.panel.Grid
37702  * @extends Roo.bootstrap.panel.Content
37703  * @constructor
37704  * Create a new GridPanel.
37705  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37706  * @param {Object} config A the config object
37707   
37708  */
37709
37710
37711
37712 Roo.bootstrap.panel.Grid = function(config)
37713 {
37714     
37715       
37716     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37717         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37718
37719     config.el = this.wrapper;
37720     //this.el = this.wrapper;
37721     
37722       if (config.container) {
37723         // ctor'ed from a Border/panel.grid
37724         
37725         
37726         this.wrapper.setStyle("overflow", "hidden");
37727         this.wrapper.addClass('roo-grid-container');
37728
37729     }
37730     
37731     
37732     if(config.toolbar){
37733         var tool_el = this.wrapper.createChild();    
37734         this.toolbar = Roo.factory(config.toolbar);
37735         var ti = [];
37736         if (config.toolbar.items) {
37737             ti = config.toolbar.items ;
37738             delete config.toolbar.items ;
37739         }
37740         
37741         var nitems = [];
37742         this.toolbar.render(tool_el);
37743         for(var i =0;i < ti.length;i++) {
37744           //  Roo.log(['add child', items[i]]);
37745             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37746         }
37747         this.toolbar.items = nitems;
37748         
37749         delete config.toolbar;
37750     }
37751     
37752     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37753     config.grid.scrollBody = true;;
37754     config.grid.monitorWindowResize = false; // turn off autosizing
37755     config.grid.autoHeight = false;
37756     config.grid.autoWidth = false;
37757     
37758     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37759     
37760     if (config.background) {
37761         // render grid on panel activation (if panel background)
37762         this.on('activate', function(gp) {
37763             if (!gp.grid.rendered) {
37764                 gp.grid.render(this.wrapper);
37765                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37766             }
37767         });
37768             
37769     } else {
37770         this.grid.render(this.wrapper);
37771         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37772
37773     }
37774     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37775     // ??? needed ??? config.el = this.wrapper;
37776     
37777     
37778     
37779   
37780     // xtype created footer. - not sure if will work as we normally have to render first..
37781     if (this.footer && !this.footer.el && this.footer.xtype) {
37782         
37783         var ctr = this.grid.getView().getFooterPanel(true);
37784         this.footer.dataSource = this.grid.dataSource;
37785         this.footer = Roo.factory(this.footer, Roo);
37786         this.footer.render(ctr);
37787         
37788     }
37789     
37790     
37791     
37792     
37793      
37794 };
37795
37796 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37797     getId : function(){
37798         return this.grid.id;
37799     },
37800     
37801     /**
37802      * Returns the grid for this panel
37803      * @return {Roo.bootstrap.Table} 
37804      */
37805     getGrid : function(){
37806         return this.grid;    
37807     },
37808     
37809     setSize : function(width, height){
37810         if(!this.ignoreResize(width, height)){
37811             var grid = this.grid;
37812             var size = this.adjustForComponents(width, height);
37813             var gridel = grid.getGridEl();
37814             gridel.setSize(size.width, size.height);
37815             /*
37816             var thd = grid.getGridEl().select('thead',true).first();
37817             var tbd = grid.getGridEl().select('tbody', true).first();
37818             if (tbd) {
37819                 tbd.setSize(width, height - thd.getHeight());
37820             }
37821             */
37822             grid.autoSize();
37823         }
37824     },
37825      
37826     
37827     
37828     beforeSlide : function(){
37829         this.grid.getView().scroller.clip();
37830     },
37831     
37832     afterSlide : function(){
37833         this.grid.getView().scroller.unclip();
37834     },
37835     
37836     destroy : function(){
37837         this.grid.destroy();
37838         delete this.grid;
37839         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37840     }
37841 });
37842
37843 /**
37844  * @class Roo.bootstrap.panel.Nest
37845  * @extends Roo.bootstrap.panel.Content
37846  * @constructor
37847  * Create a new Panel, that can contain a layout.Border.
37848  * 
37849  * 
37850  * @param {Roo.BorderLayout} layout The layout for this panel
37851  * @param {String/Object} config A string to set only the title or a config object
37852  */
37853 Roo.bootstrap.panel.Nest = function(config)
37854 {
37855     // construct with only one argument..
37856     /* FIXME - implement nicer consturctors
37857     if (layout.layout) {
37858         config = layout;
37859         layout = config.layout;
37860         delete config.layout;
37861     }
37862     if (layout.xtype && !layout.getEl) {
37863         // then layout needs constructing..
37864         layout = Roo.factory(layout, Roo);
37865     }
37866     */
37867     
37868     config.el =  config.layout.getEl();
37869     
37870     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37871     
37872     config.layout.monitorWindowResize = false; // turn off autosizing
37873     this.layout = config.layout;
37874     this.layout.getEl().addClass("roo-layout-nested-layout");
37875     
37876     
37877     
37878     
37879 };
37880
37881 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37882
37883     setSize : function(width, height){
37884         if(!this.ignoreResize(width, height)){
37885             var size = this.adjustForComponents(width, height);
37886             var el = this.layout.getEl();
37887             if (size.height < 1) {
37888                 el.setWidth(size.width);   
37889             } else {
37890                 el.setSize(size.width, size.height);
37891             }
37892             var touch = el.dom.offsetWidth;
37893             this.layout.layout();
37894             // ie requires a double layout on the first pass
37895             if(Roo.isIE && !this.initialized){
37896                 this.initialized = true;
37897                 this.layout.layout();
37898             }
37899         }
37900     },
37901     
37902     // activate all subpanels if not currently active..
37903     
37904     setActiveState : function(active){
37905         this.active = active;
37906         this.setActiveClass(active);
37907         
37908         if(!active){
37909             this.fireEvent("deactivate", this);
37910             return;
37911         }
37912         
37913         this.fireEvent("activate", this);
37914         // not sure if this should happen before or after..
37915         if (!this.layout) {
37916             return; // should not happen..
37917         }
37918         var reg = false;
37919         for (var r in this.layout.regions) {
37920             reg = this.layout.getRegion(r);
37921             if (reg.getActivePanel()) {
37922                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37923                 reg.setActivePanel(reg.getActivePanel());
37924                 continue;
37925             }
37926             if (!reg.panels.length) {
37927                 continue;
37928             }
37929             reg.showPanel(reg.getPanel(0));
37930         }
37931         
37932         
37933         
37934         
37935     },
37936     
37937     /**
37938      * Returns the nested BorderLayout for this panel
37939      * @return {Roo.BorderLayout} 
37940      */
37941     getLayout : function(){
37942         return this.layout;
37943     },
37944     
37945      /**
37946      * Adds a xtype elements to the layout of the nested panel
37947      * <pre><code>
37948
37949 panel.addxtype({
37950        xtype : 'ContentPanel',
37951        region: 'west',
37952        items: [ .... ]
37953    }
37954 );
37955
37956 panel.addxtype({
37957         xtype : 'NestedLayoutPanel',
37958         region: 'west',
37959         layout: {
37960            center: { },
37961            west: { }   
37962         },
37963         items : [ ... list of content panels or nested layout panels.. ]
37964    }
37965 );
37966 </code></pre>
37967      * @param {Object} cfg Xtype definition of item to add.
37968      */
37969     addxtype : function(cfg) {
37970         return this.layout.addxtype(cfg);
37971     
37972     }
37973 });        /*
37974  * Based on:
37975  * Ext JS Library 1.1.1
37976  * Copyright(c) 2006-2007, Ext JS, LLC.
37977  *
37978  * Originally Released Under LGPL - original licence link has changed is not relivant.
37979  *
37980  * Fork - LGPL
37981  * <script type="text/javascript">
37982  */
37983 /**
37984  * @class Roo.TabPanel
37985  * @extends Roo.util.Observable
37986  * A lightweight tab container.
37987  * <br><br>
37988  * Usage:
37989  * <pre><code>
37990 // basic tabs 1, built from existing content
37991 var tabs = new Roo.TabPanel("tabs1");
37992 tabs.addTab("script", "View Script");
37993 tabs.addTab("markup", "View Markup");
37994 tabs.activate("script");
37995
37996 // more advanced tabs, built from javascript
37997 var jtabs = new Roo.TabPanel("jtabs");
37998 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37999
38000 // set up the UpdateManager
38001 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38002 var updater = tab2.getUpdateManager();
38003 updater.setDefaultUrl("ajax1.htm");
38004 tab2.on('activate', updater.refresh, updater, true);
38005
38006 // Use setUrl for Ajax loading
38007 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38008 tab3.setUrl("ajax2.htm", null, true);
38009
38010 // Disabled tab
38011 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38012 tab4.disable();
38013
38014 jtabs.activate("jtabs-1");
38015  * </code></pre>
38016  * @constructor
38017  * Create a new TabPanel.
38018  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38019  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38020  */
38021 Roo.bootstrap.panel.Tabs = function(config){
38022     /**
38023     * The container element for this TabPanel.
38024     * @type Roo.Element
38025     */
38026     this.el = Roo.get(config.el);
38027     delete config.el;
38028     if(config){
38029         if(typeof config == "boolean"){
38030             this.tabPosition = config ? "bottom" : "top";
38031         }else{
38032             Roo.apply(this, config);
38033         }
38034     }
38035     
38036     if(this.tabPosition == "bottom"){
38037         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38038         this.el.addClass("roo-tabs-bottom");
38039     }
38040     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38041     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38042     this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38043     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38044     if(Roo.isIE){
38045         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38046     }
38047     if(this.tabPosition != "bottom"){
38048         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38049          * @type Roo.Element
38050          */
38051         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38052         this.el.addClass("roo-tabs-top");
38053     }
38054     this.items = [];
38055
38056     this.bodyEl.setStyle("position", "relative");
38057
38058     this.active = null;
38059     this.activateDelegate = this.activate.createDelegate(this);
38060
38061     this.addEvents({
38062         /**
38063          * @event tabchange
38064          * Fires when the active tab changes
38065          * @param {Roo.TabPanel} this
38066          * @param {Roo.TabPanelItem} activePanel The new active tab
38067          */
38068         "tabchange": true,
38069         /**
38070          * @event beforetabchange
38071          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38072          * @param {Roo.TabPanel} this
38073          * @param {Object} e Set cancel to true on this object to cancel the tab change
38074          * @param {Roo.TabPanelItem} tab The tab being changed to
38075          */
38076         "beforetabchange" : true
38077     });
38078
38079     Roo.EventManager.onWindowResize(this.onResize, this);
38080     this.cpad = this.el.getPadding("lr");
38081     this.hiddenCount = 0;
38082
38083
38084     // toolbar on the tabbar support...
38085     if (this.toolbar) {
38086         alert("no toolbar support yet");
38087         this.toolbar  = false;
38088         /*
38089         var tcfg = this.toolbar;
38090         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38091         this.toolbar = new Roo.Toolbar(tcfg);
38092         if (Roo.isSafari) {
38093             var tbl = tcfg.container.child('table', true);
38094             tbl.setAttribute('width', '100%');
38095         }
38096         */
38097         
38098     }
38099    
38100
38101
38102     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38103 };
38104
38105 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38106     /*
38107      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38108      */
38109     tabPosition : "top",
38110     /*
38111      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38112      */
38113     currentTabWidth : 0,
38114     /*
38115      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38116      */
38117     minTabWidth : 40,
38118     /*
38119      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38120      */
38121     maxTabWidth : 250,
38122     /*
38123      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38124      */
38125     preferredTabWidth : 175,
38126     /*
38127      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38128      */
38129     resizeTabs : false,
38130     /*
38131      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38132      */
38133     monitorResize : true,
38134     /*
38135      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38136      */
38137     toolbar : false,
38138
38139     /**
38140      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38141      * @param {String} id The id of the div to use <b>or create</b>
38142      * @param {String} text The text for the tab
38143      * @param {String} content (optional) Content to put in the TabPanelItem body
38144      * @param {Boolean} closable (optional) True to create a close icon on the tab
38145      * @return {Roo.TabPanelItem} The created TabPanelItem
38146      */
38147     addTab : function(id, text, content, closable, tpl)
38148     {
38149         var item = new Roo.bootstrap.panel.TabItem({
38150             panel: this,
38151             id : id,
38152             text : text,
38153             closable : closable,
38154             tpl : tpl
38155         });
38156         this.addTabItem(item);
38157         if(content){
38158             item.setContent(content);
38159         }
38160         return item;
38161     },
38162
38163     /**
38164      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38165      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38166      * @return {Roo.TabPanelItem}
38167      */
38168     getTab : function(id){
38169         return this.items[id];
38170     },
38171
38172     /**
38173      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38174      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38175      */
38176     hideTab : function(id){
38177         var t = this.items[id];
38178         if(!t.isHidden()){
38179            t.setHidden(true);
38180            this.hiddenCount++;
38181            this.autoSizeTabs();
38182         }
38183     },
38184
38185     /**
38186      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38187      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38188      */
38189     unhideTab : function(id){
38190         var t = this.items[id];
38191         if(t.isHidden()){
38192            t.setHidden(false);
38193            this.hiddenCount--;
38194            this.autoSizeTabs();
38195         }
38196     },
38197
38198     /**
38199      * Adds an existing {@link Roo.TabPanelItem}.
38200      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38201      */
38202     addTabItem : function(item)
38203     {
38204         this.items[item.id] = item;
38205         this.items.push(item);
38206         this.autoSizeTabs();
38207       //  if(this.resizeTabs){
38208     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38209   //         this.autoSizeTabs();
38210 //        }else{
38211 //            item.autoSize();
38212        // }
38213     },
38214
38215     /**
38216      * Removes a {@link Roo.TabPanelItem}.
38217      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38218      */
38219     removeTab : function(id){
38220         var items = this.items;
38221         var tab = items[id];
38222         if(!tab) { return; }
38223         var index = items.indexOf(tab);
38224         if(this.active == tab && items.length > 1){
38225             var newTab = this.getNextAvailable(index);
38226             if(newTab) {
38227                 newTab.activate();
38228             }
38229         }
38230         this.stripEl.dom.removeChild(tab.pnode.dom);
38231         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38232             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38233         }
38234         items.splice(index, 1);
38235         delete this.items[tab.id];
38236         tab.fireEvent("close", tab);
38237         tab.purgeListeners();
38238         this.autoSizeTabs();
38239     },
38240
38241     getNextAvailable : function(start){
38242         var items = this.items;
38243         var index = start;
38244         // look for a next tab that will slide over to
38245         // replace the one being removed
38246         while(index < items.length){
38247             var item = items[++index];
38248             if(item && !item.isHidden()){
38249                 return item;
38250             }
38251         }
38252         // if one isn't found select the previous tab (on the left)
38253         index = start;
38254         while(index >= 0){
38255             var item = items[--index];
38256             if(item && !item.isHidden()){
38257                 return item;
38258             }
38259         }
38260         return null;
38261     },
38262
38263     /**
38264      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38265      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38266      */
38267     disableTab : function(id){
38268         var tab = this.items[id];
38269         if(tab && this.active != tab){
38270             tab.disable();
38271         }
38272     },
38273
38274     /**
38275      * Enables a {@link Roo.TabPanelItem} that is disabled.
38276      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38277      */
38278     enableTab : function(id){
38279         var tab = this.items[id];
38280         tab.enable();
38281     },
38282
38283     /**
38284      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38285      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38286      * @return {Roo.TabPanelItem} The TabPanelItem.
38287      */
38288     activate : function(id)
38289     {
38290         var tab = this.items[id];
38291         if(!tab){
38292             return null;
38293         }
38294         if(tab == this.active || tab.disabled){
38295             return tab;
38296         }
38297         var e = {};
38298         this.fireEvent("beforetabchange", this, e, tab);
38299         if(e.cancel !== true && !tab.disabled){
38300             if(this.active){
38301                 this.active.hide();
38302             }
38303             this.active = this.items[id];
38304             this.active.show();
38305             this.fireEvent("tabchange", this, this.active);
38306         }
38307         return tab;
38308     },
38309
38310     /**
38311      * Gets the active {@link Roo.TabPanelItem}.
38312      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38313      */
38314     getActiveTab : function(){
38315         return this.active;
38316     },
38317
38318     /**
38319      * Updates the tab body element to fit the height of the container element
38320      * for overflow scrolling
38321      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38322      */
38323     syncHeight : function(targetHeight){
38324         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38325         var bm = this.bodyEl.getMargins();
38326         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38327         this.bodyEl.setHeight(newHeight);
38328         return newHeight;
38329     },
38330
38331     onResize : function(){
38332         if(this.monitorResize){
38333             this.autoSizeTabs();
38334         }
38335     },
38336
38337     /**
38338      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38339      */
38340     beginUpdate : function(){
38341         this.updating = true;
38342     },
38343
38344     /**
38345      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38346      */
38347     endUpdate : function(){
38348         this.updating = false;
38349         this.autoSizeTabs();
38350     },
38351
38352     /**
38353      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38354      */
38355     autoSizeTabs : function()
38356     {
38357         var count = this.items.length;
38358         var vcount = count - this.hiddenCount;
38359         
38360         if (vcount < 2) {
38361             this.stripEl.hide();
38362         } else {
38363             this.stripEl.show();
38364         }
38365         
38366         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38367             return;
38368         }
38369         
38370         
38371         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38372         var availWidth = Math.floor(w / vcount);
38373         var b = this.stripBody;
38374         if(b.getWidth() > w){
38375             var tabs = this.items;
38376             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38377             if(availWidth < this.minTabWidth){
38378                 /*if(!this.sleft){    // incomplete scrolling code
38379                     this.createScrollButtons();
38380                 }
38381                 this.showScroll();
38382                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38383             }
38384         }else{
38385             if(this.currentTabWidth < this.preferredTabWidth){
38386                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38387             }
38388         }
38389     },
38390
38391     /**
38392      * Returns the number of tabs in this TabPanel.
38393      * @return {Number}
38394      */
38395      getCount : function(){
38396          return this.items.length;
38397      },
38398
38399     /**
38400      * Resizes all the tabs to the passed width
38401      * @param {Number} The new width
38402      */
38403     setTabWidth : function(width){
38404         this.currentTabWidth = width;
38405         for(var i = 0, len = this.items.length; i < len; i++) {
38406                 if(!this.items[i].isHidden()) {
38407                 this.items[i].setWidth(width);
38408             }
38409         }
38410     },
38411
38412     /**
38413      * Destroys this TabPanel
38414      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38415      */
38416     destroy : function(removeEl){
38417         Roo.EventManager.removeResizeListener(this.onResize, this);
38418         for(var i = 0, len = this.items.length; i < len; i++){
38419             this.items[i].purgeListeners();
38420         }
38421         if(removeEl === true){
38422             this.el.update("");
38423             this.el.remove();
38424         }
38425     },
38426     
38427     createStrip : function(container)
38428     {
38429         var strip = document.createElement("nav");
38430         strip.className = Roo.bootstrap.version == 4 ?
38431             "navbar-light bg-light" : 
38432             "navbar navbar-default"; //"x-tabs-wrap";
38433         container.appendChild(strip);
38434         return strip;
38435     },
38436     
38437     createStripList : function(strip)
38438     {
38439         // div wrapper for retard IE
38440         // returns the "tr" element.
38441         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38442         //'<div class="x-tabs-strip-wrap">'+
38443           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38444           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38445         return strip.firstChild; //.firstChild.firstChild.firstChild;
38446     },
38447     createBody : function(container)
38448     {
38449         var body = document.createElement("div");
38450         Roo.id(body, "tab-body");
38451         //Roo.fly(body).addClass("x-tabs-body");
38452         Roo.fly(body).addClass("tab-content");
38453         container.appendChild(body);
38454         return body;
38455     },
38456     createItemBody :function(bodyEl, id){
38457         var body = Roo.getDom(id);
38458         if(!body){
38459             body = document.createElement("div");
38460             body.id = id;
38461         }
38462         //Roo.fly(body).addClass("x-tabs-item-body");
38463         Roo.fly(body).addClass("tab-pane");
38464          bodyEl.insertBefore(body, bodyEl.firstChild);
38465         return body;
38466     },
38467     /** @private */
38468     createStripElements :  function(stripEl, text, closable, tpl)
38469     {
38470         var td = document.createElement("li"); // was td..
38471         td.className = 'nav-item';
38472         
38473         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38474         
38475         
38476         stripEl.appendChild(td);
38477         /*if(closable){
38478             td.className = "x-tabs-closable";
38479             if(!this.closeTpl){
38480                 this.closeTpl = new Roo.Template(
38481                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38482                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38483                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38484                 );
38485             }
38486             var el = this.closeTpl.overwrite(td, {"text": text});
38487             var close = el.getElementsByTagName("div")[0];
38488             var inner = el.getElementsByTagName("em")[0];
38489             return {"el": el, "close": close, "inner": inner};
38490         } else {
38491         */
38492         // not sure what this is..
38493 //            if(!this.tabTpl){
38494                 //this.tabTpl = new Roo.Template(
38495                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38496                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38497                 //);
38498 //                this.tabTpl = new Roo.Template(
38499 //                   '<a href="#">' +
38500 //                   '<span unselectable="on"' +
38501 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38502 //                            ' >{text}</span></a>'
38503 //                );
38504 //                
38505 //            }
38506
38507
38508             var template = tpl || this.tabTpl || false;
38509             
38510             if(!template){
38511                 template =  new Roo.Template(
38512                         Roo.bootstrap.version == 4 ? 
38513                             (
38514                                 '<a class="nav-link" href="#" unselectable="on"' +
38515                                      (this.disableTooltips ? '' : ' title="{text}"') +
38516                                      ' >{text}</a>'
38517                             ) : (
38518                                 '<a class="nav-link" href="#">' +
38519                                 '<span unselectable="on"' +
38520                                          (this.disableTooltips ? '' : ' title="{text}"') +
38521                                     ' >{text}</span></a>'
38522                             )
38523                 );
38524             }
38525             
38526             switch (typeof(template)) {
38527                 case 'object' :
38528                     break;
38529                 case 'string' :
38530                     template = new Roo.Template(template);
38531                     break;
38532                 default :
38533                     break;
38534             }
38535             
38536             var el = template.overwrite(td, {"text": text});
38537             
38538             var inner = el.getElementsByTagName("span")[0];
38539             
38540             return {"el": el, "inner": inner};
38541             
38542     }
38543         
38544     
38545 });
38546
38547 /**
38548  * @class Roo.TabPanelItem
38549  * @extends Roo.util.Observable
38550  * Represents an individual item (tab plus body) in a TabPanel.
38551  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38552  * @param {String} id The id of this TabPanelItem
38553  * @param {String} text The text for the tab of this TabPanelItem
38554  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38555  */
38556 Roo.bootstrap.panel.TabItem = function(config){
38557     /**
38558      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38559      * @type Roo.TabPanel
38560      */
38561     this.tabPanel = config.panel;
38562     /**
38563      * The id for this TabPanelItem
38564      * @type String
38565      */
38566     this.id = config.id;
38567     /** @private */
38568     this.disabled = false;
38569     /** @private */
38570     this.text = config.text;
38571     /** @private */
38572     this.loaded = false;
38573     this.closable = config.closable;
38574
38575     /**
38576      * The body element for this TabPanelItem.
38577      * @type Roo.Element
38578      */
38579     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38580     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38581     this.bodyEl.setStyle("display", "block");
38582     this.bodyEl.setStyle("zoom", "1");
38583     //this.hideAction();
38584
38585     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38586     /** @private */
38587     this.el = Roo.get(els.el);
38588     this.inner = Roo.get(els.inner, true);
38589      this.textEl = Roo.bootstrap.version == 4 ?
38590         this.el : Roo.get(this.el.dom.firstChild, true);
38591
38592     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38593     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38594
38595     
38596 //    this.el.on("mousedown", this.onTabMouseDown, this);
38597     this.el.on("click", this.onTabClick, this);
38598     /** @private */
38599     if(config.closable){
38600         var c = Roo.get(els.close, true);
38601         c.dom.title = this.closeText;
38602         c.addClassOnOver("close-over");
38603         c.on("click", this.closeClick, this);
38604      }
38605
38606     this.addEvents({
38607          /**
38608          * @event activate
38609          * Fires when this tab becomes the active tab.
38610          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38611          * @param {Roo.TabPanelItem} this
38612          */
38613         "activate": true,
38614         /**
38615          * @event beforeclose
38616          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38617          * @param {Roo.TabPanelItem} this
38618          * @param {Object} e Set cancel to true on this object to cancel the close.
38619          */
38620         "beforeclose": true,
38621         /**
38622          * @event close
38623          * Fires when this tab is closed.
38624          * @param {Roo.TabPanelItem} this
38625          */
38626          "close": true,
38627         /**
38628          * @event deactivate
38629          * Fires when this tab is no longer the active tab.
38630          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38631          * @param {Roo.TabPanelItem} this
38632          */
38633          "deactivate" : true
38634     });
38635     this.hidden = false;
38636
38637     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38638 };
38639
38640 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38641            {
38642     purgeListeners : function(){
38643        Roo.util.Observable.prototype.purgeListeners.call(this);
38644        this.el.removeAllListeners();
38645     },
38646     /**
38647      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38648      */
38649     show : function(){
38650         this.status_node.addClass("active");
38651         this.showAction();
38652         if(Roo.isOpera){
38653             this.tabPanel.stripWrap.repaint();
38654         }
38655         this.fireEvent("activate", this.tabPanel, this);
38656     },
38657
38658     /**
38659      * Returns true if this tab is the active tab.
38660      * @return {Boolean}
38661      */
38662     isActive : function(){
38663         return this.tabPanel.getActiveTab() == this;
38664     },
38665
38666     /**
38667      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38668      */
38669     hide : function(){
38670         this.status_node.removeClass("active");
38671         this.hideAction();
38672         this.fireEvent("deactivate", this.tabPanel, this);
38673     },
38674
38675     hideAction : function(){
38676         this.bodyEl.hide();
38677         this.bodyEl.setStyle("position", "absolute");
38678         this.bodyEl.setLeft("-20000px");
38679         this.bodyEl.setTop("-20000px");
38680     },
38681
38682     showAction : function(){
38683         this.bodyEl.setStyle("position", "relative");
38684         this.bodyEl.setTop("");
38685         this.bodyEl.setLeft("");
38686         this.bodyEl.show();
38687     },
38688
38689     /**
38690      * Set the tooltip for the tab.
38691      * @param {String} tooltip The tab's tooltip
38692      */
38693     setTooltip : function(text){
38694         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38695             this.textEl.dom.qtip = text;
38696             this.textEl.dom.removeAttribute('title');
38697         }else{
38698             this.textEl.dom.title = text;
38699         }
38700     },
38701
38702     onTabClick : function(e){
38703         e.preventDefault();
38704         this.tabPanel.activate(this.id);
38705     },
38706
38707     onTabMouseDown : function(e){
38708         e.preventDefault();
38709         this.tabPanel.activate(this.id);
38710     },
38711 /*
38712     getWidth : function(){
38713         return this.inner.getWidth();
38714     },
38715
38716     setWidth : function(width){
38717         var iwidth = width - this.linode.getPadding("lr");
38718         this.inner.setWidth(iwidth);
38719         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38720         this.linode.setWidth(width);
38721     },
38722 */
38723     /**
38724      * Show or hide the tab
38725      * @param {Boolean} hidden True to hide or false to show.
38726      */
38727     setHidden : function(hidden){
38728         this.hidden = hidden;
38729         this.linode.setStyle("display", hidden ? "none" : "");
38730     },
38731
38732     /**
38733      * Returns true if this tab is "hidden"
38734      * @return {Boolean}
38735      */
38736     isHidden : function(){
38737         return this.hidden;
38738     },
38739
38740     /**
38741      * Returns the text for this tab
38742      * @return {String}
38743      */
38744     getText : function(){
38745         return this.text;
38746     },
38747     /*
38748     autoSize : function(){
38749         //this.el.beginMeasure();
38750         this.textEl.setWidth(1);
38751         /*
38752          *  #2804 [new] Tabs in Roojs
38753          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38754          */
38755         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38756         //this.el.endMeasure();
38757     //},
38758
38759     /**
38760      * Sets the text for the tab (Note: this also sets the tooltip text)
38761      * @param {String} text The tab's text and tooltip
38762      */
38763     setText : function(text){
38764         this.text = text;
38765         this.textEl.update(text);
38766         this.setTooltip(text);
38767         //if(!this.tabPanel.resizeTabs){
38768         //    this.autoSize();
38769         //}
38770     },
38771     /**
38772      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38773      */
38774     activate : function(){
38775         this.tabPanel.activate(this.id);
38776     },
38777
38778     /**
38779      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38780      */
38781     disable : function(){
38782         if(this.tabPanel.active != this){
38783             this.disabled = true;
38784             this.status_node.addClass("disabled");
38785         }
38786     },
38787
38788     /**
38789      * Enables this TabPanelItem if it was previously disabled.
38790      */
38791     enable : function(){
38792         this.disabled = false;
38793         this.status_node.removeClass("disabled");
38794     },
38795
38796     /**
38797      * Sets the content for this TabPanelItem.
38798      * @param {String} content The content
38799      * @param {Boolean} loadScripts true to look for and load scripts
38800      */
38801     setContent : function(content, loadScripts){
38802         this.bodyEl.update(content, loadScripts);
38803     },
38804
38805     /**
38806      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38807      * @return {Roo.UpdateManager} The UpdateManager
38808      */
38809     getUpdateManager : function(){
38810         return this.bodyEl.getUpdateManager();
38811     },
38812
38813     /**
38814      * Set a URL to be used to load the content for this TabPanelItem.
38815      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38816      * @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)
38817      * @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)
38818      * @return {Roo.UpdateManager} The UpdateManager
38819      */
38820     setUrl : function(url, params, loadOnce){
38821         if(this.refreshDelegate){
38822             this.un('activate', this.refreshDelegate);
38823         }
38824         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38825         this.on("activate", this.refreshDelegate);
38826         return this.bodyEl.getUpdateManager();
38827     },
38828
38829     /** @private */
38830     _handleRefresh : function(url, params, loadOnce){
38831         if(!loadOnce || !this.loaded){
38832             var updater = this.bodyEl.getUpdateManager();
38833             updater.update(url, params, this._setLoaded.createDelegate(this));
38834         }
38835     },
38836
38837     /**
38838      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38839      *   Will fail silently if the setUrl method has not been called.
38840      *   This does not activate the panel, just updates its content.
38841      */
38842     refresh : function(){
38843         if(this.refreshDelegate){
38844            this.loaded = false;
38845            this.refreshDelegate();
38846         }
38847     },
38848
38849     /** @private */
38850     _setLoaded : function(){
38851         this.loaded = true;
38852     },
38853
38854     /** @private */
38855     closeClick : function(e){
38856         var o = {};
38857         e.stopEvent();
38858         this.fireEvent("beforeclose", this, o);
38859         if(o.cancel !== true){
38860             this.tabPanel.removeTab(this.id);
38861         }
38862     },
38863     /**
38864      * The text displayed in the tooltip for the close icon.
38865      * @type String
38866      */
38867     closeText : "Close this tab"
38868 });
38869 /**
38870 *    This script refer to:
38871 *    Title: International Telephone Input
38872 *    Author: Jack O'Connor
38873 *    Code version:  v12.1.12
38874 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38875 **/
38876
38877 Roo.bootstrap.PhoneInputData = function() {
38878     var d = [
38879       [
38880         "Afghanistan (‫افغانستان‬‎)",
38881         "af",
38882         "93"
38883       ],
38884       [
38885         "Albania (Shqipëri)",
38886         "al",
38887         "355"
38888       ],
38889       [
38890         "Algeria (‫الجزائر‬‎)",
38891         "dz",
38892         "213"
38893       ],
38894       [
38895         "American Samoa",
38896         "as",
38897         "1684"
38898       ],
38899       [
38900         "Andorra",
38901         "ad",
38902         "376"
38903       ],
38904       [
38905         "Angola",
38906         "ao",
38907         "244"
38908       ],
38909       [
38910         "Anguilla",
38911         "ai",
38912         "1264"
38913       ],
38914       [
38915         "Antigua and Barbuda",
38916         "ag",
38917         "1268"
38918       ],
38919       [
38920         "Argentina",
38921         "ar",
38922         "54"
38923       ],
38924       [
38925         "Armenia (Հայաստան)",
38926         "am",
38927         "374"
38928       ],
38929       [
38930         "Aruba",
38931         "aw",
38932         "297"
38933       ],
38934       [
38935         "Australia",
38936         "au",
38937         "61",
38938         0
38939       ],
38940       [
38941         "Austria (Österreich)",
38942         "at",
38943         "43"
38944       ],
38945       [
38946         "Azerbaijan (Azərbaycan)",
38947         "az",
38948         "994"
38949       ],
38950       [
38951         "Bahamas",
38952         "bs",
38953         "1242"
38954       ],
38955       [
38956         "Bahrain (‫البحرين‬‎)",
38957         "bh",
38958         "973"
38959       ],
38960       [
38961         "Bangladesh (বাংলাদেশ)",
38962         "bd",
38963         "880"
38964       ],
38965       [
38966         "Barbados",
38967         "bb",
38968         "1246"
38969       ],
38970       [
38971         "Belarus (Беларусь)",
38972         "by",
38973         "375"
38974       ],
38975       [
38976         "Belgium (België)",
38977         "be",
38978         "32"
38979       ],
38980       [
38981         "Belize",
38982         "bz",
38983         "501"
38984       ],
38985       [
38986         "Benin (Bénin)",
38987         "bj",
38988         "229"
38989       ],
38990       [
38991         "Bermuda",
38992         "bm",
38993         "1441"
38994       ],
38995       [
38996         "Bhutan (འབྲུག)",
38997         "bt",
38998         "975"
38999       ],
39000       [
39001         "Bolivia",
39002         "bo",
39003         "591"
39004       ],
39005       [
39006         "Bosnia and Herzegovina (Босна и Херцеговина)",
39007         "ba",
39008         "387"
39009       ],
39010       [
39011         "Botswana",
39012         "bw",
39013         "267"
39014       ],
39015       [
39016         "Brazil (Brasil)",
39017         "br",
39018         "55"
39019       ],
39020       [
39021         "British Indian Ocean Territory",
39022         "io",
39023         "246"
39024       ],
39025       [
39026         "British Virgin Islands",
39027         "vg",
39028         "1284"
39029       ],
39030       [
39031         "Brunei",
39032         "bn",
39033         "673"
39034       ],
39035       [
39036         "Bulgaria (България)",
39037         "bg",
39038         "359"
39039       ],
39040       [
39041         "Burkina Faso",
39042         "bf",
39043         "226"
39044       ],
39045       [
39046         "Burundi (Uburundi)",
39047         "bi",
39048         "257"
39049       ],
39050       [
39051         "Cambodia (កម្ពុជា)",
39052         "kh",
39053         "855"
39054       ],
39055       [
39056         "Cameroon (Cameroun)",
39057         "cm",
39058         "237"
39059       ],
39060       [
39061         "Canada",
39062         "ca",
39063         "1",
39064         1,
39065         ["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"]
39066       ],
39067       [
39068         "Cape Verde (Kabu Verdi)",
39069         "cv",
39070         "238"
39071       ],
39072       [
39073         "Caribbean Netherlands",
39074         "bq",
39075         "599",
39076         1
39077       ],
39078       [
39079         "Cayman Islands",
39080         "ky",
39081         "1345"
39082       ],
39083       [
39084         "Central African Republic (République centrafricaine)",
39085         "cf",
39086         "236"
39087       ],
39088       [
39089         "Chad (Tchad)",
39090         "td",
39091         "235"
39092       ],
39093       [
39094         "Chile",
39095         "cl",
39096         "56"
39097       ],
39098       [
39099         "China (中国)",
39100         "cn",
39101         "86"
39102       ],
39103       [
39104         "Christmas Island",
39105         "cx",
39106         "61",
39107         2
39108       ],
39109       [
39110         "Cocos (Keeling) Islands",
39111         "cc",
39112         "61",
39113         1
39114       ],
39115       [
39116         "Colombia",
39117         "co",
39118         "57"
39119       ],
39120       [
39121         "Comoros (‫جزر القمر‬‎)",
39122         "km",
39123         "269"
39124       ],
39125       [
39126         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39127         "cd",
39128         "243"
39129       ],
39130       [
39131         "Congo (Republic) (Congo-Brazzaville)",
39132         "cg",
39133         "242"
39134       ],
39135       [
39136         "Cook Islands",
39137         "ck",
39138         "682"
39139       ],
39140       [
39141         "Costa Rica",
39142         "cr",
39143         "506"
39144       ],
39145       [
39146         "Côte d’Ivoire",
39147         "ci",
39148         "225"
39149       ],
39150       [
39151         "Croatia (Hrvatska)",
39152         "hr",
39153         "385"
39154       ],
39155       [
39156         "Cuba",
39157         "cu",
39158         "53"
39159       ],
39160       [
39161         "Curaçao",
39162         "cw",
39163         "599",
39164         0
39165       ],
39166       [
39167         "Cyprus (Κύπρος)",
39168         "cy",
39169         "357"
39170       ],
39171       [
39172         "Czech Republic (Česká republika)",
39173         "cz",
39174         "420"
39175       ],
39176       [
39177         "Denmark (Danmark)",
39178         "dk",
39179         "45"
39180       ],
39181       [
39182         "Djibouti",
39183         "dj",
39184         "253"
39185       ],
39186       [
39187         "Dominica",
39188         "dm",
39189         "1767"
39190       ],
39191       [
39192         "Dominican Republic (República Dominicana)",
39193         "do",
39194         "1",
39195         2,
39196         ["809", "829", "849"]
39197       ],
39198       [
39199         "Ecuador",
39200         "ec",
39201         "593"
39202       ],
39203       [
39204         "Egypt (‫مصر‬‎)",
39205         "eg",
39206         "20"
39207       ],
39208       [
39209         "El Salvador",
39210         "sv",
39211         "503"
39212       ],
39213       [
39214         "Equatorial Guinea (Guinea Ecuatorial)",
39215         "gq",
39216         "240"
39217       ],
39218       [
39219         "Eritrea",
39220         "er",
39221         "291"
39222       ],
39223       [
39224         "Estonia (Eesti)",
39225         "ee",
39226         "372"
39227       ],
39228       [
39229         "Ethiopia",
39230         "et",
39231         "251"
39232       ],
39233       [
39234         "Falkland Islands (Islas Malvinas)",
39235         "fk",
39236         "500"
39237       ],
39238       [
39239         "Faroe Islands (Føroyar)",
39240         "fo",
39241         "298"
39242       ],
39243       [
39244         "Fiji",
39245         "fj",
39246         "679"
39247       ],
39248       [
39249         "Finland (Suomi)",
39250         "fi",
39251         "358",
39252         0
39253       ],
39254       [
39255         "France",
39256         "fr",
39257         "33"
39258       ],
39259       [
39260         "French Guiana (Guyane française)",
39261         "gf",
39262         "594"
39263       ],
39264       [
39265         "French Polynesia (Polynésie française)",
39266         "pf",
39267         "689"
39268       ],
39269       [
39270         "Gabon",
39271         "ga",
39272         "241"
39273       ],
39274       [
39275         "Gambia",
39276         "gm",
39277         "220"
39278       ],
39279       [
39280         "Georgia (საქართველო)",
39281         "ge",
39282         "995"
39283       ],
39284       [
39285         "Germany (Deutschland)",
39286         "de",
39287         "49"
39288       ],
39289       [
39290         "Ghana (Gaana)",
39291         "gh",
39292         "233"
39293       ],
39294       [
39295         "Gibraltar",
39296         "gi",
39297         "350"
39298       ],
39299       [
39300         "Greece (Ελλάδα)",
39301         "gr",
39302         "30"
39303       ],
39304       [
39305         "Greenland (Kalaallit Nunaat)",
39306         "gl",
39307         "299"
39308       ],
39309       [
39310         "Grenada",
39311         "gd",
39312         "1473"
39313       ],
39314       [
39315         "Guadeloupe",
39316         "gp",
39317         "590",
39318         0
39319       ],
39320       [
39321         "Guam",
39322         "gu",
39323         "1671"
39324       ],
39325       [
39326         "Guatemala",
39327         "gt",
39328         "502"
39329       ],
39330       [
39331         "Guernsey",
39332         "gg",
39333         "44",
39334         1
39335       ],
39336       [
39337         "Guinea (Guinée)",
39338         "gn",
39339         "224"
39340       ],
39341       [
39342         "Guinea-Bissau (Guiné Bissau)",
39343         "gw",
39344         "245"
39345       ],
39346       [
39347         "Guyana",
39348         "gy",
39349         "592"
39350       ],
39351       [
39352         "Haiti",
39353         "ht",
39354         "509"
39355       ],
39356       [
39357         "Honduras",
39358         "hn",
39359         "504"
39360       ],
39361       [
39362         "Hong Kong (香港)",
39363         "hk",
39364         "852"
39365       ],
39366       [
39367         "Hungary (Magyarország)",
39368         "hu",
39369         "36"
39370       ],
39371       [
39372         "Iceland (Ísland)",
39373         "is",
39374         "354"
39375       ],
39376       [
39377         "India (भारत)",
39378         "in",
39379         "91"
39380       ],
39381       [
39382         "Indonesia",
39383         "id",
39384         "62"
39385       ],
39386       [
39387         "Iran (‫ایران‬‎)",
39388         "ir",
39389         "98"
39390       ],
39391       [
39392         "Iraq (‫العراق‬‎)",
39393         "iq",
39394         "964"
39395       ],
39396       [
39397         "Ireland",
39398         "ie",
39399         "353"
39400       ],
39401       [
39402         "Isle of Man",
39403         "im",
39404         "44",
39405         2
39406       ],
39407       [
39408         "Israel (‫ישראל‬‎)",
39409         "il",
39410         "972"
39411       ],
39412       [
39413         "Italy (Italia)",
39414         "it",
39415         "39",
39416         0
39417       ],
39418       [
39419         "Jamaica",
39420         "jm",
39421         "1876"
39422       ],
39423       [
39424         "Japan (日本)",
39425         "jp",
39426         "81"
39427       ],
39428       [
39429         "Jersey",
39430         "je",
39431         "44",
39432         3
39433       ],
39434       [
39435         "Jordan (‫الأردن‬‎)",
39436         "jo",
39437         "962"
39438       ],
39439       [
39440         "Kazakhstan (Казахстан)",
39441         "kz",
39442         "7",
39443         1
39444       ],
39445       [
39446         "Kenya",
39447         "ke",
39448         "254"
39449       ],
39450       [
39451         "Kiribati",
39452         "ki",
39453         "686"
39454       ],
39455       [
39456         "Kosovo",
39457         "xk",
39458         "383"
39459       ],
39460       [
39461         "Kuwait (‫الكويت‬‎)",
39462         "kw",
39463         "965"
39464       ],
39465       [
39466         "Kyrgyzstan (Кыргызстан)",
39467         "kg",
39468         "996"
39469       ],
39470       [
39471         "Laos (ລາວ)",
39472         "la",
39473         "856"
39474       ],
39475       [
39476         "Latvia (Latvija)",
39477         "lv",
39478         "371"
39479       ],
39480       [
39481         "Lebanon (‫لبنان‬‎)",
39482         "lb",
39483         "961"
39484       ],
39485       [
39486         "Lesotho",
39487         "ls",
39488         "266"
39489       ],
39490       [
39491         "Liberia",
39492         "lr",
39493         "231"
39494       ],
39495       [
39496         "Libya (‫ليبيا‬‎)",
39497         "ly",
39498         "218"
39499       ],
39500       [
39501         "Liechtenstein",
39502         "li",
39503         "423"
39504       ],
39505       [
39506         "Lithuania (Lietuva)",
39507         "lt",
39508         "370"
39509       ],
39510       [
39511         "Luxembourg",
39512         "lu",
39513         "352"
39514       ],
39515       [
39516         "Macau (澳門)",
39517         "mo",
39518         "853"
39519       ],
39520       [
39521         "Macedonia (FYROM) (Македонија)",
39522         "mk",
39523         "389"
39524       ],
39525       [
39526         "Madagascar (Madagasikara)",
39527         "mg",
39528         "261"
39529       ],
39530       [
39531         "Malawi",
39532         "mw",
39533         "265"
39534       ],
39535       [
39536         "Malaysia",
39537         "my",
39538         "60"
39539       ],
39540       [
39541         "Maldives",
39542         "mv",
39543         "960"
39544       ],
39545       [
39546         "Mali",
39547         "ml",
39548         "223"
39549       ],
39550       [
39551         "Malta",
39552         "mt",
39553         "356"
39554       ],
39555       [
39556         "Marshall Islands",
39557         "mh",
39558         "692"
39559       ],
39560       [
39561         "Martinique",
39562         "mq",
39563         "596"
39564       ],
39565       [
39566         "Mauritania (‫موريتانيا‬‎)",
39567         "mr",
39568         "222"
39569       ],
39570       [
39571         "Mauritius (Moris)",
39572         "mu",
39573         "230"
39574       ],
39575       [
39576         "Mayotte",
39577         "yt",
39578         "262",
39579         1
39580       ],
39581       [
39582         "Mexico (México)",
39583         "mx",
39584         "52"
39585       ],
39586       [
39587         "Micronesia",
39588         "fm",
39589         "691"
39590       ],
39591       [
39592         "Moldova (Republica Moldova)",
39593         "md",
39594         "373"
39595       ],
39596       [
39597         "Monaco",
39598         "mc",
39599         "377"
39600       ],
39601       [
39602         "Mongolia (Монгол)",
39603         "mn",
39604         "976"
39605       ],
39606       [
39607         "Montenegro (Crna Gora)",
39608         "me",
39609         "382"
39610       ],
39611       [
39612         "Montserrat",
39613         "ms",
39614         "1664"
39615       ],
39616       [
39617         "Morocco (‫المغرب‬‎)",
39618         "ma",
39619         "212",
39620         0
39621       ],
39622       [
39623         "Mozambique (Moçambique)",
39624         "mz",
39625         "258"
39626       ],
39627       [
39628         "Myanmar (Burma) (မြန်မာ)",
39629         "mm",
39630         "95"
39631       ],
39632       [
39633         "Namibia (Namibië)",
39634         "na",
39635         "264"
39636       ],
39637       [
39638         "Nauru",
39639         "nr",
39640         "674"
39641       ],
39642       [
39643         "Nepal (नेपाल)",
39644         "np",
39645         "977"
39646       ],
39647       [
39648         "Netherlands (Nederland)",
39649         "nl",
39650         "31"
39651       ],
39652       [
39653         "New Caledonia (Nouvelle-Calédonie)",
39654         "nc",
39655         "687"
39656       ],
39657       [
39658         "New Zealand",
39659         "nz",
39660         "64"
39661       ],
39662       [
39663         "Nicaragua",
39664         "ni",
39665         "505"
39666       ],
39667       [
39668         "Niger (Nijar)",
39669         "ne",
39670         "227"
39671       ],
39672       [
39673         "Nigeria",
39674         "ng",
39675         "234"
39676       ],
39677       [
39678         "Niue",
39679         "nu",
39680         "683"
39681       ],
39682       [
39683         "Norfolk Island",
39684         "nf",
39685         "672"
39686       ],
39687       [
39688         "North Korea (조선 민주주의 인민 공화국)",
39689         "kp",
39690         "850"
39691       ],
39692       [
39693         "Northern Mariana Islands",
39694         "mp",
39695         "1670"
39696       ],
39697       [
39698         "Norway (Norge)",
39699         "no",
39700         "47",
39701         0
39702       ],
39703       [
39704         "Oman (‫عُمان‬‎)",
39705         "om",
39706         "968"
39707       ],
39708       [
39709         "Pakistan (‫پاکستان‬‎)",
39710         "pk",
39711         "92"
39712       ],
39713       [
39714         "Palau",
39715         "pw",
39716         "680"
39717       ],
39718       [
39719         "Palestine (‫فلسطين‬‎)",
39720         "ps",
39721         "970"
39722       ],
39723       [
39724         "Panama (Panamá)",
39725         "pa",
39726         "507"
39727       ],
39728       [
39729         "Papua New Guinea",
39730         "pg",
39731         "675"
39732       ],
39733       [
39734         "Paraguay",
39735         "py",
39736         "595"
39737       ],
39738       [
39739         "Peru (Perú)",
39740         "pe",
39741         "51"
39742       ],
39743       [
39744         "Philippines",
39745         "ph",
39746         "63"
39747       ],
39748       [
39749         "Poland (Polska)",
39750         "pl",
39751         "48"
39752       ],
39753       [
39754         "Portugal",
39755         "pt",
39756         "351"
39757       ],
39758       [
39759         "Puerto Rico",
39760         "pr",
39761         "1",
39762         3,
39763         ["787", "939"]
39764       ],
39765       [
39766         "Qatar (‫قطر‬‎)",
39767         "qa",
39768         "974"
39769       ],
39770       [
39771         "Réunion (La Réunion)",
39772         "re",
39773         "262",
39774         0
39775       ],
39776       [
39777         "Romania (România)",
39778         "ro",
39779         "40"
39780       ],
39781       [
39782         "Russia (Россия)",
39783         "ru",
39784         "7",
39785         0
39786       ],
39787       [
39788         "Rwanda",
39789         "rw",
39790         "250"
39791       ],
39792       [
39793         "Saint Barthélemy",
39794         "bl",
39795         "590",
39796         1
39797       ],
39798       [
39799         "Saint Helena",
39800         "sh",
39801         "290"
39802       ],
39803       [
39804         "Saint Kitts and Nevis",
39805         "kn",
39806         "1869"
39807       ],
39808       [
39809         "Saint Lucia",
39810         "lc",
39811         "1758"
39812       ],
39813       [
39814         "Saint Martin (Saint-Martin (partie française))",
39815         "mf",
39816         "590",
39817         2
39818       ],
39819       [
39820         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39821         "pm",
39822         "508"
39823       ],
39824       [
39825         "Saint Vincent and the Grenadines",
39826         "vc",
39827         "1784"
39828       ],
39829       [
39830         "Samoa",
39831         "ws",
39832         "685"
39833       ],
39834       [
39835         "San Marino",
39836         "sm",
39837         "378"
39838       ],
39839       [
39840         "São Tomé and Príncipe (São Tomé e Príncipe)",
39841         "st",
39842         "239"
39843       ],
39844       [
39845         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39846         "sa",
39847         "966"
39848       ],
39849       [
39850         "Senegal (Sénégal)",
39851         "sn",
39852         "221"
39853       ],
39854       [
39855         "Serbia (Србија)",
39856         "rs",
39857         "381"
39858       ],
39859       [
39860         "Seychelles",
39861         "sc",
39862         "248"
39863       ],
39864       [
39865         "Sierra Leone",
39866         "sl",
39867         "232"
39868       ],
39869       [
39870         "Singapore",
39871         "sg",
39872         "65"
39873       ],
39874       [
39875         "Sint Maarten",
39876         "sx",
39877         "1721"
39878       ],
39879       [
39880         "Slovakia (Slovensko)",
39881         "sk",
39882         "421"
39883       ],
39884       [
39885         "Slovenia (Slovenija)",
39886         "si",
39887         "386"
39888       ],
39889       [
39890         "Solomon Islands",
39891         "sb",
39892         "677"
39893       ],
39894       [
39895         "Somalia (Soomaaliya)",
39896         "so",
39897         "252"
39898       ],
39899       [
39900         "South Africa",
39901         "za",
39902         "27"
39903       ],
39904       [
39905         "South Korea (대한민국)",
39906         "kr",
39907         "82"
39908       ],
39909       [
39910         "South Sudan (‫جنوب السودان‬‎)",
39911         "ss",
39912         "211"
39913       ],
39914       [
39915         "Spain (España)",
39916         "es",
39917         "34"
39918       ],
39919       [
39920         "Sri Lanka (ශ්‍රී ලංකාව)",
39921         "lk",
39922         "94"
39923       ],
39924       [
39925         "Sudan (‫السودان‬‎)",
39926         "sd",
39927         "249"
39928       ],
39929       [
39930         "Suriname",
39931         "sr",
39932         "597"
39933       ],
39934       [
39935         "Svalbard and Jan Mayen",
39936         "sj",
39937         "47",
39938         1
39939       ],
39940       [
39941         "Swaziland",
39942         "sz",
39943         "268"
39944       ],
39945       [
39946         "Sweden (Sverige)",
39947         "se",
39948         "46"
39949       ],
39950       [
39951         "Switzerland (Schweiz)",
39952         "ch",
39953         "41"
39954       ],
39955       [
39956         "Syria (‫سوريا‬‎)",
39957         "sy",
39958         "963"
39959       ],
39960       [
39961         "Taiwan (台灣)",
39962         "tw",
39963         "886"
39964       ],
39965       [
39966         "Tajikistan",
39967         "tj",
39968         "992"
39969       ],
39970       [
39971         "Tanzania",
39972         "tz",
39973         "255"
39974       ],
39975       [
39976         "Thailand (ไทย)",
39977         "th",
39978         "66"
39979       ],
39980       [
39981         "Timor-Leste",
39982         "tl",
39983         "670"
39984       ],
39985       [
39986         "Togo",
39987         "tg",
39988         "228"
39989       ],
39990       [
39991         "Tokelau",
39992         "tk",
39993         "690"
39994       ],
39995       [
39996         "Tonga",
39997         "to",
39998         "676"
39999       ],
40000       [
40001         "Trinidad and Tobago",
40002         "tt",
40003         "1868"
40004       ],
40005       [
40006         "Tunisia (‫تونس‬‎)",
40007         "tn",
40008         "216"
40009       ],
40010       [
40011         "Turkey (Türkiye)",
40012         "tr",
40013         "90"
40014       ],
40015       [
40016         "Turkmenistan",
40017         "tm",
40018         "993"
40019       ],
40020       [
40021         "Turks and Caicos Islands",
40022         "tc",
40023         "1649"
40024       ],
40025       [
40026         "Tuvalu",
40027         "tv",
40028         "688"
40029       ],
40030       [
40031         "U.S. Virgin Islands",
40032         "vi",
40033         "1340"
40034       ],
40035       [
40036         "Uganda",
40037         "ug",
40038         "256"
40039       ],
40040       [
40041         "Ukraine (Україна)",
40042         "ua",
40043         "380"
40044       ],
40045       [
40046         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40047         "ae",
40048         "971"
40049       ],
40050       [
40051         "United Kingdom",
40052         "gb",
40053         "44",
40054         0
40055       ],
40056       [
40057         "United States",
40058         "us",
40059         "1",
40060         0
40061       ],
40062       [
40063         "Uruguay",
40064         "uy",
40065         "598"
40066       ],
40067       [
40068         "Uzbekistan (Oʻzbekiston)",
40069         "uz",
40070         "998"
40071       ],
40072       [
40073         "Vanuatu",
40074         "vu",
40075         "678"
40076       ],
40077       [
40078         "Vatican City (Città del Vaticano)",
40079         "va",
40080         "39",
40081         1
40082       ],
40083       [
40084         "Venezuela",
40085         "ve",
40086         "58"
40087       ],
40088       [
40089         "Vietnam (Việt Nam)",
40090         "vn",
40091         "84"
40092       ],
40093       [
40094         "Wallis and Futuna (Wallis-et-Futuna)",
40095         "wf",
40096         "681"
40097       ],
40098       [
40099         "Western Sahara (‫الصحراء الغربية‬‎)",
40100         "eh",
40101         "212",
40102         1
40103       ],
40104       [
40105         "Yemen (‫اليمن‬‎)",
40106         "ye",
40107         "967"
40108       ],
40109       [
40110         "Zambia",
40111         "zm",
40112         "260"
40113       ],
40114       [
40115         "Zimbabwe",
40116         "zw",
40117         "263"
40118       ],
40119       [
40120         "Åland Islands",
40121         "ax",
40122         "358",
40123         1
40124       ]
40125   ];
40126   
40127   return d;
40128 }/**
40129 *    This script refer to:
40130 *    Title: International Telephone Input
40131 *    Author: Jack O'Connor
40132 *    Code version:  v12.1.12
40133 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40134 **/
40135
40136 /**
40137  * @class Roo.bootstrap.PhoneInput
40138  * @extends Roo.bootstrap.TriggerField
40139  * An input with International dial-code selection
40140  
40141  * @cfg {String} defaultDialCode default '+852'
40142  * @cfg {Array} preferedCountries default []
40143   
40144  * @constructor
40145  * Create a new PhoneInput.
40146  * @param {Object} config Configuration options
40147  */
40148
40149 Roo.bootstrap.PhoneInput = function(config) {
40150     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40151 };
40152
40153 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40154         
40155         listWidth: undefined,
40156         
40157         selectedClass: 'active',
40158         
40159         invalidClass : "has-warning",
40160         
40161         validClass: 'has-success',
40162         
40163         allowed: '0123456789',
40164         
40165         max_length: 15,
40166         
40167         /**
40168          * @cfg {String} defaultDialCode The default dial code when initializing the input
40169          */
40170         defaultDialCode: '+852',
40171         
40172         /**
40173          * @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
40174          */
40175         preferedCountries: false,
40176         
40177         getAutoCreate : function()
40178         {
40179             var data = Roo.bootstrap.PhoneInputData();
40180             var align = this.labelAlign || this.parentLabelAlign();
40181             var id = Roo.id();
40182             
40183             this.allCountries = [];
40184             this.dialCodeMapping = [];
40185             
40186             for (var i = 0; i < data.length; i++) {
40187               var c = data[i];
40188               this.allCountries[i] = {
40189                 name: c[0],
40190                 iso2: c[1],
40191                 dialCode: c[2],
40192                 priority: c[3] || 0,
40193                 areaCodes: c[4] || null
40194               };
40195               this.dialCodeMapping[c[2]] = {
40196                   name: c[0],
40197                   iso2: c[1],
40198                   priority: c[3] || 0,
40199                   areaCodes: c[4] || null
40200               };
40201             }
40202             
40203             var cfg = {
40204                 cls: 'form-group',
40205                 cn: []
40206             };
40207             
40208             var input =  {
40209                 tag: 'input',
40210                 id : id,
40211                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40212                 maxlength: this.max_length,
40213                 cls : 'form-control tel-input',
40214                 autocomplete: 'new-password'
40215             };
40216             
40217             var hiddenInput = {
40218                 tag: 'input',
40219                 type: 'hidden',
40220                 cls: 'hidden-tel-input'
40221             };
40222             
40223             if (this.name) {
40224                 hiddenInput.name = this.name;
40225             }
40226             
40227             if (this.disabled) {
40228                 input.disabled = true;
40229             }
40230             
40231             var flag_container = {
40232                 tag: 'div',
40233                 cls: 'flag-box',
40234                 cn: [
40235                     {
40236                         tag: 'div',
40237                         cls: 'flag'
40238                     },
40239                     {
40240                         tag: 'div',
40241                         cls: 'caret'
40242                     }
40243                 ]
40244             };
40245             
40246             var box = {
40247                 tag: 'div',
40248                 cls: this.hasFeedback ? 'has-feedback' : '',
40249                 cn: [
40250                     hiddenInput,
40251                     input,
40252                     {
40253                         tag: 'input',
40254                         cls: 'dial-code-holder',
40255                         disabled: true
40256                     }
40257                 ]
40258             };
40259             
40260             var container = {
40261                 cls: 'roo-select2-container input-group',
40262                 cn: [
40263                     flag_container,
40264                     box
40265                 ]
40266             };
40267             
40268             if (this.fieldLabel.length) {
40269                 var indicator = {
40270                     tag: 'i',
40271                     tooltip: 'This field is required'
40272                 };
40273                 
40274                 var label = {
40275                     tag: 'label',
40276                     'for':  id,
40277                     cls: 'control-label',
40278                     cn: []
40279                 };
40280                 
40281                 var label_text = {
40282                     tag: 'span',
40283                     html: this.fieldLabel
40284                 };
40285                 
40286                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40287                 label.cn = [
40288                     indicator,
40289                     label_text
40290                 ];
40291                 
40292                 if(this.indicatorpos == 'right') {
40293                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40294                     label.cn = [
40295                         label_text,
40296                         indicator
40297                     ];
40298                 }
40299                 
40300                 if(align == 'left') {
40301                     container = {
40302                         tag: 'div',
40303                         cn: [
40304                             container
40305                         ]
40306                     };
40307                     
40308                     if(this.labelWidth > 12){
40309                         label.style = "width: " + this.labelWidth + 'px';
40310                     }
40311                     if(this.labelWidth < 13 && this.labelmd == 0){
40312                         this.labelmd = this.labelWidth;
40313                     }
40314                     if(this.labellg > 0){
40315                         label.cls += ' col-lg-' + this.labellg;
40316                         input.cls += ' col-lg-' + (12 - this.labellg);
40317                     }
40318                     if(this.labelmd > 0){
40319                         label.cls += ' col-md-' + this.labelmd;
40320                         container.cls += ' col-md-' + (12 - this.labelmd);
40321                     }
40322                     if(this.labelsm > 0){
40323                         label.cls += ' col-sm-' + this.labelsm;
40324                         container.cls += ' col-sm-' + (12 - this.labelsm);
40325                     }
40326                     if(this.labelxs > 0){
40327                         label.cls += ' col-xs-' + this.labelxs;
40328                         container.cls += ' col-xs-' + (12 - this.labelxs);
40329                     }
40330                 }
40331             }
40332             
40333             cfg.cn = [
40334                 label,
40335                 container
40336             ];
40337             
40338             var settings = this;
40339             
40340             ['xs','sm','md','lg'].map(function(size){
40341                 if (settings[size]) {
40342                     cfg.cls += ' col-' + size + '-' + settings[size];
40343                 }
40344             });
40345             
40346             this.store = new Roo.data.Store({
40347                 proxy : new Roo.data.MemoryProxy({}),
40348                 reader : new Roo.data.JsonReader({
40349                     fields : [
40350                         {
40351                             'name' : 'name',
40352                             'type' : 'string'
40353                         },
40354                         {
40355                             'name' : 'iso2',
40356                             'type' : 'string'
40357                         },
40358                         {
40359                             'name' : 'dialCode',
40360                             'type' : 'string'
40361                         },
40362                         {
40363                             'name' : 'priority',
40364                             'type' : 'string'
40365                         },
40366                         {
40367                             'name' : 'areaCodes',
40368                             'type' : 'string'
40369                         }
40370                     ]
40371                 })
40372             });
40373             
40374             if(!this.preferedCountries) {
40375                 this.preferedCountries = [
40376                     'hk',
40377                     'gb',
40378                     'us'
40379                 ];
40380             }
40381             
40382             var p = this.preferedCountries.reverse();
40383             
40384             if(p) {
40385                 for (var i = 0; i < p.length; i++) {
40386                     for (var j = 0; j < this.allCountries.length; j++) {
40387                         if(this.allCountries[j].iso2 == p[i]) {
40388                             var t = this.allCountries[j];
40389                             this.allCountries.splice(j,1);
40390                             this.allCountries.unshift(t);
40391                         }
40392                     } 
40393                 }
40394             }
40395             
40396             this.store.proxy.data = {
40397                 success: true,
40398                 data: this.allCountries
40399             };
40400             
40401             return cfg;
40402         },
40403         
40404         initEvents : function()
40405         {
40406             this.createList();
40407             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40408             
40409             this.indicator = this.indicatorEl();
40410             this.flag = this.flagEl();
40411             this.dialCodeHolder = this.dialCodeHolderEl();
40412             
40413             this.trigger = this.el.select('div.flag-box',true).first();
40414             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40415             
40416             var _this = this;
40417             
40418             (function(){
40419                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40420                 _this.list.setWidth(lw);
40421             }).defer(100);
40422             
40423             this.list.on('mouseover', this.onViewOver, this);
40424             this.list.on('mousemove', this.onViewMove, this);
40425             this.inputEl().on("keyup", this.onKeyUp, this);
40426             this.inputEl().on("keypress", this.onKeyPress, this);
40427             
40428             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40429
40430             this.view = new Roo.View(this.list, this.tpl, {
40431                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40432             });
40433             
40434             this.view.on('click', this.onViewClick, this);
40435             this.setValue(this.defaultDialCode);
40436         },
40437         
40438         onTriggerClick : function(e)
40439         {
40440             Roo.log('trigger click');
40441             if(this.disabled){
40442                 return;
40443             }
40444             
40445             if(this.isExpanded()){
40446                 this.collapse();
40447                 this.hasFocus = false;
40448             }else {
40449                 this.store.load({});
40450                 this.hasFocus = true;
40451                 this.expand();
40452             }
40453         },
40454         
40455         isExpanded : function()
40456         {
40457             return this.list.isVisible();
40458         },
40459         
40460         collapse : function()
40461         {
40462             if(!this.isExpanded()){
40463                 return;
40464             }
40465             this.list.hide();
40466             Roo.get(document).un('mousedown', this.collapseIf, this);
40467             Roo.get(document).un('mousewheel', this.collapseIf, this);
40468             this.fireEvent('collapse', this);
40469             this.validate();
40470         },
40471         
40472         expand : function()
40473         {
40474             Roo.log('expand');
40475
40476             if(this.isExpanded() || !this.hasFocus){
40477                 return;
40478             }
40479             
40480             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40481             this.list.setWidth(lw);
40482             
40483             this.list.show();
40484             this.restrictHeight();
40485             
40486             Roo.get(document).on('mousedown', this.collapseIf, this);
40487             Roo.get(document).on('mousewheel', this.collapseIf, this);
40488             
40489             this.fireEvent('expand', this);
40490         },
40491         
40492         restrictHeight : function()
40493         {
40494             this.list.alignTo(this.inputEl(), this.listAlign);
40495             this.list.alignTo(this.inputEl(), this.listAlign);
40496         },
40497         
40498         onViewOver : function(e, t)
40499         {
40500             if(this.inKeyMode){
40501                 return;
40502             }
40503             var item = this.view.findItemFromChild(t);
40504             
40505             if(item){
40506                 var index = this.view.indexOf(item);
40507                 this.select(index, false);
40508             }
40509         },
40510
40511         // private
40512         onViewClick : function(view, doFocus, el, e)
40513         {
40514             var index = this.view.getSelectedIndexes()[0];
40515             
40516             var r = this.store.getAt(index);
40517             
40518             if(r){
40519                 this.onSelect(r, index);
40520             }
40521             if(doFocus !== false && !this.blockFocus){
40522                 this.inputEl().focus();
40523             }
40524         },
40525         
40526         onViewMove : function(e, t)
40527         {
40528             this.inKeyMode = false;
40529         },
40530         
40531         select : function(index, scrollIntoView)
40532         {
40533             this.selectedIndex = index;
40534             this.view.select(index);
40535             if(scrollIntoView !== false){
40536                 var el = this.view.getNode(index);
40537                 if(el){
40538                     this.list.scrollChildIntoView(el, false);
40539                 }
40540             }
40541         },
40542         
40543         createList : function()
40544         {
40545             this.list = Roo.get(document.body).createChild({
40546                 tag: 'ul',
40547                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40548                 style: 'display:none'
40549             });
40550             
40551             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40552         },
40553         
40554         collapseIf : function(e)
40555         {
40556             var in_combo  = e.within(this.el);
40557             var in_list =  e.within(this.list);
40558             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40559             
40560             if (in_combo || in_list || is_list) {
40561                 return;
40562             }
40563             this.collapse();
40564         },
40565         
40566         onSelect : function(record, index)
40567         {
40568             if(this.fireEvent('beforeselect', this, record, index) !== false){
40569                 
40570                 this.setFlagClass(record.data.iso2);
40571                 this.setDialCode(record.data.dialCode);
40572                 this.hasFocus = false;
40573                 this.collapse();
40574                 this.fireEvent('select', this, record, index);
40575             }
40576         },
40577         
40578         flagEl : function()
40579         {
40580             var flag = this.el.select('div.flag',true).first();
40581             if(!flag){
40582                 return false;
40583             }
40584             return flag;
40585         },
40586         
40587         dialCodeHolderEl : function()
40588         {
40589             var d = this.el.select('input.dial-code-holder',true).first();
40590             if(!d){
40591                 return false;
40592             }
40593             return d;
40594         },
40595         
40596         setDialCode : function(v)
40597         {
40598             this.dialCodeHolder.dom.value = '+'+v;
40599         },
40600         
40601         setFlagClass : function(n)
40602         {
40603             this.flag.dom.className = 'flag '+n;
40604         },
40605         
40606         getValue : function()
40607         {
40608             var v = this.inputEl().getValue();
40609             if(this.dialCodeHolder) {
40610                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40611             }
40612             return v;
40613         },
40614         
40615         setValue : function(v)
40616         {
40617             var d = this.getDialCode(v);
40618             
40619             //invalid dial code
40620             if(v.length == 0 || !d || d.length == 0) {
40621                 if(this.rendered){
40622                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40623                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40624                 }
40625                 return;
40626             }
40627             
40628             //valid dial code
40629             this.setFlagClass(this.dialCodeMapping[d].iso2);
40630             this.setDialCode(d);
40631             this.inputEl().dom.value = v.replace('+'+d,'');
40632             this.hiddenEl().dom.value = this.getValue();
40633             
40634             this.validate();
40635         },
40636         
40637         getDialCode : function(v)
40638         {
40639             v = v ||  '';
40640             
40641             if (v.length == 0) {
40642                 return this.dialCodeHolder.dom.value;
40643             }
40644             
40645             var dialCode = "";
40646             if (v.charAt(0) != "+") {
40647                 return false;
40648             }
40649             var numericChars = "";
40650             for (var i = 1; i < v.length; i++) {
40651               var c = v.charAt(i);
40652               if (!isNaN(c)) {
40653                 numericChars += c;
40654                 if (this.dialCodeMapping[numericChars]) {
40655                   dialCode = v.substr(1, i);
40656                 }
40657                 if (numericChars.length == 4) {
40658                   break;
40659                 }
40660               }
40661             }
40662             return dialCode;
40663         },
40664         
40665         reset : function()
40666         {
40667             this.setValue(this.defaultDialCode);
40668             this.validate();
40669         },
40670         
40671         hiddenEl : function()
40672         {
40673             return this.el.select('input.hidden-tel-input',true).first();
40674         },
40675         
40676         // after setting val
40677         onKeyUp : function(e){
40678             this.setValue(this.getValue());
40679         },
40680         
40681         onKeyPress : function(e){
40682             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40683                 e.stopEvent();
40684             }
40685         }
40686         
40687 });
40688 /**
40689  * @class Roo.bootstrap.MoneyField
40690  * @extends Roo.bootstrap.ComboBox
40691  * Bootstrap MoneyField class
40692  * 
40693  * @constructor
40694  * Create a new MoneyField.
40695  * @param {Object} config Configuration options
40696  */
40697
40698 Roo.bootstrap.MoneyField = function(config) {
40699     
40700     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40701     
40702 };
40703
40704 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40705     
40706     /**
40707      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40708      */
40709     allowDecimals : true,
40710     /**
40711      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40712      */
40713     decimalSeparator : ".",
40714     /**
40715      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40716      */
40717     decimalPrecision : 0,
40718     /**
40719      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40720      */
40721     allowNegative : true,
40722     /**
40723      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40724      */
40725     allowZero: true,
40726     /**
40727      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40728      */
40729     minValue : Number.NEGATIVE_INFINITY,
40730     /**
40731      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40732      */
40733     maxValue : Number.MAX_VALUE,
40734     /**
40735      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40736      */
40737     minText : "The minimum value for this field is {0}",
40738     /**
40739      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40740      */
40741     maxText : "The maximum value for this field is {0}",
40742     /**
40743      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40744      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40745      */
40746     nanText : "{0} is not a valid number",
40747     /**
40748      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40749      */
40750     castInt : true,
40751     /**
40752      * @cfg {String} defaults currency of the MoneyField
40753      * value should be in lkey
40754      */
40755     defaultCurrency : false,
40756     /**
40757      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40758      */
40759     thousandsDelimiter : false,
40760     /**
40761      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40762      */
40763     max_length: false,
40764     
40765     inputlg : 9,
40766     inputmd : 9,
40767     inputsm : 9,
40768     inputxs : 6,
40769     
40770     store : false,
40771     
40772     getAutoCreate : function()
40773     {
40774         var align = this.labelAlign || this.parentLabelAlign();
40775         
40776         var id = Roo.id();
40777
40778         var cfg = {
40779             cls: 'form-group',
40780             cn: []
40781         };
40782
40783         var input =  {
40784             tag: 'input',
40785             id : id,
40786             cls : 'form-control roo-money-amount-input',
40787             autocomplete: 'new-password'
40788         };
40789         
40790         var hiddenInput = {
40791             tag: 'input',
40792             type: 'hidden',
40793             id: Roo.id(),
40794             cls: 'hidden-number-input'
40795         };
40796         
40797         if(this.max_length) {
40798             input.maxlength = this.max_length; 
40799         }
40800         
40801         if (this.name) {
40802             hiddenInput.name = this.name;
40803         }
40804
40805         if (this.disabled) {
40806             input.disabled = true;
40807         }
40808
40809         var clg = 12 - this.inputlg;
40810         var cmd = 12 - this.inputmd;
40811         var csm = 12 - this.inputsm;
40812         var cxs = 12 - this.inputxs;
40813         
40814         var container = {
40815             tag : 'div',
40816             cls : 'row roo-money-field',
40817             cn : [
40818                 {
40819                     tag : 'div',
40820                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40821                     cn : [
40822                         {
40823                             tag : 'div',
40824                             cls: 'roo-select2-container input-group',
40825                             cn: [
40826                                 {
40827                                     tag : 'input',
40828                                     cls : 'form-control roo-money-currency-input',
40829                                     autocomplete: 'new-password',
40830                                     readOnly : 1,
40831                                     name : this.currencyName
40832                                 },
40833                                 {
40834                                     tag :'span',
40835                                     cls : 'input-group-addon',
40836                                     cn : [
40837                                         {
40838                                             tag: 'span',
40839                                             cls: 'caret'
40840                                         }
40841                                     ]
40842                                 }
40843                             ]
40844                         }
40845                     ]
40846                 },
40847                 {
40848                     tag : 'div',
40849                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40850                     cn : [
40851                         {
40852                             tag: 'div',
40853                             cls: this.hasFeedback ? 'has-feedback' : '',
40854                             cn: [
40855                                 input
40856                             ]
40857                         }
40858                     ]
40859                 }
40860             ]
40861             
40862         };
40863         
40864         if (this.fieldLabel.length) {
40865             var indicator = {
40866                 tag: 'i',
40867                 tooltip: 'This field is required'
40868             };
40869
40870             var label = {
40871                 tag: 'label',
40872                 'for':  id,
40873                 cls: 'control-label',
40874                 cn: []
40875             };
40876
40877             var label_text = {
40878                 tag: 'span',
40879                 html: this.fieldLabel
40880             };
40881
40882             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40883             label.cn = [
40884                 indicator,
40885                 label_text
40886             ];
40887
40888             if(this.indicatorpos == 'right') {
40889                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40890                 label.cn = [
40891                     label_text,
40892                     indicator
40893                 ];
40894             }
40895
40896             if(align == 'left') {
40897                 container = {
40898                     tag: 'div',
40899                     cn: [
40900                         container
40901                     ]
40902                 };
40903
40904                 if(this.labelWidth > 12){
40905                     label.style = "width: " + this.labelWidth + 'px';
40906                 }
40907                 if(this.labelWidth < 13 && this.labelmd == 0){
40908                     this.labelmd = this.labelWidth;
40909                 }
40910                 if(this.labellg > 0){
40911                     label.cls += ' col-lg-' + this.labellg;
40912                     input.cls += ' col-lg-' + (12 - this.labellg);
40913                 }
40914                 if(this.labelmd > 0){
40915                     label.cls += ' col-md-' + this.labelmd;
40916                     container.cls += ' col-md-' + (12 - this.labelmd);
40917                 }
40918                 if(this.labelsm > 0){
40919                     label.cls += ' col-sm-' + this.labelsm;
40920                     container.cls += ' col-sm-' + (12 - this.labelsm);
40921                 }
40922                 if(this.labelxs > 0){
40923                     label.cls += ' col-xs-' + this.labelxs;
40924                     container.cls += ' col-xs-' + (12 - this.labelxs);
40925                 }
40926             }
40927         }
40928
40929         cfg.cn = [
40930             label,
40931             container,
40932             hiddenInput
40933         ];
40934         
40935         var settings = this;
40936
40937         ['xs','sm','md','lg'].map(function(size){
40938             if (settings[size]) {
40939                 cfg.cls += ' col-' + size + '-' + settings[size];
40940             }
40941         });
40942         
40943         return cfg;
40944     },
40945     
40946     initEvents : function()
40947     {
40948         this.indicator = this.indicatorEl();
40949         
40950         this.initCurrencyEvent();
40951         
40952         this.initNumberEvent();
40953     },
40954     
40955     initCurrencyEvent : function()
40956     {
40957         if (!this.store) {
40958             throw "can not find store for combo";
40959         }
40960         
40961         this.store = Roo.factory(this.store, Roo.data);
40962         this.store.parent = this;
40963         
40964         this.createList();
40965         
40966         this.triggerEl = this.el.select('.input-group-addon', true).first();
40967         
40968         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40969         
40970         var _this = this;
40971         
40972         (function(){
40973             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40974             _this.list.setWidth(lw);
40975         }).defer(100);
40976         
40977         this.list.on('mouseover', this.onViewOver, this);
40978         this.list.on('mousemove', this.onViewMove, this);
40979         this.list.on('scroll', this.onViewScroll, this);
40980         
40981         if(!this.tpl){
40982             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40983         }
40984         
40985         this.view = new Roo.View(this.list, this.tpl, {
40986             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40987         });
40988         
40989         this.view.on('click', this.onViewClick, this);
40990         
40991         this.store.on('beforeload', this.onBeforeLoad, this);
40992         this.store.on('load', this.onLoad, this);
40993         this.store.on('loadexception', this.onLoadException, this);
40994         
40995         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40996             "up" : function(e){
40997                 this.inKeyMode = true;
40998                 this.selectPrev();
40999             },
41000
41001             "down" : function(e){
41002                 if(!this.isExpanded()){
41003                     this.onTriggerClick();
41004                 }else{
41005                     this.inKeyMode = true;
41006                     this.selectNext();
41007                 }
41008             },
41009
41010             "enter" : function(e){
41011                 this.collapse();
41012                 
41013                 if(this.fireEvent("specialkey", this, e)){
41014                     this.onViewClick(false);
41015                 }
41016                 
41017                 return true;
41018             },
41019
41020             "esc" : function(e){
41021                 this.collapse();
41022             },
41023
41024             "tab" : function(e){
41025                 this.collapse();
41026                 
41027                 if(this.fireEvent("specialkey", this, e)){
41028                     this.onViewClick(false);
41029                 }
41030                 
41031                 return true;
41032             },
41033
41034             scope : this,
41035
41036             doRelay : function(foo, bar, hname){
41037                 if(hname == 'down' || this.scope.isExpanded()){
41038                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41039                 }
41040                 return true;
41041             },
41042
41043             forceKeyDown: true
41044         });
41045         
41046         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41047         
41048     },
41049     
41050     initNumberEvent : function(e)
41051     {
41052         this.inputEl().on("keydown" , this.fireKey,  this);
41053         this.inputEl().on("focus", this.onFocus,  this);
41054         this.inputEl().on("blur", this.onBlur,  this);
41055         
41056         this.inputEl().relayEvent('keyup', this);
41057         
41058         if(this.indicator){
41059             this.indicator.addClass('invisible');
41060         }
41061  
41062         this.originalValue = this.getValue();
41063         
41064         if(this.validationEvent == 'keyup'){
41065             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41066             this.inputEl().on('keyup', this.filterValidation, this);
41067         }
41068         else if(this.validationEvent !== false){
41069             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41070         }
41071         
41072         if(this.selectOnFocus){
41073             this.on("focus", this.preFocus, this);
41074             
41075         }
41076         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41077             this.inputEl().on("keypress", this.filterKeys, this);
41078         } else {
41079             this.inputEl().relayEvent('keypress', this);
41080         }
41081         
41082         var allowed = "0123456789";
41083         
41084         if(this.allowDecimals){
41085             allowed += this.decimalSeparator;
41086         }
41087         
41088         if(this.allowNegative){
41089             allowed += "-";
41090         }
41091         
41092         if(this.thousandsDelimiter) {
41093             allowed += ",";
41094         }
41095         
41096         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41097         
41098         var keyPress = function(e){
41099             
41100             var k = e.getKey();
41101             
41102             var c = e.getCharCode();
41103             
41104             if(
41105                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41106                     allowed.indexOf(String.fromCharCode(c)) === -1
41107             ){
41108                 e.stopEvent();
41109                 return;
41110             }
41111             
41112             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41113                 return;
41114             }
41115             
41116             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41117                 e.stopEvent();
41118             }
41119         };
41120         
41121         this.inputEl().on("keypress", keyPress, this);
41122         
41123     },
41124     
41125     onTriggerClick : function(e)
41126     {   
41127         if(this.disabled){
41128             return;
41129         }
41130         
41131         this.page = 0;
41132         this.loadNext = false;
41133         
41134         if(this.isExpanded()){
41135             this.collapse();
41136             return;
41137         }
41138         
41139         this.hasFocus = true;
41140         
41141         if(this.triggerAction == 'all') {
41142             this.doQuery(this.allQuery, true);
41143             return;
41144         }
41145         
41146         this.doQuery(this.getRawValue());
41147     },
41148     
41149     getCurrency : function()
41150     {   
41151         var v = this.currencyEl().getValue();
41152         
41153         return v;
41154     },
41155     
41156     restrictHeight : function()
41157     {
41158         this.list.alignTo(this.currencyEl(), this.listAlign);
41159         this.list.alignTo(this.currencyEl(), this.listAlign);
41160     },
41161     
41162     onViewClick : function(view, doFocus, el, e)
41163     {
41164         var index = this.view.getSelectedIndexes()[0];
41165         
41166         var r = this.store.getAt(index);
41167         
41168         if(r){
41169             this.onSelect(r, index);
41170         }
41171     },
41172     
41173     onSelect : function(record, index){
41174         
41175         if(this.fireEvent('beforeselect', this, record, index) !== false){
41176         
41177             this.setFromCurrencyData(index > -1 ? record.data : false);
41178             
41179             this.collapse();
41180             
41181             this.fireEvent('select', this, record, index);
41182         }
41183     },
41184     
41185     setFromCurrencyData : function(o)
41186     {
41187         var currency = '';
41188         
41189         this.lastCurrency = o;
41190         
41191         if (this.currencyField) {
41192             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41193         } else {
41194             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41195         }
41196         
41197         this.lastSelectionText = currency;
41198         
41199         //setting default currency
41200         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41201             this.setCurrency(this.defaultCurrency);
41202             return;
41203         }
41204         
41205         this.setCurrency(currency);
41206     },
41207     
41208     setFromData : function(o)
41209     {
41210         var c = {};
41211         
41212         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41213         
41214         this.setFromCurrencyData(c);
41215         
41216         var value = '';
41217         
41218         if (this.name) {
41219             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41220         } else {
41221             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41222         }
41223         
41224         this.setValue(value);
41225         
41226     },
41227     
41228     setCurrency : function(v)
41229     {   
41230         this.currencyValue = v;
41231         
41232         if(this.rendered){
41233             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41234             this.validate();
41235         }
41236     },
41237     
41238     setValue : function(v)
41239     {
41240         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41241         
41242         this.value = v;
41243         
41244         if(this.rendered){
41245             
41246             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41247             
41248             this.inputEl().dom.value = (v == '') ? '' :
41249                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41250             
41251             if(!this.allowZero && v === '0') {
41252                 this.hiddenEl().dom.value = '';
41253                 this.inputEl().dom.value = '';
41254             }
41255             
41256             this.validate();
41257         }
41258     },
41259     
41260     getRawValue : function()
41261     {
41262         var v = this.inputEl().getValue();
41263         
41264         return v;
41265     },
41266     
41267     getValue : function()
41268     {
41269         return this.fixPrecision(this.parseValue(this.getRawValue()));
41270     },
41271     
41272     parseValue : function(value)
41273     {
41274         if(this.thousandsDelimiter) {
41275             value += "";
41276             r = new RegExp(",", "g");
41277             value = value.replace(r, "");
41278         }
41279         
41280         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41281         return isNaN(value) ? '' : value;
41282         
41283     },
41284     
41285     fixPrecision : function(value)
41286     {
41287         if(this.thousandsDelimiter) {
41288             value += "";
41289             r = new RegExp(",", "g");
41290             value = value.replace(r, "");
41291         }
41292         
41293         var nan = isNaN(value);
41294         
41295         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41296             return nan ? '' : value;
41297         }
41298         return parseFloat(value).toFixed(this.decimalPrecision);
41299     },
41300     
41301     decimalPrecisionFcn : function(v)
41302     {
41303         return Math.floor(v);
41304     },
41305     
41306     validateValue : function(value)
41307     {
41308         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41309             return false;
41310         }
41311         
41312         var num = this.parseValue(value);
41313         
41314         if(isNaN(num)){
41315             this.markInvalid(String.format(this.nanText, value));
41316             return false;
41317         }
41318         
41319         if(num < this.minValue){
41320             this.markInvalid(String.format(this.minText, this.minValue));
41321             return false;
41322         }
41323         
41324         if(num > this.maxValue){
41325             this.markInvalid(String.format(this.maxText, this.maxValue));
41326             return false;
41327         }
41328         
41329         return true;
41330     },
41331     
41332     validate : function()
41333     {
41334         if(this.disabled || this.allowBlank){
41335             this.markValid();
41336             return true;
41337         }
41338         
41339         var currency = this.getCurrency();
41340         
41341         if(this.validateValue(this.getRawValue()) && currency.length){
41342             this.markValid();
41343             return true;
41344         }
41345         
41346         this.markInvalid();
41347         return false;
41348     },
41349     
41350     getName: function()
41351     {
41352         return this.name;
41353     },
41354     
41355     beforeBlur : function()
41356     {
41357         if(!this.castInt){
41358             return;
41359         }
41360         
41361         var v = this.parseValue(this.getRawValue());
41362         
41363         if(v || v == 0){
41364             this.setValue(v);
41365         }
41366     },
41367     
41368     onBlur : function()
41369     {
41370         this.beforeBlur();
41371         
41372         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41373             //this.el.removeClass(this.focusClass);
41374         }
41375         
41376         this.hasFocus = false;
41377         
41378         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41379             this.validate();
41380         }
41381         
41382         var v = this.getValue();
41383         
41384         if(String(v) !== String(this.startValue)){
41385             this.fireEvent('change', this, v, this.startValue);
41386         }
41387         
41388         this.fireEvent("blur", this);
41389     },
41390     
41391     inputEl : function()
41392     {
41393         return this.el.select('.roo-money-amount-input', true).first();
41394     },
41395     
41396     currencyEl : function()
41397     {
41398         return this.el.select('.roo-money-currency-input', true).first();
41399     },
41400     
41401     hiddenEl : function()
41402     {
41403         return this.el.select('input.hidden-number-input',true).first();
41404     }
41405     
41406 });